Revision cbb1668f
Von Bernd Bleßmann vor mehr als 3 Jahren hinzugefügt
- ID cbb1668fb815449b20be91641debf16e94fb6421
- Vorgänger 47cbf8da
SL/Controller/TimeRecording.pm | ||
---|---|---|
5 | 5 |
|
6 | 6 |
use DateTime; |
7 | 7 |
use English qw(-no_match_vars); |
8 |
use List::Util qw(sum0); |
|
8 |
use List::Util qw(sum0 notall);
|
|
9 | 9 |
use POSIX qw(strftime); |
10 | 10 |
|
11 | 11 |
use SL::Controller::Helper::GetModels; |
... | ... | |
18 | 18 |
use SL::DB::Project; |
19 | 19 |
use SL::DB::TimeRecording; |
20 | 20 |
use SL::DB::TimeRecordingArticle; |
21 |
use SL::Helper::Flash qw(flash); |
|
21 |
use SL::Helper::Flash qw(flash flash_later);
|
|
22 | 22 |
use SL::Helper::Number qw(_round_number _parse_number _round_total); |
23 | 23 |
use SL::Helper::UserPreferences::TimeRecording; |
24 | 24 |
use SL::Locale::String qw(t8); |
... | ... | |
35 | 35 |
# safety |
36 | 36 |
__PACKAGE__->run_before('check_auth'); |
37 | 37 |
__PACKAGE__->run_before('check_auth_edit', only => [ qw(edit save delete) ]); |
38 |
__PACKAGE__->run_before('check_auth_edit_all', only => [ qw(mark_as_booked) ]); |
|
38 |
__PACKAGE__->run_before('check_auth_edit_all', only => [ qw(mark_as_booked assign_order_dialog assign_order) ]);
|
|
39 | 39 |
|
40 | 40 |
|
41 | 41 |
my %sort_columns = ( |
... | ... | |
99 | 99 |
$self->redirect_to(safe_callback()); |
100 | 100 |
} |
101 | 101 |
|
102 |
sub action_assign_order_dialog { |
|
103 |
my ($self) = @_; |
|
104 |
|
|
105 |
if (!scalar @{ $::form->{ids} }) { |
|
106 |
return $self->js->flash('warning', t8('No entries have been selected.'))->render; |
|
107 |
} |
|
108 |
|
|
109 |
# Sanity check: all entries must have the same customer assigned. |
|
110 |
my $trs = SL::DB::Manager::TimeRecording->get_all(where => [id => $::form->{ids}]); |
|
111 |
if (notall { $_->customer_id == $trs->[0]->customer_id } @$trs) { |
|
112 |
return $self->js->flash('error', t8('All entries must have the same customer assigned.'))->render; |
|
113 |
}; |
|
114 |
|
|
115 |
# Search assigneable orders |
|
116 |
my $orders = SL::DB::Manager::Order->get_all(query => [or => [ closed => 0, closed => undef ], |
|
117 |
or => [ quotation => 0, quotation => undef ], |
|
118 |
customer_id => $trs->[0]->customer_id]); |
|
119 |
$orders = [ map { [$_->id, sprintf("%s %s", $_->number, $_->customervendor->name) ] } sort { $a->number <=> $b->number } @{$orders||[]} ]; |
|
120 |
|
|
121 |
# Prepare dialog |
|
122 |
my $dialog_html = $self->render('time_recording/_assign_order_dialog', { output => 0 }, |
|
123 |
orders => $orders, |
|
124 |
time_recordings => $trs, |
|
125 |
callback => $::form->escape($::form->{callback}), |
|
126 |
); |
|
127 |
|
|
128 |
my %dialog_params = ( |
|
129 |
html => $dialog_html, |
|
130 |
id => 'assign_order_dialog', |
|
131 |
dialog => { |
|
132 |
title => t8('Assign an order to entries'), |
|
133 |
width => 500, |
|
134 |
height => 600, |
|
135 |
}, |
|
136 |
); |
|
137 |
|
|
138 |
$self->js |
|
139 |
->dialog->open(\%dialog_params) |
|
140 |
->render; |
|
141 |
} |
|
142 |
|
|
143 |
sub action_assign_order { |
|
144 |
my ($self) = @_; |
|
145 |
|
|
146 |
if (!$::form->{assign}->{order_id}) { |
|
147 |
return $self->js->flash('warning', t8('No order has been selected.'))->render; |
|
148 |
} |
|
149 |
|
|
150 |
if (!scalar @{ $::form->{ids} }) { |
|
151 |
return $self->js->flash('warning', t8('No entries have been selected.'))->render; |
|
152 |
} |
|
153 |
|
|
154 |
# Sanity check: all entries must have the same customer assigned. |
|
155 |
my $trs = SL::DB::Manager::TimeRecording->get_all(where => [id => $::form->{ids}]); |
|
156 |
if (notall { $_->customer_id == $trs->[0]->customer_id } @$trs) { |
|
157 |
return $self->js->flash('error', t8('All entries must have the same customer assigned.'))->render; |
|
158 |
}; |
|
159 |
|
|
160 |
my $errors_occurred; |
|
161 |
foreach my $tr (@$trs) { |
|
162 |
$tr->assign_attributes(order_id => $::form->{assign}->{order_id}); |
|
163 |
my @errors = $tr->validate; |
|
164 |
if (@errors) { |
|
165 |
$self->js->flash('error', $tr->displayable_times . ': ' . $_) for @errors; |
|
166 |
$errors_occurred = 1; |
|
167 |
} |
|
168 |
} |
|
169 |
|
|
170 |
if ($errors_occurred) { |
|
171 |
return $self->js->flash('warning', t8('No changes were saved.'))->render; |
|
172 |
|
|
173 |
} else { |
|
174 |
$_->save for @$trs; |
|
175 |
flash_later('info', t8('The changes have been saved.')); |
|
176 |
} |
|
177 |
|
|
178 |
$self->redirect_to($::form->unescape($::form->{assign}->{callback})); |
|
179 |
} |
|
180 |
|
|
102 | 181 |
sub action_edit { |
103 | 182 |
my ($self) = @_; |
104 | 183 |
|
... | ... | |
415 | 494 |
confirm => $::locale->text('Do you really want to mark the selected entries as booked?'), |
416 | 495 |
only_if => $self->can_edit_all, |
417 | 496 |
], |
497 |
action => [ |
|
498 |
t8('Assign Order'), |
|
499 |
call => [ 'kivi.TimeRecording.assign_order_dialog', { callback => $self->models->get_callback } ], |
|
500 |
checks => [ [ 'kivi.check_if_entries_selected', '[name="ids[]"]' ] ], |
|
501 |
only_if => $self->can_edit_all, |
|
502 |
], |
|
418 | 503 |
], |
419 | 504 |
action => [ |
420 | 505 |
t8('Add'), |
js/kivi.TimeRecording.js | ||
---|---|---|
80 | 80 |
}); |
81 | 81 |
}; |
82 | 82 |
|
83 |
ns.assign_order_dialog = function(params) { |
|
84 |
var callback = params.callback; |
|
85 |
|
|
86 |
var data = $('#form').serializeArray(); |
|
87 |
data = data.concat($('#filter_form').serializeArray()); |
|
88 |
data.push({name: 'action', value: 'TimeRecording/assign_order_dialog'}, |
|
89 |
{name: 'callback', value: callback}); |
|
90 |
|
|
91 |
$.post("controller.pl", data, kivi.eval_json_result); |
|
92 |
}; |
|
93 |
|
|
94 |
ns.assign_order = function() { |
|
95 |
var data = $('#form').serializeArray(); |
|
96 |
data = data.concat($('#assign_order_form').serializeArray()); |
|
97 |
data.push({name: 'action', value: 'TimeRecording/assign_order'}); |
|
98 |
|
|
99 |
$('#assign_order_dialog').dialog('close'); |
|
100 |
|
|
101 |
$.post("controller.pl", data, kivi.eval_json_result); |
|
102 |
}; |
|
103 |
|
|
83 | 104 |
}); |
84 | 105 |
|
85 | 106 |
$(function() { |
locale/de/all | ||
---|---|---|
269 | 269 |
'All changes in that file have been reverted.' => 'Alle Änderungen in dieser Datei wurden rückgängig gemacht.', |
270 | 270 |
'All clients' => 'Alle Mandanten', |
271 | 271 |
'All employees' => 'Alle Angestellten', |
272 |
'All entries must have the same customer assigned.' => 'Alle Einträge müssen den selben Kunden zugeordnet haben.', |
|
272 | 273 |
'All general ledger entries' => 'Alle Hauptbucheinträge', |
273 | 274 |
'All groups' => 'Alle Gruppen', |
274 | 275 |
'All modules' => 'Alle Module', |
... | ... | |
353 | 354 |
'Asset' => 'Aktiva/Mittelverwendung', |
354 | 355 |
'Assets' => 'Aktiva', |
355 | 356 |
'Assign' => 'Übernehmen', |
357 |
'Assign Order' => 'Auftrag zuordnen', |
|
358 |
'Assign an order to entries' => 'Auftrag den Einträgen zuordnen', |
|
356 | 359 |
'Assign article' => 'Artikel zuweisen', |
357 | 360 |
'Assign invoice' => 'Rechnung zuweisen', |
358 | 361 |
'Assign the following article to all sections' => 'Den folgenden Artikel allen Abschnitten zuweisen', |
... | ... | |
2118 | 2121 |
'No bins have been added to this warehouse yet.' => 'Es wurden zu diesem Lager noch keine Lagerplätze angelegt.', |
2119 | 2122 |
'No carry-over chart configured!' => 'Kein Saldenvortragskonto konfiguriert!', |
2120 | 2123 |
'No changes since previous version.' => 'Keine Änderungen seit der letzten Version.', |
2124 |
'No changes were saved.' => 'Es wurden keine Änderungen gespeichert.', |
|
2121 | 2125 |
'No clients have been created yet.' => 'Es wurden noch keine Mandanten angelegt.', |
2122 | 2126 |
'No contact selected to delete' => 'Keine Ansprechperson zum Löschen ausgewählt', |
2123 | 2127 |
'No contra account selected!' => 'Kein Gegenkonto ausgewählt!', |
... | ... | |
2146 | 2150 |
'No groups have been created yet.' => 'Es wurden noch keine Gruppen angelegt.', |
2147 | 2151 |
'No internal phone extensions have been configured yet.' => 'Es wurden noch keine internen Durchwahlen konfiguriert.', |
2148 | 2152 |
'No invoices have been selected.' => 'Es wurden keine Rechnungen ausgewählt.', |
2153 |
'No order has been selected.' => 'Es wurde kein Auftrag ausgewählt', |
|
2149 | 2154 |
'No part was selected.' => 'Es wurde kein Artikel ausgewählt', |
2150 | 2155 |
'No payment term has been created yet.' => 'Es wurden noch keine Zahlungsbedingungen angelegt.', |
2151 | 2156 |
'No picture has been uploaded' => 'Es wurde kein Bild hochgeladen', |
... | ... | |
2939 | 2944 |
'Select Mulit-Item Options' => 'Multi-Treffer Auswahlliste', |
2940 | 2945 |
'Select a Customer' => 'Endkunde auswählen', |
2941 | 2946 |
'Select a period' => 'Bitte Zeitraum auswählen', |
2947 |
'Select an order to assign' => 'Wählen Sie einen Auftrag zum Zuordnen', |
|
2942 | 2948 |
'Select federal state...' => 'Bundesland auswählen...', |
2943 | 2949 |
'Select file to upload' => 'Datei zum Hochladen auswählen', |
2944 | 2950 |
'Select from one of the items below' => 'Wählen Sie einen der untenstehenden Einträge', |
... | ... | |
2950 | 2956 |
'Select type of transfer in' => 'Grund der Einlagerung auswählen:', |
2951 | 2957 |
'Selected' => 'Ausgewählt', |
2952 | 2958 |
'Selected identity fields' => 'Ausgewählte Felder', |
2959 |
'Selected time recordings' => 'Ausgewählte Zeiterfassungs-Einträge', |
|
2953 | 2960 |
'Selection' => 'Auswahlbox', |
2954 | 2961 |
'Selection fields: The option field must contain the available options for the selection. Options are separated by \'##\', for example \'Early##Normal##Late\'.' => 'Auswahlboxen: Das Optionenfeld muss die für die Auswahl verfügbaren Einträge enthalten. Die Einträge werden mit \'##\' voneinander getrennt. Beispiel: \'Früh##Normal##Spät\'.', |
2955 | 2962 |
'Sell Price' => 'Verkaufspreis', |
... | ... | |
3381 | 3388 |
'The booking group needs an inventory account.' => 'Die Buchungsgruppe braucht ein Warenbestandskonto.', |
3382 | 3389 |
'The buchungsgruppe is missing.' => 'Die Buchungsgruppe fehlt.', |
3383 | 3390 |
'The categories has been saved.' => 'Artikelgruppe gespeichert', |
3391 |
'The changes have been saved.' => 'Die Änderungen wurden gespeichert.', |
|
3384 | 3392 |
'The changing of tax-o-matic account is NOT recommended, but if you do so please also (re)configure booking groups and reconfigure ALL charts which point to this tax-o-matic account. ' => 'Es wird nicht empfohlen Steuerkonten (Umsatzsteuer oder Vorsteuer) "umzuhängen", aber falls es gemacht wird, bitte auch entsprechend konsequent die Buchungsgruppen und die Konten die mit dieser Steuer verknüpft sind umkonfigurieren.', |
3385 | 3393 |
'The chart is not valid.' => 'Das Konto ist nicht gültig.', |
3386 | 3394 |
'The client could not be deleted.' => 'Der Mandant konnte nicht gelöscht werden.', |
... | ... | |
4242 | 4250 |
'correction' => 'Korrektur', |
4243 | 4251 |
'correction_br' => 'Korr.', |
4244 | 4252 |
'cp_greeting to cp_gender migration' => 'Datenumwandlung von Titel nach Geschlecht (cp_greeting to cp_gender)', |
4253 |
'current order' => 'aktueller Auftrag', |
|
4245 | 4254 |
'customer_list' => 'kundenliste', |
4246 | 4255 |
'dated' => 'datiert', |
4247 | 4256 |
'delete' => 'Löschen', |
... | ... | |
4447 | 4456 |
'taxnumber' => 'Automatikkonto', |
4448 | 4457 |
'terminated' => 'gekündigt', |
4449 | 4458 |
'time and effort based position' => 'Aufwandsposition', |
4459 |
'time recording entry' => 'Zeiterfassungs-Eintrag', |
|
4450 | 4460 |
'time_recordings' => 'zeiterfassung', |
4451 | 4461 |
'to' => 'bis', |
4452 | 4462 |
'to (date)' => 'bis', |
locale/en/all | ||
---|---|---|
269 | 269 |
'All changes in that file have been reverted.' => '', |
270 | 270 |
'All clients' => '', |
271 | 271 |
'All employees' => '', |
272 |
'All entries must have the same customer assigned.' => '', |
|
272 | 273 |
'All general ledger entries' => '', |
273 | 274 |
'All groups' => '', |
274 | 275 |
'All modules' => '', |
... | ... | |
353 | 354 |
'Asset' => '', |
354 | 355 |
'Assets' => '', |
355 | 356 |
'Assign' => '', |
357 |
'Assign Order' => '', |
|
358 |
'Assign an order to entries' => '', |
|
356 | 359 |
'Assign article' => '', |
357 | 360 |
'Assign invoice' => '', |
358 | 361 |
'Assign the following article to all sections' => '', |
... | ... | |
2118 | 2121 |
'No bins have been added to this warehouse yet.' => '', |
2119 | 2122 |
'No carry-over chart configured!' => '', |
2120 | 2123 |
'No changes since previous version.' => '', |
2124 |
'No changes were saved.' => '', |
|
2121 | 2125 |
'No clients have been created yet.' => '', |
2122 | 2126 |
'No contact selected to delete' => '', |
2123 | 2127 |
'No contra account selected!' => '', |
... | ... | |
2146 | 2150 |
'No groups have been created yet.' => '', |
2147 | 2151 |
'No internal phone extensions have been configured yet.' => '', |
2148 | 2152 |
'No invoices have been selected.' => '', |
2153 |
'No order has been selected.' => '', |
|
2149 | 2154 |
'No part was selected.' => '', |
2150 | 2155 |
'No payment term has been created yet.' => '', |
2151 | 2156 |
'No picture has been uploaded' => '', |
... | ... | |
2939 | 2944 |
'Select Mulit-Item Options' => '', |
2940 | 2945 |
'Select a Customer' => '', |
2941 | 2946 |
'Select a period' => '', |
2947 |
'Select an order to assign' => '', |
|
2942 | 2948 |
'Select federal state...' => '', |
2943 | 2949 |
'Select file to upload' => '', |
2944 | 2950 |
'Select from one of the items below' => '', |
... | ... | |
2950 | 2956 |
'Select type of transfer in' => '', |
2951 | 2957 |
'Selected' => '', |
2952 | 2958 |
'Selected identity fields' => '', |
2959 |
'Selected time recordings' => '', |
|
2953 | 2960 |
'Selection' => '', |
2954 | 2961 |
'Selection fields: The option field must contain the available options for the selection. Options are separated by \'##\', for example \'Early##Normal##Late\'.' => '', |
2955 | 2962 |
'Sell Price' => '', |
... | ... | |
3380 | 3387 |
'The booking group needs an inventory account.' => '', |
3381 | 3388 |
'The buchungsgruppe is missing.' => '', |
3382 | 3389 |
'The categories has been saved.' => '', |
3390 |
'The changes have been saved.' => '', |
|
3383 | 3391 |
'The changing of tax-o-matic account is NOT recommended, but if you do so please also (re)configure booking groups and reconfigure ALL charts which point to this tax-o-matic account. ' => '', |
3384 | 3392 |
'The chart is not valid.' => '', |
3385 | 3393 |
'The client could not be deleted.' => '', |
... | ... | |
4241 | 4249 |
'correction' => '', |
4242 | 4250 |
'correction_br' => 'correction', |
4243 | 4251 |
'cp_greeting to cp_gender migration' => '', |
4252 |
'current order' => '', |
|
4244 | 4253 |
'customer_list' => '', |
4245 | 4254 |
'dated' => '', |
4246 | 4255 |
'delete' => '', |
... | ... | |
4446 | 4455 |
'taxnumber' => '', |
4447 | 4456 |
'terminated' => '', |
4448 | 4457 |
'time and effort based position' => '', |
4458 |
'time recording entry' => '', |
|
4449 | 4459 |
'time_recordings' => '', |
4450 | 4460 |
'to' => '', |
4451 | 4461 |
'to (date)' => '', |
templates/webpages/time_recording/_assign_order_dialog.html | ||
---|---|---|
1 |
[%- USE T8 %][%- USE HTML %][%- USE L %][%- USE P %][%- USE LxERP %] |
|
2 |
|
|
3 |
<form method="POST" id="assign_order_form"> |
|
4 |
[% L.hidden_tag('assign.callback', HTML.escape(callback)) %] |
|
5 |
|
|
6 |
<p> |
|
7 |
<h3>[% 'Selected time recordings' | $T8 %]</h3> |
|
8 |
<table> |
|
9 |
<tr class="listheading"> |
|
10 |
<th>[% 'time recording entry' | $T8 %]</th> |
|
11 |
<th>[% 'current order' | $T8 %]</th> |
|
12 |
</tr> |
|
13 |
[% FOREACH tr = time_recordings %] |
|
14 |
<tr class="listrow"> |
|
15 |
<td>[% tr.displayable_times %]</td> |
|
16 |
<td align="right">[% tr.order_id ? tr.order.number : '-' %]</td> |
|
17 |
</tr> |
|
18 |
[% END %] |
|
19 |
</table> |
|
20 |
</p> |
|
21 |
|
|
22 |
<p> |
|
23 |
<h3>[% 'Select an order to assign' | $T8 %]</h3> |
|
24 |
[% P.select_tag('assign.order_id', orders, with_empty=1, style='width: 300px') %] |
|
25 |
</p> |
|
26 |
|
|
27 |
<p> |
|
28 |
[% L.button_tag("kivi.TimeRecording.assign_order()", LxERP.t8('Continue')) %] |
|
29 |
</p> |
|
30 |
</form> |
Auch abrufbar als: Unified diff
Zeiterfassung: Im Bericht Aufträge zuordnen können