Revision 099fc63b
Von Bernd Bleßmann vor fast 9 Jahren hinzugefügt
SL/Controller/Order.pm | ||
---|---|---|
1 |
package SL::Controller::Order; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent qw(SL::Controller::Base); |
|
5 |
|
|
6 |
use SL::Helper::Flash; |
|
7 |
use SL::Presenter; |
|
8 |
use SL::PriceSource; |
|
9 |
|
|
10 |
use SL::DB::Order; |
|
11 |
use SL::DB::Customer; |
|
12 |
use SL::DB::Vendor; |
|
13 |
use SL::DB::TaxZone; |
|
14 |
use SL::DB::Employee; |
|
15 |
use SL::DB::Project; |
|
16 |
use SL::DB::Default; |
|
17 |
use SL::DB::Unit; |
|
18 |
|
|
19 |
use SL::Helper::DateTime; |
|
20 |
|
|
21 |
use List::Util qw(max first); |
|
22 |
use List::MoreUtils qw(none pairwise); |
|
23 |
|
|
24 |
use Rose::Object::MakeMethods::Generic |
|
25 |
( |
|
26 |
'scalar --get_set_init' => [ qw(order valid_types type cv p) ], |
|
27 |
); |
|
28 |
|
|
29 |
|
|
30 |
# safety |
|
31 |
__PACKAGE__->run_before('_check_auth'); |
|
32 |
|
|
33 |
__PACKAGE__->run_before('_recalc', |
|
34 |
only => [ qw(edit update save) ]); |
|
35 |
|
|
36 |
__PACKAGE__->run_before('_get_unalterable_data', |
|
37 |
only => [ qw(save save_and_delivery_order create_pdf send_email) ]); |
|
38 |
|
|
39 |
# |
|
40 |
# actions |
|
41 |
# |
|
42 |
|
|
43 |
sub action_add { |
|
44 |
my ($self) = @_; |
|
45 |
|
|
46 |
$self->order->transdate(DateTime->now_local()); |
|
47 |
|
|
48 |
$self->_pre_render(); |
|
49 |
$self->render( |
|
50 |
'order/form', |
|
51 |
title => $self->type eq _sales_order_type() ? $::locale->text('Add Sales Order') |
|
52 |
: $self->type eq _purchase_order_type() ? $::locale->text('Add Purchase Order') |
|
53 |
: '', |
|
54 |
%{$self->{template_args}} |
|
55 |
); |
|
56 |
} |
|
57 |
|
|
58 |
sub action_edit { |
|
59 |
my ($self) = @_; |
|
60 |
|
|
61 |
$self->_pre_render(); |
|
62 |
$self->render( |
|
63 |
'order/form', |
|
64 |
title => $self->type eq _sales_order_type() ? $::locale->text('Edit Sales Order') |
|
65 |
: $self->type eq _purchase_order_type() ? $::locale->text('Edit Purchase Order') |
|
66 |
: '', |
|
67 |
%{$self->{template_args}} |
|
68 |
); |
|
69 |
} |
|
70 |
|
|
71 |
sub action_update { |
|
72 |
my ($self) = @_; |
|
73 |
|
|
74 |
$self->_pre_render(); |
|
75 |
$self->render( |
|
76 |
'order/form', |
|
77 |
title => $self->type eq _sales_order_type() ? $::locale->text('Edit Sales Order') |
|
78 |
: $self->type eq _purchase_order_type() ? $::locale->text('Edit Purchase Order') |
|
79 |
: '', |
|
80 |
%{$self->{template_args}} |
|
81 |
); |
|
82 |
} |
|
83 |
|
|
84 |
sub action_save { |
|
85 |
my ($self) = @_; |
|
86 |
|
|
87 |
my $errors = $self->_save(); |
|
88 |
|
|
89 |
if (scalar @{ $errors }) { |
|
90 |
$self->js->flash('error', $_) foreach @{ $errors }; |
|
91 |
return $self->js->render(); |
|
92 |
} |
|
93 |
|
|
94 |
flash_later('info', $::locale->text('The order has been saved')); |
|
95 |
my @redirect_params = ( |
|
96 |
action => 'edit', |
|
97 |
type => $self->type, |
|
98 |
id => $self->order->id, |
|
99 |
); |
|
100 |
|
|
101 |
$self->redirect_to(@redirect_params); |
|
102 |
} |
|
103 |
|
|
104 |
sub action_customer_vendor_changed { |
|
105 |
my ($self) = @_; |
|
106 |
|
|
107 |
my $cv_method = $self->cv; |
|
108 |
|
|
109 |
if ($self->order->$cv_method->contacts && scalar @{ $self->order->$cv_method->contacts } > 0) { |
|
110 |
$self->js->show('#cp_row'); |
|
111 |
} else { |
|
112 |
$self->js->hide('#cp_row'); |
|
113 |
} |
|
114 |
|
|
115 |
if ($self->order->$cv_method->shipto && scalar @{ $self->order->$cv_method->shipto } > 0) { |
|
116 |
$self->js->show('#shipto_row'); |
|
117 |
} else { |
|
118 |
$self->js->hide('#shipto_row'); |
|
119 |
} |
|
120 |
|
|
121 |
$self->js |
|
122 |
->replaceWith('#order_cp_id', $self->build_contact_select) |
|
123 |
->replaceWith('#order_shipto_id', $self->build_shipto_select) |
|
124 |
->val('#order_taxzone_id', $self->order->taxzone_id) |
|
125 |
->focus('#order_' . $self->cv . '_id') |
|
126 |
->render($self); |
|
127 |
} |
|
128 |
|
|
129 |
sub action_add_item { |
|
130 |
my ($self) = @_; |
|
131 |
|
|
132 |
my $form_attr = $::form->{add_item}; |
|
133 |
|
|
134 |
return unless $form_attr->{parts_id}; |
|
135 |
|
|
136 |
my $item = SL::DB::OrderItem->new; |
|
137 |
$item->assign_attributes(%$form_attr); |
|
138 |
|
|
139 |
my $part = SL::DB::Part->new(id => $form_attr->{parts_id})->load; |
|
140 |
my $cv_method = $self->cv; |
|
141 |
my $cv_discount = $self->order->$cv_method? $self->order->$cv_method->discount : 0.0; |
|
142 |
|
|
143 |
my %new_attr; |
|
144 |
$new_attr{part} = $part; |
|
145 |
$new_attr{description} = $part->description if ! $item->description; |
|
146 |
$new_attr{qty} = 1.0 if ! $item->qty; |
|
147 |
$new_attr{unit} = $part->unit; |
|
148 |
$new_attr{sellprice} = $part->sellprice if ! $item->sellprice; |
|
149 |
$new_attr{discount} = $cv_discount if ! $item->discount; |
|
150 |
|
|
151 |
# add_custom_variables adds cvars to an orderitem with no cvars for saving, but |
|
152 |
# they cannot be retrieved via custom_variables until the order/orderitem is |
|
153 |
# saved. Adding empty custom_variables to new orderitem here solves this problem. |
|
154 |
$new_attr{custom_variables} = []; |
|
155 |
|
|
156 |
$item->assign_attributes(%new_attr); |
|
157 |
|
|
158 |
$self->order->add_items($item); |
|
159 |
|
|
160 |
$self->_recalc(); |
|
161 |
|
|
162 |
my $item_id = join('_', 'new', Time::HiRes::gettimeofday(), int rand 1000000000000); |
|
163 |
my $row_as_html = $self->p->render('order/tabs/_row', ITEM => $item, ID => $item_id); |
|
164 |
|
|
165 |
$self->js |
|
166 |
->append('#row_table_id', $row_as_html) |
|
167 |
->val('.add_item_input', '') |
|
168 |
->run('row_table_scroll_down') |
|
169 |
->run('row_set_keyboard_events_by_id', $item_id) |
|
170 |
->on('.recalc', 'change', 'recalc_amounts_and_taxes') |
|
171 |
->focus('#add_item_parts_id_name'); |
|
172 |
|
|
173 |
$self->_js_redisplay_amounts_and_taxes; |
|
174 |
$self->js->render(); |
|
175 |
} |
|
176 |
|
|
177 |
sub action_recalc_amounts_and_taxes { |
|
178 |
my ($self) = @_; |
|
179 |
|
|
180 |
$self->_recalc(); |
|
181 |
|
|
182 |
$self->_js_redisplay_linetotals; |
|
183 |
$self->_js_redisplay_amounts_and_taxes; |
|
184 |
$self->js->render(); |
|
185 |
} |
|
186 |
|
|
187 |
sub _js_redisplay_linetotals { |
|
188 |
my ($self) = @_; |
|
189 |
|
|
190 |
my @data = map {$::form->format_amount(\%::myconfig, $_->{linetotal}, 2, 0)} @{ $self->order->items }; |
|
191 |
$self->js |
|
192 |
->run('redisplay_linetotals', \@data); |
|
193 |
} |
|
194 |
|
|
195 |
sub _js_redisplay_amounts_and_taxes { |
|
196 |
my ($self) = @_; |
|
197 |
|
|
198 |
$self->js |
|
199 |
->html('#netamount_id', $::form->format_amount(\%::myconfig, $self->order->netamount, -2)) |
|
200 |
->html('#amount_id', $::form->format_amount(\%::myconfig, $self->order->amount, -2)) |
|
201 |
->remove('.tax_row') |
|
202 |
->insertBefore($self->build_tax_rows, '#amount_row_id'); |
|
203 |
} |
|
204 |
|
|
205 |
# |
|
206 |
# helpers |
|
207 |
# |
|
208 |
|
|
209 |
sub init_valid_types { |
|
210 |
[ _sales_order_type(), _purchase_order_type() ]; |
|
211 |
} |
|
212 |
|
|
213 |
sub init_type { |
|
214 |
my ($self) = @_; |
|
215 |
|
|
216 |
if (none { $::form->{type} eq $_ } @{$self->valid_types}) { |
|
217 |
die "Not a valid type for order"; |
|
218 |
} |
|
219 |
|
|
220 |
$self->type($::form->{type}); |
|
221 |
} |
|
222 |
|
|
223 |
sub init_cv { |
|
224 |
my ($self) = @_; |
|
225 |
|
|
226 |
my $cv = $self->type eq _sales_order_type() ? 'customer' |
|
227 |
: $self->type eq _purchase_order_type() ? 'vendor' |
|
228 |
: die "Not a valid type for order"; |
|
229 |
|
|
230 |
return $cv; |
|
231 |
} |
|
232 |
|
|
233 |
sub init_p { |
|
234 |
SL::Presenter->get; |
|
235 |
} |
|
236 |
|
|
237 |
sub init_order { |
|
238 |
_make_order(); |
|
239 |
} |
|
240 |
|
|
241 |
sub _check_auth { |
|
242 |
my ($self) = @_; |
|
243 |
|
|
244 |
my $right_for = { map { $_ => $_.'_edit' } @{$self->valid_types} }; |
|
245 |
|
|
246 |
my $right = $right_for->{ $self->type }; |
|
247 |
$right ||= 'DOES_NOT_EXIST'; |
|
248 |
|
|
249 |
$::auth->assert($right); |
|
250 |
} |
|
251 |
|
|
252 |
sub build_contact_select { |
|
253 |
my ($self) = @_; |
|
254 |
|
|
255 |
$self->p->select_tag('order.cp_id', [ $self->order->{$self->cv}->contacts ], |
|
256 |
value_key => 'cp_id', |
|
257 |
title_key => 'full_name_dep', |
|
258 |
default => $self->order->cp_id, |
|
259 |
with_empty => 1, |
|
260 |
style => 'width: 300px', |
|
261 |
); |
|
262 |
} |
|
263 |
|
|
264 |
sub build_shipto_select { |
|
265 |
my ($self) = @_; |
|
266 |
|
|
267 |
$self->p->select_tag('order.shipto_id', [ $self->order->{$self->cv}->shipto ], |
|
268 |
value_key => 'shipto_id', |
|
269 |
title_key => 'displayable_id', |
|
270 |
default => $self->order->shipto_id, |
|
271 |
with_empty => 1, |
|
272 |
style => 'width: 300px', |
|
273 |
); |
|
274 |
} |
|
275 |
|
|
276 |
sub build_tax_rows { |
|
277 |
my ($self) = @_; |
|
278 |
|
|
279 |
my $rows_as_html; |
|
280 |
foreach my $tax (sort { $a->{tax}->rate cmp $b->{tax}->rate } @{ $self->{taxes} }) { |
|
281 |
$rows_as_html .= $self->p->render('order/tabs/_tax_row', TAX => $tax); |
|
282 |
} |
|
283 |
return $rows_as_html; |
|
284 |
} |
|
285 |
|
|
286 |
|
|
287 |
sub _make_order { |
|
288 |
my ($self) = @_; |
|
289 |
|
|
290 |
# add_items adds items to an order with no items for saving, but they cannot |
|
291 |
# be retrieved via items until the order is saved. Adding empty items to new |
|
292 |
# order here solves this problem. |
|
293 |
my $order; |
|
294 |
$order = SL::DB::Manager::Order->find_by(id => $::form->{id}) if $::form->{id}; |
|
295 |
$order ||= SL::DB::Order->new(orderitems => []); |
|
296 |
|
|
297 |
$order->assign_attributes(%{$::form->{order}}); |
|
298 |
|
|
299 |
return $order; |
|
300 |
} |
|
301 |
|
|
302 |
|
|
303 |
sub _recalc { |
|
304 |
my ($self) = @_; |
|
305 |
|
|
306 |
# bb: todo: currency later |
|
307 |
$self->order->currency_id($::instance_conf->get_currency_id()); |
|
308 |
|
|
309 |
my %pat = $self->order->calculate_prices_and_taxes(); |
|
310 |
$self->{taxes} = []; |
|
311 |
foreach my $tax_chart_id (keys %{ $pat{taxes} }) { |
|
312 |
my $tax = SL::DB::Manager::Tax->find_by(chart_id => $tax_chart_id); |
|
313 |
push(@{ $self->{taxes} }, { amount => $pat{taxes}->{$tax_chart_id}, |
|
314 |
tax => $tax }); |
|
315 |
} |
|
316 |
|
|
317 |
pairwise { $a->{linetotal} = $b->{linetotal} } @{$self->order->items}, @{$pat{items}}; |
|
318 |
} |
|
319 |
|
|
320 |
|
|
321 |
sub _get_unalterable_data { |
|
322 |
my ($self) = @_; |
|
323 |
|
|
324 |
foreach my $item (@{ $self->order->items }) { |
|
325 |
if ($item->id) { |
|
326 |
# load data from orderitems (db) |
|
327 |
my $db_item = SL::DB::OrderItem->new(id => $item->id)->load; |
|
328 |
$item->$_($db_item->$_) for qw(active_discount_source active_price_source longdescription); |
|
329 |
} else { |
|
330 |
# set data from part (or other sources) |
|
331 |
$item->longdescription($item->part->notes); |
|
332 |
#$item->active_price_source(''); |
|
333 |
#$item->active_discount_source(''); |
|
334 |
} |
|
335 |
|
|
336 |
# autovivify all cvars that are not in the form (cvars_by_config can do it). |
|
337 |
# workaround to pre-parse number-cvars (parse_custom_variable_values does not parse number values). |
|
338 |
foreach my $var (@{ $item->cvars_by_config }) { |
|
339 |
$var->unparsed_value($::form->parse_amount(\%::myconfig, $var->{__unparsed_value})) if ($var->config->type eq 'number' && exists($var->{__unparsed_value})); |
|
340 |
} |
|
341 |
$item->parse_custom_variable_values; |
|
342 |
} |
|
343 |
} |
|
344 |
|
|
345 |
|
|
346 |
sub _save { |
|
347 |
my ($self) = @_; |
|
348 |
|
|
349 |
my $errors = []; |
|
350 |
my $db = $self->order->db; |
|
351 |
|
|
352 |
$db->do_transaction( |
|
353 |
sub { |
|
354 |
$self->order->save(); |
|
355 |
}) || push(@{$errors}, $db->error); |
|
356 |
|
|
357 |
return $errors; |
|
358 |
} |
|
359 |
|
|
360 |
|
|
361 |
sub _pre_render { |
|
362 |
my ($self) = @_; |
|
363 |
|
|
364 |
$self->{all_taxzones} = SL::DB::Manager::TaxZone->get_all_sorted(); |
|
365 |
$self->{all_employees} = SL::DB::Manager::Employee->get_all(where => [ or => [ id => $self->order->employee_id, |
|
366 |
deleted => 0 ] ], |
|
367 |
sort_by => 'name'); |
|
368 |
$self->{all_salesmen} = SL::DB::Manager::Employee->get_all(where => [ or => [ id => $self->order->salesman_id, |
|
369 |
deleted => 0 ] ], |
|
370 |
sort_by => 'name'); |
|
371 |
$self->{all_projects} = SL::DB::Manager::Project->get_all(where => [ or => [ id => $self->order->globalproject_id, |
|
372 |
active => 1 ] ], |
|
373 |
sort_by => 'projectnumber'); |
|
374 |
|
|
375 |
$self->{current_employee_id} = SL::DB::Manager::Employee->current->id; |
|
376 |
} |
|
377 |
|
|
378 |
sub _sales_order_type { |
|
379 |
'sales_order'; |
|
380 |
} |
|
381 |
|
|
382 |
sub _purchase_order_type { |
|
383 |
'purchase_order'; |
|
384 |
} |
|
385 |
|
|
386 |
1; |
js/locale/de.js | ||
---|---|---|
52 | 52 |
"Part picker":"Artikelauswahl", |
53 | 53 |
"Paste":"Einfügen", |
54 | 54 |
"Paste template":"Vorlage einfügen", |
55 |
"Please select a customer.":"Bitte wählen Sie einen Kunden aus.", |
|
56 |
"Please select a vendor.":"Bitte wählen Sie einen Lieferanten aus.", |
|
55 | 57 |
"Price Types":"Preistypen", |
56 | 58 |
"Project link actions":"Projektverknüpfungs-Aktionen", |
57 | 59 |
"Quotations/Orders actions":"Aktionen für Angebote/Aufträge", |
locale/de/all | ||
---|---|---|
2019 | 2019 |
'Please re-run the analysis for broken general ledger entries by clicking this button:' => 'Bitte wiederholen Sie die Analyse der Hauptbucheinträge, indem Sie auf diesen Button klicken:', |
2020 | 2020 |
'Please read the file' => 'Bitte lesen Sie die Datei', |
2021 | 2021 |
'Please select a customer from the list below.' => 'Bitte einen Endkunden aus der Liste auswählen', |
2022 |
'Please select a customer.' => 'Bitte wählen Sie einen Kunden aus.', |
|
2022 | 2023 |
'Please select a part from the list below.' => 'Bitte wählen Sie einen Artikel aus der Liste aus.', |
2023 | 2024 |
'Please select a vendor from the list below.' => 'Bitte einen Händler aus der Liste auswählen', |
2025 |
'Please select a vendor.' => 'Bitte wählen Sie einen Lieferanten aus.', |
|
2024 | 2026 |
'Please select the dataset you want to delete:' => 'Bitte wählen Sie die zu löschende Datenbank aus:', |
2025 | 2027 |
'Please select the destination bank account for the collections:' => 'Bitte wählen Sie das Bankkonto als Ziel für die Einzüge aus:', |
2026 | 2028 |
'Please select the source bank account for the transfers:' => 'Bitte wählen Sie das Bankkonto als Quelle für die Überweisungen aus:', |
... | ... | |
2837 | 2839 |
'The number of days for full payment' => 'Die Anzahl Tage, bis die Rechnung in voller Höhe bezahlt werden muss', |
2838 | 2840 |
'The numbering will start at 1 with each requirement spec.' => 'Die Nummerierung beginnt bei jedem Pflichtenheft bei 1.', |
2839 | 2841 |
'The option field is empty.' => 'Das Optionsfeld ist leer.', |
2842 |
'The order has been saved' => 'Der Auftrag wurde gespeichert.', |
|
2840 | 2843 |
'The package name is invalid.' => 'Der Paketname ist ungültig.', |
2841 | 2844 |
'The parts for this delivery order have already been transferred in.' => 'Die Artikel dieses Lieferscheins wurden bereits eingelagert.', |
2842 | 2845 |
'The parts for this delivery order have already been transferred out.' => 'Die Artikel dieses Lieferscheins wurden bereits ausgelagert.', |
... | ... | |
3390 | 3393 |
'dated' => 'datiert', |
3391 | 3394 |
'debug' => 'Debug', |
3392 | 3395 |
'delete' => 'Löschen', |
3396 |
'delete item' => 'Position löschen', |
|
3393 | 3397 |
'delivered' => 'geliefert', |
3394 | 3398 |
'deliverydate' => 'Lieferdatum', |
3395 | 3399 |
'difference as skonto' => 'Differenz als Skonto', |
templates/webpages/order/form.html | ||
---|---|---|
1 |
[%- USE T8 %] |
|
2 |
[%- USE LxERP %] |
|
3 |
[%- USE L %] |
|
4 |
|
|
5 |
<form method="post" action="controller.pl" id="order_form"> |
|
6 |
<div class="listtop">[% FORM.title %]</div> |
|
7 |
|
|
8 |
[% L.hidden_tag('callback', FORM.callback) %] |
|
9 |
[% L.hidden_tag('type', FORM.type) %] |
|
10 |
[% L.hidden_tag('id', SELF.order.id) %] |
|
11 |
|
|
12 |
[%- INCLUDE 'common/flash.html' %] |
|
13 |
|
|
14 |
<div class="tabwidget" id="order_tabs"> |
|
15 |
<ul> |
|
16 |
<li><a href="#ui-tabs-basic-data">[% 'Basic Data' | $T8 %]</a></li> |
|
17 |
[%- IF INSTANCE_CONF.get_webdav %] |
|
18 |
<li><a href="#ui-tabs-webdav">[% 'WebDAV' | $T8 %]</a></li> |
|
19 |
[%- END %] |
|
20 |
</ul> |
|
21 |
|
|
22 |
[% PROCESS "order/tabs/basic_data.html" %] |
|
23 |
[% PROCESS 'webdav/_list.html' %] |
|
24 |
<div id="ui-tabs-1"> |
|
25 |
[%- LxERP.t8("Loading...") %] |
|
26 |
</div> |
|
27 |
</div> |
|
28 |
|
|
29 |
<br> |
|
30 |
|
|
31 |
[% L.hidden_tag('action', 'Order/dispatch') %] |
|
32 |
|
|
33 |
[% L.button_tag('save()', LxERP.t8('Save')) %] |
|
34 |
|
|
35 |
</form> |
|
36 |
|
|
37 |
|
|
38 |
<script type='text/javascript'> |
|
39 |
|
|
40 |
function save() { |
|
41 |
if (!check_cv()) return; |
|
42 |
var data = $('#order_form').serialize(); |
|
43 |
data += '&action=Order/save'; |
|
44 |
|
|
45 |
$.post("controller.pl", data, kivi.eval_json_result); |
|
46 |
} |
|
47 |
|
|
48 |
function check_cv() { |
|
49 |
if ($('#order_[%- cv_id %]').val() == '') { |
|
50 |
[%- IF SELF.cv == 'customer' %] |
|
51 |
alert(kivi.t8('Please select a customer.')); |
|
52 |
[%- ELSE %] |
|
53 |
alert(kivi.t8('Please select a vendor.')); |
|
54 |
[%- END %] |
|
55 |
return false; |
|
56 |
} |
|
57 |
return true; |
|
58 |
} |
|
59 |
</script> |
templates/webpages/order/tabs/_item_input.html | ||
---|---|---|
1 |
[%- USE T8 %][%- USE HTML %][%- USE LxERP %][%- USE L %] |
|
2 |
|
|
3 |
<div> |
|
4 |
<table id="input_row_table_id"> |
|
5 |
<thead> |
|
6 |
<tr class="listheading"> |
|
7 |
<th class="listheading" nowrap >[%- 'Part' | $T8 %] </th> |
|
8 |
<th class="listheading" nowrap >[%- 'Description' | $T8 %] </th> |
|
9 |
<th class="listheading" nowrap width="5" >[%- 'Qty' | $T8 %] </th> |
|
10 |
<th class="listheading" nowrap width="15">[%- 'Price' | $T8 %] </th> |
|
11 |
<th class="listheading" nowrap width="5" >[%- 'Discount' | $T8 %] </th> |
|
12 |
<th></th> |
|
13 |
</tr> |
|
14 |
</thead> |
|
15 |
<tbody> |
|
16 |
<tr valign="top" class="listrow"> |
|
17 |
<td>[% L.part_picker('add_item.parts_id', '', fat_set_item=1, style='width: 300px', class="add_item_input") %]</td> |
|
18 |
<td>[% L.input_tag('add_item.description', '', class="add_item_input") %]</td> |
|
19 |
<td>[% L.input_tag('add_item.qty_as_number', '', size = 5, style='text-align:right', class="add_item_input") %]</td> |
|
20 |
<td>[% L.input_tag('add_item.sellprice_as_number', '', size = 10, style='text-align:right', class="add_item_input") %]</td> |
|
21 |
<td>[% L.input_tag('add_item.discount_as_percent', '', size = 5, style='text-align:right', class="add_item_input") %]</td> |
|
22 |
<td>[% L.button_tag('add_item()', LxERP.t8('Add part')) %]</td> |
|
23 |
</tr> |
|
24 |
</tbody> |
|
25 |
</table> |
|
26 |
</div> |
templates/webpages/order/tabs/_row.html | ||
---|---|---|
1 |
[%- USE T8 %] |
|
2 |
[%- USE HTML %] |
|
3 |
[%- USE LxERP %] |
|
4 |
[%- USE L %] |
|
5 |
|
|
6 |
<tbody class="row_entry"> |
|
7 |
|
|
8 |
<tr class="listrow0"> |
|
9 |
<td style='display:none'> |
|
10 |
[% L.hidden_tag("order.orderitems[+].id", ITEM.id, id='item_' _ ID) %] |
|
11 |
[% L.hidden_tag("order.orderitems[].parts_id", ITEM.parts_id) %] |
|
12 |
</td> |
|
13 |
<td align="center"> |
|
14 |
<img src="image/updown.png" alt="[%- LxERP.t8('reorder item') %]" class="dragdrop"> |
|
15 |
</td> |
|
16 |
<td align="center"> |
|
17 |
[%- L.button_tag("delete_order_item_row(this)", |
|
18 |
LxERP.t8("X"), |
|
19 |
confirm=LxERP.t8("Are you sure?")) %] |
|
20 |
</td> |
|
21 |
<td> |
|
22 |
[% HTML.escape(ITEM.part.partnumber) %] |
|
23 |
</td> |
|
24 |
<td> |
|
25 |
[% L.input_tag("order.orderitems[].description", |
|
26 |
ITEM.description, |
|
27 |
style='width: 300px') %] |
|
28 |
</td> |
|
29 |
<td> |
|
30 |
[%- L.input_tag("order.orderitems[].qty_as_number", |
|
31 |
ITEM.qty_as_number, |
|
32 |
size = 5, |
|
33 |
style='text-align:right', |
|
34 |
class="recalc") %] |
|
35 |
</td> |
|
36 |
<td> |
|
37 |
[%- L.input_tag("order.orderitems[].price_factor", |
|
38 |
ITEM.price_factor, |
|
39 |
size = 5, |
|
40 |
style='text-align:right', |
|
41 |
class="recalc") %] |
|
42 |
</td> |
|
43 |
<td nowrap> |
|
44 |
[%- L.input_tag("order.orderitems[].unit", |
|
45 |
ITEM.unit, |
|
46 |
size = 5, |
|
47 |
class="recalc") %] |
|
48 |
</td> |
|
49 |
<td> |
|
50 |
[%- L.input_tag("order.orderitems[].sellprice_as_number", |
|
51 |
ITEM.sellprice_as_number, |
|
52 |
size = 10, |
|
53 |
style='text-align:right', |
|
54 |
class="recalc") %] |
|
55 |
</td> |
|
56 |
<td> |
|
57 |
[%- L.input_tag("order.orderitems[].discount_as_percent", |
|
58 |
ITEM.discount_as_percent, |
|
59 |
size = 5, |
|
60 |
style='text-align:right', |
|
61 |
class="recalc") %] |
|
62 |
</td> |
|
63 |
<td align="right"> |
|
64 |
[%- L.div_tag(LxERP.format_amount(ITEM.linetotal, 2, 0), name="linetotal") %] |
|
65 |
</td> |
|
66 |
|
|
67 |
</tr> |
|
68 |
|
|
69 |
<tr class="listrow1" style="display:none"> |
|
70 |
<td colspan="100%"> |
|
71 |
<table> |
|
72 |
<tr class="listrow1"> |
|
73 |
[%- SET n = 0 %] |
|
74 |
[%- FOREACH var = ITEM.cvars_by_config %] |
|
75 |
[%- NEXT UNLESS (var.config.processed_flags.editable && ITEM.part.cvar_by_name(var.config.name).is_valid) %] |
|
76 |
[%- SET n = n + 1 %] |
|
77 |
<th> |
|
78 |
[% var.config.description %] |
|
79 |
</th> |
|
80 |
<td> |
|
81 |
[% L.hidden_tag('order.orderitems[].custom_variables[+].config_id', var.config.id) %] |
|
82 |
[% L.hidden_tag('order.orderitems[].custom_variables[].id', var.id) %] |
|
83 |
[% L.hidden_tag('order.orderitems[].custom_variables[].sub_module', var.sub_module) %] |
|
84 |
[% INCLUDE 'common/render_cvar_input.html' var_name='order.orderitems[].custom_variables[].unparsed_value' %] |
|
85 |
</td> |
|
86 |
[%- IF (n % (MYCONFIG.form_cvars_nr_cols || 3)) == 0 %] |
|
87 |
</tr><tr class="listrow1"> |
|
88 |
[%- END %] |
|
89 |
[%- END %] |
|
90 |
</tr> |
|
91 |
</table> |
|
92 |
</td> |
|
93 |
</tr> |
|
94 |
|
|
95 |
</tbody> |
templates/webpages/order/tabs/_tax_row.html | ||
---|---|---|
1 |
[%- USE T8 %] |
|
2 |
[%- USE HTML %] |
|
3 |
[%- USE LxERP %] |
|
4 |
[%- USE L %] |
|
5 |
|
|
6 |
<tr class="tax_row"> |
|
7 |
<th align="right">[%- TAX.tax.taxdescription %] [% TAX.tax.rate_as_percent %]%</th> |
|
8 |
<td align="right">[%- LxERP.format_amount(TAX.amount, 2, 0) %]</td> |
|
9 |
</tr> |
templates/webpages/order/tabs/basic_data.html | ||
---|---|---|
1 |
[%- USE T8 %] |
|
2 |
[%- USE HTML %] |
|
3 |
[%- USE LxERP %] |
|
4 |
[%- USE L %] |
|
5 |
|
|
6 |
<div id="ui-tabs-basic-data"> |
|
7 |
<table width="100%"> |
|
8 |
<tr valign="top"> |
|
9 |
<td> |
|
10 |
<table width="100%"> |
|
11 |
<tr> |
|
12 |
<th align="right">[% SELF.cv | $T8 %]</th> |
|
13 |
[% SET cv_id = SELF.cv _ '_id' %] |
|
14 |
<td>[% L.customer_vendor_picker("order.${SELF.cv}" _ '_id', SELF.order.$cv_id, type=SELF.cv, style='width: 300px') %]</td> |
|
15 |
</tr> |
|
16 |
|
|
17 |
<tr id='cp_row' [%- IF !SELF.order.${SELF.cv}.contacts.size %]style='display:none'[%- END %]> |
|
18 |
<th align="right">[% 'Contact Person' | $T8 %]</th> |
|
19 |
<td>[% L.select_tag('order.cp_id', |
|
20 |
SELF.order.${SELF.cv}.contacts, |
|
21 |
default=SELF.order.cp_id, |
|
22 |
title_key='full_name_dep', |
|
23 |
value_key='cp_id', |
|
24 |
with_empty=1, |
|
25 |
style='width: 300px') %]</td> |
|
26 |
</tr> |
|
27 |
|
|
28 |
<tr id='shipto_row' [%- IF !SELF.order.${SELF.cv}.shipto.size %]style='display:none'[%- END %]> |
|
29 |
<th align="right">[% 'Shipping Address' | $T8 %]</th> |
|
30 |
<td>[% L.select_tag('order.shipto_id', |
|
31 |
SELF.order.${SELF.cv}.shipto, |
|
32 |
default=SELF.order.shipto_id, |
|
33 |
title_key='displayable_id', |
|
34 |
value_key='shipto_id', |
|
35 |
with_empty=1, |
|
36 |
style='width: 300px') %]</td> |
|
37 |
</tr> |
|
38 |
|
|
39 |
<tr> |
|
40 |
<th align="right">[% 'Steuersatz' | $T8 %]</th> |
|
41 |
<td>[% L.select_tag('order.taxzone_id', SELF.all_taxzones, default=SELF.order.taxzone_id, title_key='description', style='width: 300px') %]</td> |
|
42 |
</tr> |
|
43 |
|
|
44 |
<tr> |
|
45 |
<th align="right">[% 'Shipping Point' | $T8 %]</th> |
|
46 |
<td>[% L.input_tag('order.shippingpoint', SELF.order.shippingpoint, style='width: 300px') %]</td> |
|
47 |
</tr> |
|
48 |
|
|
49 |
<tr> |
|
50 |
<th align="right">[% 'Ship via' | $T8 %]</th> |
|
51 |
<td>[% L.input_tag('order.shipvia', SELF.order.shipvia, style='width: 300px') %]</td> |
|
52 |
</tr> |
|
53 |
|
|
54 |
<tr> |
|
55 |
<th align="right">[% 'Transaction description' | $T8 %]</th> |
|
56 |
<td>[% L.input_tag('order.transaction_description', SELF.order.transaction_description, style='width: 300px') %]</td> |
|
57 |
</tr> |
|
58 |
|
|
59 |
</table> |
|
60 |
</td> |
|
61 |
|
|
62 |
<td align="right"> |
|
63 |
<table> |
|
64 |
|
|
65 |
<tr> |
|
66 |
<td colspan="2" align="center"> |
|
67 |
[%- IF SELF.order.id %] |
|
68 |
<label for="order.delivered">[% 'Delivery Order(s) for full qty created' | $T8 %]</label> |
|
69 |
[% L.yes_no_tag('order.delivered', SELF.order.delivered) %] |
|
70 |
<label for="order.closed">[% 'Closed' | $T8 %]</label> |
|
71 |
[% L.yes_no_tag('order.closed', SELF.order.closed) %] |
|
72 |
[%- END %] |
|
73 |
</td> |
|
74 |
</tr> |
|
75 |
|
|
76 |
<tr> |
|
77 |
<th align="right">[% 'Employee' | $T8 %]</th> |
|
78 |
<td>[% L.select_tag('order.employee_id', |
|
79 |
SELF.all_employees, |
|
80 |
default=(SELF.order.employee_id ? SELF.order.employee_id : SELF.current_employee_id), |
|
81 |
title_key='safe_name') %]</td> |
|
82 |
</tr> |
|
83 |
|
|
84 |
[% IF SELF.cv == 'customer' %] |
|
85 |
<tr> |
|
86 |
<th align="right">[% 'Salesman' | $T8 %]</th> |
|
87 |
<td>[% L.select_tag('order.salesman_id', |
|
88 |
SELF.all_salesmen, |
|
89 |
default=(SELF.order.salesman_id ? SELF.order.salesman_id : SELF.current_employee_id), |
|
90 |
title_key='safe_name') %]</td> |
|
91 |
</tr> |
|
92 |
[% END %] |
|
93 |
|
|
94 |
<tr> |
|
95 |
<th width="70%" align="right" nowrap>[% 'Order Number' | $T8 %]</th> |
|
96 |
<td>[% L.input_tag('order.ordnumber', SELF.order.ordnumber, size = 11) %]</td> |
|
97 |
</tr> |
|
98 |
|
|
99 |
<tr> |
|
100 |
<th width="70%" align="right" nowrap>[% 'Customer Order Number' | $T8 %]</th> |
|
101 |
<td>[% L.input_tag('order.cusordnumber', SELF.order.cusordnumber, size = 11) %]</td> |
|
102 |
</tr> |
|
103 |
|
|
104 |
<tr> |
|
105 |
<th width="70%" align="right" nowrap>[% 'Order Date' | $T8 %]</th> |
|
106 |
<td>[% L.date_tag('order.transdate', SELF.order.transdate) %]</td> |
|
107 |
</tr> |
|
108 |
|
|
109 |
<tr> |
|
110 |
<th width="70%" align="right" nowrap>[% 'Project Number' | $T8 %]</th> |
|
111 |
<td>[%- L.select_tag('order.globalproject_id', SELF.all_projects, default=SELF.order.globalproject_id, title_key='projectnumber', with_empty = 1) %]</td> |
|
112 |
</tr> |
|
113 |
|
|
114 |
</table> |
|
115 |
|
|
116 |
</td> |
|
117 |
</tr> |
|
118 |
</table> |
|
119 |
|
|
120 |
[%- PROCESS order/tabs/_item_input.html %] |
|
121 |
|
|
122 |
<table width="100%"> |
|
123 |
<tr> |
|
124 |
<td> |
|
125 |
|
|
126 |
<div id="row_table_scroll_id" style="overflow-y: auto; height: 25vh"> |
|
127 |
<table id="row_table_id" width="100%"> |
|
128 |
<thead> |
|
129 |
<tr class="listheading"> |
|
130 |
<th class="listheading" style='display:none'></th> |
|
131 |
<th class="listheading" style='text-align:center' nowrap width="1"><img src="image/updown.png" alt="[%- LxERP.t8('reorder item') %]"></th> |
|
132 |
<th class="listheading" style='text-align:center' nowrap width="1"><img src="image/close.png" alt="[%- LxERP.t8('delete item') %]"></th> |
|
133 |
<th class="listheading" nowrap width="15">[%- 'Partnumber' | $T8 %] </th> |
|
134 |
<th class="listheading" nowrap >[%- 'Description' | $T8 %] </th> |
|
135 |
<th class="listheading" nowrap width="5" >[%- 'Qty' | $T8 %] </th> |
|
136 |
<th class="listheading" nowrap width="5" >[%- 'Price Factor' | $T8 %] </th> |
|
137 |
<th class="listheading" nowrap width="5" >[%- 'Unit' | $T8 %] </th> |
|
138 |
<th class="listheading" nowrap width="15">[%- 'Price' | $T8 %] </th> |
|
139 |
<th class="listheading" nowrap width="5" >[%- 'Discount' | $T8 %] </th> |
|
140 |
<th class="listheading" nowrap width="10">[%- 'Extended' | $T8 %] </th> |
|
141 |
</tr> |
|
142 |
</thead> |
|
143 |
|
|
144 |
[%- FOREACH item = SELF.order.items_sorted %] |
|
145 |
[%- PROCESS order/tabs/_row.html ITEM=item %] |
|
146 |
[%- END %] |
|
147 |
|
|
148 |
</table> |
|
149 |
</div> |
|
150 |
|
|
151 |
</td> |
|
152 |
</tr> |
|
153 |
|
|
154 |
<tr> |
|
155 |
</tr> |
|
156 |
|
|
157 |
<tr> |
|
158 |
<td align="right"> |
|
159 |
<table> |
|
160 |
[%- IF NOT taxincluded %] |
|
161 |
<tr> |
|
162 |
<th align="right">[%- 'Subtotal' | $T8 %]</th> |
|
163 |
<td align="right"> |
|
164 |
[%- L.div_tag(SELF.order.netamount_as_number, id='netamount_id') %] |
|
165 |
</td> |
|
166 |
</tr> |
|
167 |
[%- END %] |
|
168 |
[%- FOREACH tax = SELF.taxes %] |
|
169 |
[%- PROCESS order/tabs/_tax_row.html TAX=tax %] |
|
170 |
[%- END %] |
|
171 |
<tr id="amount_row_id"> |
|
172 |
<th align="right">[%- 'Total' | $T8 %]</th> |
|
173 |
<td align="right"> |
|
174 |
<table> |
|
175 |
[%- IF NOT taxincluded %] |
|
176 |
<tr> |
|
177 |
<th align="right">[%- 'Subtotal' | $T8 %]</th> |
|
178 |
<td align="right"> |
|
179 |
[%- L.div_tag(SELF.order.netamount_as_number, id='netamount_id') %] |
|
180 |
</td> |
|
181 |
</tr> |
|
182 |
[%- END %] |
|
183 |
[%- FOREACH tax = SELF.taxes %] |
|
184 |
[%- PROCESS order/tabs/_tax_row.html TAX=tax %] |
|
185 |
[%- END %] |
|
186 |
<tr id="amount_row_id"> |
|
187 |
<th align="right">[%- 'Total' | $T8 %]</th> |
|
188 |
<td align="right"> |
|
189 |
[%- L.div_tag(SELF.order.amount_as_number, id='amount_id') %] |
|
190 |
</td> |
|
191 |
</tr> |
|
192 |
</table> |
|
193 |
</td> |
|
194 |
|
|
195 |
</tr> |
|
196 |
</table> |
|
197 |
</td> |
|
198 |
</tr> |
|
199 |
|
|
200 |
</table> |
|
201 |
|
|
202 |
</div> |
|
203 |
|
|
204 |
|
|
205 |
[% L.sortable_element('#row_table_id') %] |
|
206 |
|
|
207 |
<script type='text/javascript'> |
|
208 |
function reload_cv_dependend_selections() { |
|
209 |
$.post("controller.pl", { 'action': 'Order/customer_vendor_changed', |
|
210 |
'cv_id': function(){ return $('#order_[%- cv_id%]').val() }, |
|
211 |
'type': function(){ return $('#type').val() }, |
|
212 |
}, kivi.eval_json_result); |
|
213 |
} |
|
214 |
|
|
215 |
function add_item() { |
|
216 |
if ($('#add_item_parts_id').val() == '') return; |
|
217 |
if (!check_cv()) return; |
|
218 |
|
|
219 |
var data = $('#order_form').serialize(); |
|
220 |
data += '&action=Order/add_item'; |
|
221 |
|
|
222 |
$.post("controller.pl", data, kivi.eval_json_result); |
|
223 |
} |
|
224 |
|
|
225 |
function delete_order_item_row(clicked) { |
|
226 |
var row = $(clicked).parents("tbody").first(); |
|
227 |
$(row).remove(); |
|
228 |
|
|
229 |
recalc_amounts_and_taxes(); |
|
230 |
} |
|
231 |
|
|
232 |
function recalc_amounts_and_taxes() { |
|
233 |
var data = $('#order_form').serialize(); |
|
234 |
data += '&action=Order/recalc_amounts_and_taxes'; |
|
235 |
|
|
236 |
$.post("controller.pl", data, kivi.eval_json_result); |
|
237 |
} |
|
238 |
|
|
239 |
function redisplay_linetotals(data) { |
|
240 |
$('.row_entry [name="linetotal"]').each(function(idx, elt) { |
|
241 |
$(elt).html(data[idx]); |
|
242 |
}); |
|
243 |
} |
|
244 |
|
|
245 |
function row_table_scroll_down() { |
|
246 |
$('#row_table_scroll_id').scrollTop($('#row_table_scroll_id')[0].scrollHeight); |
|
247 |
} |
|
248 |
|
|
249 |
function row_set_keyboard_events_by_id(item_id) { |
|
250 |
var row = $('#item_' + item_id).parents("tbody").first(); |
|
251 |
|
|
252 |
row_set_keyboard_events(row); |
|
253 |
} |
|
254 |
|
|
255 |
function row_set_keyboard_events(rows) { |
|
256 |
$(rows).keydown(function(event) { |
|
257 |
if(event.keyCode == 40 && event.shiftKey == true) { |
|
258 |
// shift arrow down |
|
259 |
event.preventDefault(); |
|
260 |
var row = $(event.target).parents(".row_entry").first(); |
|
261 |
$(row).children().not(':first').show(); |
|
262 |
return false; |
|
263 |
} |
|
264 |
if(event.keyCode == 38 && event.shiftKey == true) { |
|
265 |
// shift arrow up |
|
266 |
event.preventDefault(); |
|
267 |
var row = $(event.target).parents(".row_entry").first(); |
|
268 |
$(row).children().not(':first').hide(); |
|
269 |
return false; |
|
270 |
} |
|
271 |
}); |
|
272 |
|
|
273 |
$(rows).dblclick(function(event) { |
|
274 |
event.preventDefault(); |
|
275 |
var row = $(event.target).parents(".row_entry").first(); |
|
276 |
$(row).children().not(':first').toggle(); |
|
277 |
return false; |
|
278 |
}); |
|
279 |
} |
|
280 |
|
|
281 |
$(function(){ |
|
282 |
$('#order_[%- cv_id %]').change(reload_cv_dependend_selections); |
|
283 |
$('#add_item_parts_id').on('set_item:PartPicker', function(e,o) { $('#add_item_sellprice_as_number').val(kivi.format_amount(o.sellprice, -2)) }); |
|
284 |
$('#add_item_parts_id').on('set_item:PartPicker', function(e,o) { $('#add_item_description').val(o.description) }); |
|
285 |
$('.add_item_input').keydown(function(event) { |
|
286 |
if(event.keyCode == 13) { |
|
287 |
event.preventDefault(); |
|
288 |
add_item(); |
|
289 |
return false; |
|
290 |
} |
|
291 |
}); |
|
292 |
row_set_keyboard_events($('.row_entry')); |
|
293 |
$('.recalc').change(recalc_amounts_and_taxes); |
|
294 |
}); |
|
295 |
|
|
296 |
</script> |
Auch abrufbar als: Unified diff
Auftrags-Controller: neue Eingabemakse für Aufträge basierend auf Controller