Revision 91abaf6c
Von Bernd Bleßmann vor fast 9 Jahren hinzugefügt
SL/Controller/Order.pm | ||
---|---|---|
20 | 20 |
use SL::DB::Default; |
21 | 21 |
use SL::DB::Unit; |
22 | 22 |
use SL::DB::Price; |
23 |
use SL::DB::Part; |
|
23 | 24 |
|
24 | 25 |
use SL::Helper::DateTime; |
25 | 26 |
use SL::Helper::CreatePDF qw(:all); |
26 | 27 |
|
28 |
use SL::Controller::Helper::GetModels; |
|
29 |
|
|
27 | 30 |
use List::Util qw(max first); |
28 | 31 |
use List::MoreUtils qw(none pairwise first_index); |
29 | 32 |
use English qw(-no_match_vars); |
... | ... | |
31 | 34 |
|
32 | 35 |
use Rose::Object::MakeMethods::Generic |
33 | 36 |
( |
34 |
'scalar --get_set_init' => [ qw(order valid_types type cv p) ], |
|
37 |
'scalar --get_set_init' => [ qw(order valid_types type cv p multi_items_models) ],
|
|
35 | 38 |
); |
36 | 39 |
|
37 | 40 |
|
... | ... | |
333 | 336 |
|
334 | 337 |
return unless $form_attr->{parts_id}; |
335 | 338 |
|
336 |
my $item = SL::DB::OrderItem->new;
|
|
337 |
$item->assign_attributes(%$form_attr);
|
|
339 |
my $item = _make_item($self->order, $form_attr);
|
|
340 |
$self->order->add_items($item);
|
|
338 | 341 |
|
339 |
my $part = SL::DB::Part->new(id => $form_attr->{parts_id})->load;
|
|
342 |
$self->_recalc();
|
|
340 | 343 |
|
341 |
my $price_source = SL::PriceSource->new(record_item => $item, record => $self->order); |
|
344 |
my $item_id = join('_', 'new', Time::HiRes::gettimeofday(), int rand 1000000000000); |
|
345 |
my $row_as_html = $self->p->render('order/tabs/_row', ITEM => $item, ID => $item_id); |
|
342 | 346 |
|
343 |
my $price_src; |
|
344 |
if ($item->sellprice) { |
|
345 |
$price_src = $price_source->price_from_source(""); |
|
346 |
$price_src->price($item->sellprice); |
|
347 |
} else { |
|
348 |
$price_src = $price_source->best_price |
|
349 |
? $price_source->best_price |
|
350 |
: $price_source->price_from_source(""); |
|
351 |
$price_src->price(0) if !$price_source->best_price; |
|
352 |
} |
|
347 |
$self->js |
|
348 |
->append('#row_table_id', $row_as_html) |
|
349 |
->val('.add_item_input', '') |
|
350 |
->run('row_table_scroll_down') |
|
351 |
->run('row_set_keyboard_events_by_id', $item_id) |
|
352 |
->on('.recalc', 'change', 'recalc_amounts_and_taxes') |
|
353 |
->on('.reformat_number', 'change', 'reformat_number') |
|
354 |
->focus('#add_item_parts_id_name'); |
|
353 | 355 |
|
354 |
my $discount_src; |
|
355 |
if ($item->discount) { |
|
356 |
$discount_src = $price_source->discount_from_source(""); |
|
357 |
$discount_src->discount($item->discount); |
|
356 |
$self->_js_redisplay_amounts_and_taxes; |
|
357 |
$self->js->render(); |
|
358 |
} |
|
359 |
|
|
360 |
sub action_show_multi_items_dialog { |
|
361 |
require SL::DB::PartsGroup; |
|
362 |
$_[0]->render('order/tabs/_multi_items_dialog', { layout => 0 }, |
|
363 |
all_partsgroups => SL::DB::Manager::PartsGroup->get_all); |
|
364 |
} |
|
365 |
|
|
366 |
sub action_multi_items_update_result { |
|
367 |
my $max_count = 100; |
|
368 |
my $count = $_[0]->multi_items_models->count; |
|
369 |
|
|
370 |
if ($count == 0) { |
|
371 |
my $text = SL::Presenter::EscapedText->new(text => $::locale->text('No results.')); |
|
372 |
$_[0]->render($text, { layout => 0 }); |
|
373 |
} elsif ($count > $max_count) { |
|
374 |
my $text = SL::Presenter::EscapedText->new(text => $::locale->text('Too many results (#1 from #2).', $count, $max_count)); |
|
375 |
$_[0]->render($text, { layout => 0 }); |
|
358 | 376 |
} else { |
359 |
$discount_src = $price_source->best_discount |
|
360 |
? $price_source->best_discount |
|
361 |
: $price_source->discount_from_source(""); |
|
362 |
$discount_src->discount(0) if !$price_source->best_discount; |
|
377 |
my $multi_items = $_[0]->multi_items_models->get; |
|
378 |
$_[0]->render('order/tabs/_multi_items_result', { layout => 0 }, |
|
379 |
multi_items => $multi_items); |
|
363 | 380 |
} |
381 |
} |
|
364 | 382 |
|
365 |
my %new_attr; |
|
366 |
$new_attr{part} = $part; |
|
367 |
$new_attr{description} = $part->description if ! $item->description; |
|
368 |
$new_attr{qty} = 1.0 if ! $item->qty; |
|
369 |
$new_attr{sellprice} = $price_src->price; |
|
370 |
$new_attr{discount} = $discount_src->discount; |
|
371 |
$new_attr{active_price_source} = $price_src; |
|
372 |
$new_attr{active_discount_source} = $discount_src; |
|
373 |
|
|
374 |
# add_custom_variables adds cvars to an orderitem with no cvars for saving, but |
|
375 |
# they cannot be retrieved via custom_variables until the order/orderitem is |
|
376 |
# saved. Adding empty custom_variables to new orderitem here solves this problem. |
|
377 |
$new_attr{custom_variables} = []; |
|
383 |
sub action_add_multi_items { |
|
384 |
my ($self) = @_; |
|
378 | 385 |
|
379 |
$item->assign_attributes(%new_attr); |
|
386 |
my @form_attr = grep { $_->{qty_as_number} } @{ $::form->{add_multi_items} }; |
|
387 |
return $self->js->render() unless scalar @form_attr; |
|
380 | 388 |
|
381 |
$self->order->add_items($item); |
|
389 |
my @items; |
|
390 |
foreach my $attr (@form_attr) { |
|
391 |
push @items, _make_item($self->order, $attr); |
|
392 |
} |
|
393 |
$self->order->add_items(@items); |
|
382 | 394 |
|
383 | 395 |
$self->_recalc(); |
384 | 396 |
|
385 |
my $item_id = join('_', 'new', Time::HiRes::gettimeofday(), int rand 1000000000000); |
|
386 |
my $row_as_html = $self->p->render('order/tabs/_row', ITEM => $item, ID => $item_id); |
|
397 |
foreach my $item (@items) { |
|
398 |
my $item_id = join('_', 'new', Time::HiRes::gettimeofday(), int rand 1000000000000); |
|
399 |
my $row_as_html = $self->p->render('order/tabs/_row', ITEM => $item, ID => $item_id); |
|
400 |
|
|
401 |
$self->js |
|
402 |
->append('#row_table_id', $row_as_html) |
|
403 |
->run('row_set_keyboard_events_by_id', $item_id); |
|
404 |
} |
|
387 | 405 |
|
388 | 406 |
$self->js |
389 |
->append('#row_table_id', $row_as_html) |
|
390 |
->val('.add_item_input', '') |
|
407 |
->run('close_multi_items_dialog') |
|
391 | 408 |
->run('row_table_scroll_down') |
392 |
->run('row_set_keyboard_events_by_id', $item_id) |
|
393 | 409 |
->on('.recalc', 'change', 'recalc_amounts_and_taxes') |
394 | 410 |
->on('.reformat_number', 'change', 'reformat_number') |
395 | 411 |
->focus('#add_item_parts_id_name'); |
... | ... | |
483 | 499 |
_make_order(); |
484 | 500 |
} |
485 | 501 |
|
502 |
sub init_multi_items_models { |
|
503 |
SL::Controller::Helper::GetModels->new( |
|
504 |
controller => $_[0], |
|
505 |
model => 'Part', |
|
506 |
with_objects => [ qw(unit_obj) ], |
|
507 |
disable_plugin => 'paginated', |
|
508 |
source => $::form->{multi_items}, |
|
509 |
sorted => { |
|
510 |
_default => { |
|
511 |
by => 'partnumber', |
|
512 |
dir => 1, |
|
513 |
}, |
|
514 |
partnumber => t8('Partnumber'), |
|
515 |
description => t8('Description')} |
|
516 |
); |
|
517 |
} |
|
518 |
|
|
486 | 519 |
sub _check_auth { |
487 | 520 |
my ($self) = @_; |
488 | 521 |
|
... | ... | |
565 | 598 |
return $order; |
566 | 599 |
} |
567 | 600 |
|
601 |
sub _make_item { |
|
602 |
my ($record, $attr) = @_; |
|
603 |
|
|
604 |
my $item = SL::DB::OrderItem->new; |
|
605 |
$item->assign_attributes(%$attr); |
|
606 |
|
|
607 |
my $part = SL::DB::Part->new(id => $attr->{parts_id})->load; |
|
608 |
my $price_source = SL::PriceSource->new(record_item => $item, record => $record); |
|
609 |
|
|
610 |
$item->unit($part->unit) if !$item->unit; |
|
611 |
|
|
612 |
my $price_src; |
|
613 |
if ($item->sellprice) { |
|
614 |
$price_src = $price_source->price_from_source(""); |
|
615 |
$price_src->price($item->sellprice); |
|
616 |
} else { |
|
617 |
$price_src = $price_source->best_price |
|
618 |
? $price_source->best_price |
|
619 |
: $price_source->price_from_source(""); |
|
620 |
$price_src->price(0) if !$price_source->best_price; |
|
621 |
} |
|
622 |
|
|
623 |
my $discount_src; |
|
624 |
if ($item->discount) { |
|
625 |
$discount_src = $price_source->discount_from_source(""); |
|
626 |
$discount_src->discount($item->discount); |
|
627 |
} else { |
|
628 |
$discount_src = $price_source->best_discount |
|
629 |
? $price_source->best_discount |
|
630 |
: $price_source->discount_from_source(""); |
|
631 |
$discount_src->discount(0) if !$price_source->best_discount; |
|
632 |
} |
|
633 |
|
|
634 |
my %new_attr; |
|
635 |
$new_attr{part} = $part; |
|
636 |
$new_attr{description} = $part->description if ! $item->description; |
|
637 |
$new_attr{qty} = 1.0 if ! $item->qty; |
|
638 |
$new_attr{sellprice} = $price_src->price; |
|
639 |
$new_attr{discount} = $discount_src->discount; |
|
640 |
$new_attr{active_price_source} = $price_src; |
|
641 |
$new_attr{active_discount_source} = $discount_src; |
|
642 |
|
|
643 |
# add_custom_variables adds cvars to an orderitem with no cvars for saving, but |
|
644 |
# they cannot be retrieved via custom_variables until the order/orderitem is |
|
645 |
# saved. Adding empty custom_variables to new orderitem here solves this problem. |
|
646 |
$new_attr{custom_variables} = []; |
|
647 |
|
|
648 |
$item->assign_attributes(%new_attr); |
|
649 |
|
|
650 |
return $item; |
|
651 |
} |
|
652 |
|
|
568 | 653 |
sub _recalc { |
569 | 654 |
my ($self) = @_; |
570 | 655 |
|
js/locale/de.js | ||
---|---|---|
2 | 2 |
"A transaction description is required.":"Die Vorgangsbezeichnung muss eingegeben werden.", |
3 | 3 |
"Add function block":"Funktionsblock hinzufügen", |
4 | 4 |
"Add linked record":"Verknüpften Beleg hinzufügen", |
5 |
"Add multiple items":"Mehrere Artikel hinzufügen", |
|
5 | 6 |
"Add picture":"Bild hinzufügen", |
6 | 7 |
"Add picture to text block":"Bild dem Textblock hinzufügen", |
7 | 8 |
"Add section":"Abschnitt hinzufügen", |
locale/de/all | ||
---|---|---|
187 | 187 |
'Add link: select records to link with' => 'Verknüpfungen hinzufügen: zu verknüpfende Belege auswählen', |
188 | 188 |
'Add linked record' => 'Verknüpften Beleg hinzufügen', |
189 | 189 |
'Add links' => 'Verknüpfungen hinzufügen', |
190 |
'Add multiple items' => 'Mehrere Artikel hinzufügen', |
|
190 | 191 |
'Add new currency' => 'Neue Währung hinzufügen', |
191 | 192 |
'Add new custom variable' => 'Neue benutzerdefinierte Variable erfassen', |
192 | 193 |
'Add new price rule item' => 'Neue Bedingung hinzufügen', |
... | ... | |
1783 | 1784 |
'No requirement spec statuses has been created yet.' => 'Es wurden noch keine Pflichtenheftstatus angelegt.', |
1784 | 1785 |
'No requirement spec templates have been created yet.' => 'Es wurden noch keine Pflichtenheftvorlagen angelegt.', |
1785 | 1786 |
'No requirement spec type has been created yet.' => 'Es wurden noch keine Pflichtenhefttypen angelegt.', |
1787 |
'No results.' => 'Keine Artikel', |
|
1786 | 1788 |
'No risks level has been created yet.' => 'Es wurden noch keine Risikograde angelegt.', |
1787 | 1789 |
'No sections created yet' => 'Keine Abschnitte erstellt', |
1788 | 1790 |
'No sections have been created so far.' => 'Bisher wurden noch keine Abschnitte angelegt.', |
... | ... | |
3079 | 3081 |
'To continue please change the taxkey 0 to another value.' => 'Um fortzufahren, ändern Sie bitte den Steuerschlüssel 0 auf einen anderen Wert.', |
3080 | 3082 |
'To user login' => 'Zum Benutzerlogin', |
3081 | 3083 |
'Toggle marker' => 'Markierung umschalten', |
3084 |
'Too many results (#1 from #2).' => 'Zu viele Artikel (#1 von #2)', |
|
3082 | 3085 |
'Top' => 'Oben', |
3083 | 3086 |
'Top (CSS)' => 'Oben (mit CSS)', |
3084 | 3087 |
'Top (Javascript)' => 'Oben (mit Javascript)', |
... | ... | |
3424 | 3427 |
'follow_up_list' => 'wiedervorlageliste', |
3425 | 3428 |
'for' => 'für', |
3426 | 3429 |
'for Period' => 'für den Zeitraum', |
3430 |
'for all' => 'für alle', |
|
3427 | 3431 |
'for date' => 'zum Stichtag', |
3428 | 3432 |
'found' => 'Gefunden', |
3429 | 3433 |
'from (time)' => 'von', |
templates/webpages/order/tabs/_multi_items_dialog.html | ||
---|---|---|
1 |
[%- USE T8 %][%- USE HTML %][%- USE L %][%- USE LxERP %] |
|
2 |
|
|
3 |
<form method="post" id="multi_items_form" method="POST"> |
|
4 |
|
|
5 |
<table id='multi_items_filter_table'> |
|
6 |
<tr> |
|
7 |
<th>[%- LxERP.t8("Description") %]/[%- LxERP.t8("Partnumber") %]:</th> |
|
8 |
<td>[%- L.input_tag('multi_items.filter.all:substr:multi::ilike', '') %]</td> |
|
9 |
<th>[%- LxERP.t8("Group") %]</th> |
|
10 |
<td>[%- L.select_tag('multi_items.filter.partsgroup_id', all_partsgroups, title_key='displayable_name', value_key='id', with_empty=1) %]</td> |
|
11 |
<tr> |
|
12 |
</table> |
|
13 |
|
|
14 |
[% L.button_tag('update_result()', LxERP.t8('Filter')) %] |
|
15 |
<a href='#' onClick='javascript:$("#multi_items_filter_table input").val("");$("#multi_items_filter_table input[type=checkbox]").prop("checked", 0);$("#multi_items_filter_table select").prop("selectedIndex", 0);'>[% 'Reset' | $T8 %]</a> |
|
16 |
|
|
17 |
<hr> |
|
18 |
<div id='multi_items_result'></div> |
|
19 |
<hr> |
|
20 |
|
|
21 |
[% L.button_tag('add_multi_items()', LxERP.t8('Continue')) %] |
|
22 |
<a href="#" onclick="close_multi_items_dialog();">[%- LxERP.t8("Cancel") %]</a> |
|
23 |
|
|
24 |
<script type='text/javascript'> |
|
25 |
function update_result() { |
|
26 |
var data = $('#multi_items_form').serialize(); |
|
27 |
data += '&type=[%- FORM.type %]'; |
|
28 |
$.ajax({ |
|
29 |
url: 'controller.pl?action=Order/multi_items_update_result', |
|
30 |
data: data, |
|
31 |
method: 'post', |
|
32 |
success: function(data){ |
|
33 |
$('#multi_items_result').html(data); |
|
34 |
$('#multi_items_result input').keydown(function(event) { |
|
35 |
if(event.keyCode == 13) { |
|
36 |
event.preventDefault(); |
|
37 |
add_multi_items(); |
|
38 |
return false; |
|
39 |
} |
|
40 |
}); |
|
41 |
} |
|
42 |
}); |
|
43 |
} |
|
44 |
|
|
45 |
function add_multi_items() { |
|
46 |
var data = $('#[%- FORM.callback_data_id %]').serialize(); |
|
47 |
data += '&'; |
|
48 |
data += $('#multi_items_form').serialize(); |
|
49 |
data += '&action=[%- FORM.callback %]'; |
|
50 |
$.post("controller.pl", data, kivi.eval_json_result); |
|
51 |
} |
|
52 |
|
|
53 |
$('#multi_items_filter_table input, #multi_items_filter_table select').keydown(function(event) { |
|
54 |
if(event.keyCode == 13) { |
|
55 |
event.preventDefault(); |
|
56 |
update_result(); |
|
57 |
return false; |
|
58 |
} |
|
59 |
}); |
|
60 |
|
|
61 |
$('#multi_items_filter_all_substr_multi_ilike').focus(); |
|
62 |
</script> |
|
63 |
|
|
64 |
</form> |
templates/webpages/order/tabs/_multi_items_result.html | ||
---|---|---|
1 |
[%- USE T8 %][%- USE HTML %][%- USE L %][%- USE LxERP %] |
|
2 |
|
|
3 |
<table width="100%"> |
|
4 |
<tr> |
|
5 |
<td>[% 'for all' | $T8 %] |
|
6 |
<td>[% L.input_tag("multi_items.all_qty", '', size = 5, style='text-align:right') %]</td> |
|
7 |
</tr> |
|
8 |
<tr> |
|
9 |
<td colspan="5"><hr></td> |
|
10 |
</tr> |
|
11 |
[%- FOREACH item = multi_items %] |
|
12 |
<tr> |
|
13 |
<td></td> |
|
14 |
<td> |
|
15 |
[% L.hidden_tag("add_multi_items[+].parts_id", item.id) %] |
|
16 |
[% L.input_tag("add_multi_items[].qty_as_number", '', size = 5, style='text-align:right', |
|
17 |
class = 'multi_items_qty', onclick = 'set_qty_to_one(this)') %] |
|
18 |
</td> |
|
19 |
<td>[% HTML.escape(item.unit) %]</td> |
|
20 |
<td>[% HTML.escape(item.partnumber) %]</td> |
|
21 |
<td>[% HTML.escape(item.description) %]</td> |
|
22 |
</tr> |
|
23 |
[%- END %] |
|
24 |
</table> |
|
25 |
|
|
26 |
<script type='text/javascript'> |
|
27 |
function set_qty_to_one(clicked) { |
|
28 |
if ($(clicked).val() == '') { |
|
29 |
$(clicked).val('[%- LxERP.format_amount(1.00, -2) %]'); |
|
30 |
} |
|
31 |
$(clicked).select(); |
|
32 |
} |
|
33 |
|
|
34 |
$('#multi_items_all_qty').change(function(event){ |
|
35 |
$('.multi_items_qty').val($(event.target).val()); |
|
36 |
}); |
|
37 |
</script> |
templates/webpages/order/tabs/basic_data.html | ||
---|---|---|
143 | 143 |
|
144 | 144 |
[%- PROCESS order/tabs/_item_input.html %] |
145 | 145 |
|
146 |
[% L.button_tag('show_multi_items_dialog()', LxERP.t8('Add multiple parts')) %]</td> |
|
147 |
|
|
146 | 148 |
<table width="100%"> |
147 | 149 |
<tr> |
148 | 150 |
<td> |
... | ... | |
280 | 282 |
$.post("controller.pl", data, kivi.eval_json_result); |
281 | 283 |
} |
282 | 284 |
|
285 |
function show_multi_items_dialog() { |
|
286 |
if (!check_cv()) return; |
|
287 |
|
|
288 |
kivi.popup_dialog({ |
|
289 |
url: 'controller.pl?action=Order/show_multi_items_dialog', |
|
290 |
data: { type: $('#type').val(), |
|
291 |
callback: 'Order/add_multi_items', |
|
292 |
callback_data_id: 'order_form' }, |
|
293 |
id: 'jq_multi_items_dialog', |
|
294 |
dialog: { |
|
295 |
title: kivi.t8('Add multiple items'), |
|
296 |
width: 800, |
|
297 |
height: 500 |
|
298 |
} |
|
299 |
}); |
|
300 |
return true; |
|
301 |
} |
|
302 |
|
|
303 |
function close_multi_items_dialog() { |
|
304 |
$('#jq_multi_items_dialog').dialog('close'); |
|
305 |
}; |
|
306 |
|
|
283 | 307 |
function delete_order_item_row(clicked) { |
284 | 308 |
var row = $(clicked).parents("tbody").first(); |
285 | 309 |
$(row).remove(); |
Auch abrufbar als: Unified diff
Auftrags-Controller: Mehrfach-Artikelauswahl mit Mengeneingabe.