Revision dbba5f4d
Von Bernd Bleßmann vor mehr als 3 Jahren hinzugefügt
SL/AM.pm | ||
---|---|---|
54 | 54 |
use SL::GenericTranslations; |
55 | 55 |
use SL::Helper::UserPreferences::PositionsScrollbar; |
56 | 56 |
use SL::Helper::UserPreferences::PartPickerSearch; |
57 |
use SL::Helper::UserPreferences::TimeRecording; |
|
57 | 58 |
use SL::Helper::UserPreferences::UpdatePositions; |
58 | 59 |
|
59 | 60 |
use strict; |
... | ... | |
546 | 547 |
SL::Helper::UserPreferences::UpdatePositions->new()->get_show_update_button(); |
547 | 548 |
} |
548 | 549 |
|
550 |
sub time_recording_use_duration { |
|
551 |
SL::Helper::UserPreferences::TimeRecording->new()->get_use_duration(); |
|
552 |
} |
|
553 |
|
|
549 | 554 |
sub save_preferences { |
550 | 555 |
$main::lxdebug->enter_sub(); |
551 | 556 |
|
... | ... | |
583 | 588 |
if (exists $form->{positions_show_update_button}) { |
584 | 589 |
SL::Helper::UserPreferences::UpdatePositions->new()->store_show_update_button($form->{positions_show_update_button}) |
585 | 590 |
} |
591 |
if (exists $form->{time_recording_use_duration}) { |
|
592 |
SL::Helper::UserPreferences::TimeRecording->new()->store_use_duration($form->{time_recording_use_duration}) |
|
593 |
} |
|
586 | 594 |
|
587 | 595 |
$main::lxdebug->leave_sub(); |
588 | 596 |
|
SL/Controller/TimeRecording.pm | ||
---|---|---|
14 | 14 |
use SL::DB::Part; |
15 | 15 |
use SL::DB::TimeRecording; |
16 | 16 |
use SL::DB::TimeRecordingArticle; |
17 |
use SL::Helper::Flash qw(flash); |
|
18 |
use SL::Helper::UserPreferences::TimeRecording; |
|
17 | 19 |
use SL::Locale::String qw(t8); |
18 | 20 |
use SL::ReportGenerator; |
19 | 21 |
|
20 | 22 |
use Rose::Object::MakeMethods::Generic |
21 | 23 |
( |
22 | 24 |
# scalar => [ qw() ], |
23 |
'scalar --get_set_init' => [ qw(time_recording models all_employees all_time_recording_articles can_view_all can_edit_all) ], |
|
25 |
'scalar --get_set_init' => [ qw(time_recording models all_employees all_time_recording_articles can_view_all can_edit_all use_duration) ],
|
|
24 | 26 |
); |
25 | 27 |
|
26 | 28 |
|
... | ... | |
65 | 67 |
|
66 | 68 |
$::request->{layout}->use_javascript("${_}.js") for qw(kivi.TimeRecording ckeditor/ckeditor ckeditor/adapters/jquery kivi.Validator); |
67 | 69 |
|
70 |
if ($self->use_duration) { |
|
71 |
flash('warning', t8('This entry is using start and end time. This information will be overwritten on saving.')) if !$self->time_recording->is_duration_used; |
|
72 |
} else { |
|
73 |
flash('warning', t8('This entry is using date and duration. This information will be overwritten on saving.')) if $self->time_recording->is_duration_used; |
|
74 |
} |
|
75 |
|
|
68 | 76 |
if ($self->time_recording->start_time) { |
69 | 77 |
$self->{start_date} = $self->time_recording->start_time->to_kivitendo; |
70 | 78 |
$self->{start_time} = $self->time_recording->start_time->to_kivitendo_time; |
... | ... | |
84 | 92 |
sub action_save { |
85 | 93 |
my ($self) = @_; |
86 | 94 |
|
95 |
if ($self->use_duration) { |
|
96 |
$self->time_recording->start_date(undef); |
|
97 |
$self->time_recording->end_date(undef); |
|
98 |
} |
|
99 |
|
|
87 | 100 |
my @errors = $self->time_recording->validate; |
88 | 101 |
if (@errors) { |
89 | 102 |
$::form->error(t8('Saving the time recording entry failed: #1', join '<br>', @errors)); |
... | ... | |
107 | 120 |
} |
108 | 121 |
|
109 | 122 |
sub init_time_recording { |
123 |
my ($self) = @_; |
|
124 |
|
|
110 | 125 |
my $is_new = !$::form->{id}; |
111 |
my $time_recording = $is_new ? SL::DB::TimeRecording->new(start_time => DateTime->now_local) |
|
112 |
: SL::DB::TimeRecording->new(id => $::form->{id})->load; |
|
126 |
my $time_recording = !$is_new ? SL::DB::TimeRecording->new(id => $::form->{id})->load |
|
127 |
: $self->use_duration ? SL::DB::TimeRecording->new(date => DateTime->today_local) |
|
128 |
: SL::DB::TimeRecording->new(start_time => DateTime->now_local); |
|
113 | 129 |
|
114 | 130 |
my %attributes = %{ $::form->{time_recording} || {} }; |
115 | 131 |
|
116 |
foreach my $type (qw(start end)) { |
|
117 |
if ($::form->{$type . '_date'}) { |
|
118 |
my $date = DateTime->from_kivitendo($::form->{$type . '_date'}); |
|
119 |
$attributes{$type . '_time'} = $date->clone; |
|
120 |
if ($::form->{$type . '_time'}) { |
|
121 |
my ($hour, $min) = split ':', $::form->{$type . '_time'}; |
|
122 |
$attributes{$type . '_time'}->set_hour($hour) if $hour; |
|
123 |
$attributes{$type . '_time'}->set_minute($min) if $min; |
|
132 |
if (!$self->use_duration) { |
|
133 |
foreach my $type (qw(start end)) { |
|
134 |
if ($::form->{$type . '_date'}) { |
|
135 |
my $date = DateTime->from_kivitendo($::form->{$type . '_date'}); |
|
136 |
$attributes{$type . '_time'} = $date->clone; |
|
137 |
if ($::form->{$type . '_time'}) { |
|
138 |
my ($hour, $min) = split ':', $::form->{$type . '_time'}; |
|
139 |
$attributes{$type . '_time'}->set_hour($hour) if $hour; |
|
140 |
$attributes{$type . '_time'}->set_minute($min) if $min; |
|
141 |
} |
|
124 | 142 |
} |
125 | 143 |
} |
126 | 144 |
} |
127 | 145 |
|
128 |
# do not overwright staff member if you do not have the right
|
|
146 |
# do not overwrite staff member if you do not have the right
|
|
129 | 147 |
delete $attributes{staff_member_id} if !$_[0]->can_edit_all; |
130 | 148 |
$attributes{staff_member_id} = SL::DB::Manager::Employee->current->id if $is_new; |
131 | 149 |
|
... | ... | |
178 | 196 |
return $res; |
179 | 197 |
} |
180 | 198 |
|
199 |
sub init_use_duration { |
|
200 |
return SL::Helper::UserPreferences::TimeRecording->new()->get_use_duration(); |
|
201 |
} |
|
202 |
|
|
181 | 203 |
sub check_auth { |
182 | 204 |
$::auth->assert('time_recording'); |
183 | 205 |
} |
... | ... | |
255 | 277 |
my $staff_member = $filter->{staff_member_id} ? SL::DB::Employee->new(id => $filter->{staff_member_id})->load->safe_name : ''; |
256 | 278 |
|
257 | 279 |
my @filters = ( |
258 |
[ $filter->{"start_time:date::ge"}, t8('From Start') ],
|
|
259 |
[ $filter->{"start_time:date::le"}, t8('To Start') ],
|
|
280 |
[ $filter->{"date:date::ge"}, t8('From Date') ],
|
|
281 |
[ $filter->{"date:date::le"}, t8('To Date') ],
|
|
260 | 282 |
[ $filter->{"customer"}->{"name:substr::ilike"}, t8('Customer') ], |
261 | 283 |
[ $filter->{"customer"}->{"customernumber:substr::ilike"}, t8('Customer Number') ], |
262 | 284 |
[ $staff_member, t8('Mitarbeiter') ], |
SL/DB/TimeRecording.pm | ||
---|---|---|
33 | 33 |
|
34 | 34 |
my @errors; |
35 | 35 |
|
36 |
push @errors, t8('Start time must not be empty.') if !$self->start_time; |
|
37 | 36 |
push @errors, t8('Customer must not be empty.') if !$self->customer_id; |
38 | 37 |
push @errors, t8('Staff member must not be empty.') if !$self->staff_member_id; |
39 | 38 |
push @errors, t8('Employee must not be empty.') if !$self->employee_id; |
... | ... | |
109 | 108 |
return; |
110 | 109 |
} |
111 | 110 |
|
111 |
sub is_duration_used { |
|
112 |
return !$_[0]->start_time; |
|
113 |
} |
|
114 |
|
|
112 | 115 |
sub displayable_times { |
113 | 116 |
my ($self) = @_; |
114 | 117 |
|
SL/Helper/UserPreferences/TimeRecording.pm | ||
---|---|---|
1 |
package SL::Helper::UserPreferences::TimeRecording; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent qw(Rose::Object); |
|
5 |
|
|
6 |
use Carp; |
|
7 |
use List::MoreUtils qw(none); |
|
8 |
|
|
9 |
use SL::Helper::UserPreferences; |
|
10 |
|
|
11 |
use Rose::Object::MakeMethods::Generic ( |
|
12 |
'scalar --get_set_init' => [ qw(user_prefs) ], |
|
13 |
); |
|
14 |
|
|
15 |
sub get_use_duration { |
|
16 |
!!$_[0]->user_prefs->get('use_duration'); |
|
17 |
} |
|
18 |
|
|
19 |
sub store_use_duration { |
|
20 |
$_[0]->user_prefs->store('use_duration', $_[1]); |
|
21 |
} |
|
22 |
|
|
23 |
sub init_user_prefs { |
|
24 |
SL::Helper::UserPreferences->new( |
|
25 |
namespace => $_[0]->namespace, |
|
26 |
) |
|
27 |
} |
|
28 |
|
|
29 |
# read only stuff |
|
30 |
sub namespace { 'TimeRecording' } |
|
31 |
sub version { 1 } |
|
32 |
|
|
33 |
1; |
|
34 |
|
|
35 |
__END__ |
|
36 |
|
|
37 |
=pod |
|
38 |
|
|
39 |
=encoding utf-8 |
|
40 |
|
|
41 |
=head1 NAME |
|
42 |
|
|
43 |
SL::Helper::UserPreferences::TimeRecording - preferences intended |
|
44 |
to store user settings for using the time recording functionality. |
|
45 |
|
|
46 |
=head1 SYNOPSIS |
|
47 |
|
|
48 |
use SL::Helper::UserPreferences::TimeRecording; |
|
49 |
my $prefs = SL::Helper::UserPreferences::TimeRecording->new(); |
|
50 |
|
|
51 |
$prefs->store_use_duration(1); |
|
52 |
my $value = $prefs->get_use_duration; |
|
53 |
|
|
54 |
=head1 DESCRIPTION |
|
55 |
|
|
56 |
This module manages storing the user's choise for settings for |
|
57 |
the time recording controller. |
|
58 |
For now it can be choosen if an entry is done by entering start and |
|
59 |
end time or a date and a duration. |
|
60 |
|
|
61 |
=head1 BUGS |
|
62 |
|
|
63 |
None yet :) |
|
64 |
|
|
65 |
=head1 AUTHOR |
|
66 |
|
|
67 |
Bernd Bleßmann E<lt>bernd@kivitendo-premium.deE<gt> |
|
68 |
|
|
69 |
=cut |
bin/mozilla/am.pl | ||
---|---|---|
664 | 664 |
$form->{purchase_search_makemodel} = AM->purchase_search_makemodel(); |
665 | 665 |
$form->{sales_search_customer_partnumber} = AM->sales_search_customer_partnumber(); |
666 | 666 |
$form->{positions_show_update_button} = AM->positions_show_update_button(); |
667 |
$form->{time_recording_use_duration} = AM->time_recording_use_duration(); |
|
667 | 668 |
|
668 | 669 |
$myconfig{show_form_details} = 1 unless (defined($myconfig{show_form_details})); |
669 | 670 |
$form->{CAN_CHANGE_PASSWORD} = $main::auth->can_change_password(); |
locale/de/all | ||
---|---|---|
1530 | 1530 |
'Fristsetzung' => 'Fristsetzung', |
1531 | 1531 |
'From' => 'Von', |
1532 | 1532 |
'From Date' => 'Von', |
1533 |
'From Start' => 'Ab Start', |
|
1534 | 1533 |
'From bin' => 'Ausgelagert', |
1535 | 1534 |
'From shop "#1" : #2 ' => 'Shop #1 : #2', |
1536 | 1535 |
'From shop #1 : #2 shoporders have been fetched.' => 'Es wurden #2 Bestellungen von #1 geholt.', |
... | ... | |
3105 | 3104 |
'Start the correction assistant' => 'Korrekturassistenten starten', |
3106 | 3105 |
'Start time' => 'Startzeit', |
3107 | 3106 |
'Start time must be earlier than end time.' => 'Startzeit muss vor der Endzeit liegen.', |
3108 |
'Start time must not be empty.' => 'Startzeit darf nicht leer sein.', |
|
3109 | 3107 |
'Startdate method' => 'Methode zur Ermittlung des Startdatums', |
3110 | 3108 |
'Startdate_coa' => 'Gültig ab', |
3111 | 3109 |
'Starting Balance' => 'Eröffnungsbilanzwerte', |
... | ... | |
3661 | 3659 |
'This discount is only valid in purchase documents' => 'Dieser Rabatt ist nur in Einkaufsdokumenten gültig', |
3662 | 3660 |
'This discount is only valid in records with customer or vendor' => 'Dieser Rabatt ist nur in Dokumenten mit Kunde oder Lieferant gültig', |
3663 | 3661 |
'This discount is only valid in sales documents' => 'Dieser Rabatt ist nur in Verkaufsdokumenten gültig', |
3662 |
'This entry is using date and duration. This information will be overwritten on saving.' => 'Dieser Eintrag verwendet Datum und Dauer. Diese Information wird beim Speichern überschrieben.', |
|
3663 |
'This entry is using start and end time. This information will be overwritten on saving.' => 'Dieser Eintrag verwendet Start- und End-Zeit. Diese Information wird beim Speichern überschrieben.', |
|
3664 | 3664 |
'This export will include all records in the given time range and all supplicant information from checked entities. You will receive a single zip file. Please extract this file onto the data medium requested by your auditor.' => 'Dieser Export umfasst alle Belege im gewählten Zeitrahmen und die dazugehörgen Informationen aus den gewählten Blöcken. Sie erhalten eine einzelne Zip-Datei. Bitte entpacken Sie diese auf das Medium das Ihr Steuerprüfer wünscht.', |
3665 | 3665 |
'This feature especially prevents mistakes by mixing up prior tax and sales tax.' => 'Dieses Feature vermeidet insbesondere Verwechslungen von Umsatz- und Vorsteuer.', |
3666 | 3666 |
'This field must not be empty.' => 'Dieses Feld darf nicht leer sein.', |
... | ... | |
3737 | 3737 |
'To (email)' => 'An', |
3738 | 3738 |
'To (time)' => 'Bis', |
3739 | 3739 |
'To Date' => 'Bis', |
3740 |
'To Start' => 'Bis Start', |
|
3741 | 3740 |
'To continue please change the taxkey 0 to another value.' => 'Um fortzufahren, ändern Sie bitte den Steuerschlüssel 0 auf einen anderen Wert.', |
3742 | 3741 |
'To import' => 'Zu importieren', |
3743 | 3742 |
'To upload images: Please create shoppart first' => 'Um Bilder hochzuladen bitte Shopartikel zuerst anlegen', |
... | ... | |
3910 | 3909 |
'Use a text field to enter (new) contact titles if enabled. Otherwise, only a drop down box is offered.' => 'Textfeld zusätzlich zur Eingabe (neuer) Titel von Ansprechpersonen verwenden. Sonst wird nur eine Auswahlliste angezeigt.', |
3911 | 3910 |
'Use a text field to enter (new) greetings if enabled. Otherwise, only a drop down box is offered.' => 'Textfeld zusätzlich zur Eingabe (neuer) Anreden verwenden. Sonst wird nur eine Auswahlliste angezeigt.', |
3912 | 3911 |
'Use as new' => 'Als neu verwenden', |
3912 |
'Use date and duration for time recordings' => 'Datum und Dauer für Zeiterfassung verwenden', |
|
3913 | 3913 |
'Use default booking group because setting is \'all\'' => 'Standardbuchungsgruppe wird verwendet', |
3914 | 3914 |
'Use default booking group because wanted is missing' => 'Fehlende Buchungsgruppe, deshalb Standardbuchungsgruppe', |
3915 | 3915 |
'Use default warehouse for assembly transfer' => 'Zum Fertigen Standardlager des Bestandteils verwenden', |
... | ... | |
4232 | 4232 |
'http' => 'http', |
4233 | 4233 |
'https' => 'https', |
4234 | 4234 |
'imported' => 'Importiert', |
4235 |
'in minutes' => 'in Minuten', |
|
4235 | 4236 |
'inactive' => 'inaktiv', |
4236 | 4237 |
'income' => 'Einnahmen-Überschuß-Rechnung', |
4237 | 4238 |
'internal error (see details)' => 'Interner Fehler (siehe Details)!', |
locale/en/all | ||
---|---|---|
1530 | 1530 |
'Fristsetzung' => '', |
1531 | 1531 |
'From' => '', |
1532 | 1532 |
'From Date' => '', |
1533 |
'From Start' => '', |
|
1534 | 1533 |
'From bin' => '', |
1535 | 1534 |
'From shop "#1" : #2 ' => '', |
1536 | 1535 |
'From shop #1 : #2 shoporders have been fetched.' => '', |
... | ... | |
3105 | 3104 |
'Start the correction assistant' => '', |
3106 | 3105 |
'Start time' => '', |
3107 | 3106 |
'Start time must be earlier than end time.' => '', |
3108 |
'Start time must not be empty.' => '', |
|
3109 | 3107 |
'Startdate method' => '', |
3110 | 3108 |
'Startdate_coa' => '', |
3111 | 3109 |
'Starting Balance' => '', |
... | ... | |
3660 | 3658 |
'This discount is only valid in purchase documents' => '', |
3661 | 3659 |
'This discount is only valid in records with customer or vendor' => '', |
3662 | 3660 |
'This discount is only valid in sales documents' => '', |
3661 |
'This entry is using date and duration. This information will be overwritten on saving.' => '', |
|
3662 |
'This entry is using start and end time. This information will be overwritten on saving.' => '', |
|
3663 | 3663 |
'This export will include all records in the given time range and all supplicant information from checked entities. You will receive a single zip file. Please extract this file onto the data medium requested by your auditor.' => '', |
3664 | 3664 |
'This feature especially prevents mistakes by mixing up prior tax and sales tax.' => '', |
3665 | 3665 |
'This field must not be empty.' => '', |
... | ... | |
3736 | 3736 |
'To (email)' => '', |
3737 | 3737 |
'To (time)' => '', |
3738 | 3738 |
'To Date' => '', |
3739 |
'To Start' => '', |
|
3740 | 3739 |
'To continue please change the taxkey 0 to another value.' => '', |
3741 | 3740 |
'To import' => '', |
3742 | 3741 |
'To upload images: Please create shoppart first' => '', |
... | ... | |
3909 | 3908 |
'Use a text field to enter (new) contact titles if enabled. Otherwise, only a drop down box is offered.' => '', |
3910 | 3909 |
'Use a text field to enter (new) greetings if enabled. Otherwise, only a drop down box is offered.' => '', |
3911 | 3910 |
'Use as new' => '', |
3911 |
'Use date and duration for time recordings' => '', |
|
3912 | 3912 |
'Use default booking group because setting is \'all\'' => '', |
3913 | 3913 |
'Use default booking group because wanted is missing' => '', |
3914 | 3914 |
'Use default warehouse for assembly transfer' => '', |
... | ... | |
4230 | 4230 |
'http' => '', |
4231 | 4231 |
'https' => '', |
4232 | 4232 |
'imported' => '', |
4233 |
'in minutes' => '', |
|
4233 | 4234 |
'inactive' => '', |
4234 | 4235 |
'income' => 'GUV and BWA', |
4235 | 4236 |
'internal error (see details)' => '', |
templates/webpages/am/config.html | ||
---|---|---|
87 | 87 |
</td> |
88 | 88 |
</tr> |
89 | 89 |
|
90 |
<tr> |
|
91 |
<th align="right">[% 'Use date and duration for time recordings' | $T8 %]</th> |
|
92 |
<td> |
|
93 |
[% L.yes_no_tag('time_recording_use_duration', time_recording_use_duration) %] |
|
94 |
</td> |
|
95 |
</tr> |
|
96 |
|
|
90 | 97 |
</table> |
91 | 98 |
</div> |
92 | 99 |
|
templates/webpages/time_recording/form.html | ||
---|---|---|
15 | 15 |
<table> |
16 | 16 |
<thead class="listheading"> |
17 | 17 |
<tr> |
18 |
[%- IF SELF.use_duration %] |
|
19 |
<th>[% 'Date' | $T8 %]</th> |
|
20 |
<th>[% 'Duration' | $T8 %] ([% 'in minutes' | $T8 %])</th> |
|
21 |
[%- ELSE %] |
|
18 | 22 |
<th>[% 'Start' | $T8 %]</th> |
19 |
<th>[% 'End' | $T8 %]</th> |
|
23 |
<th>[% 'End' | $T8 %]</th> |
|
24 |
[%- END %] |
|
20 | 25 |
<th>[% 'Customer' | $T8 %]</th> |
21 | 26 |
<th>[% 'Article' | $T8 %]</th> |
22 | 27 |
<th>[% 'Project' | $T8 %]</th> |
... | ... | |
26 | 31 |
</thead> |
27 | 32 |
<tbody valign="top"> |
28 | 33 |
<tr> |
34 |
[%- IF SELF.use_duration %] |
|
35 |
<td> |
|
36 |
[% P.date_tag('time_recording.date_as_date', SELF.time_recording.date_as_date, "data-validate"="required", "data-title"=LxERP.t8('Date')) %]<br> |
|
37 |
</td> |
|
38 |
<td> |
|
39 |
[% P.input_tag('time_recording.duration', SELF.time_recording.duration, size=15) %] |
|
40 |
</td> |
|
41 |
[%- ELSE %] |
|
29 | 42 |
<td> |
30 | 43 |
[% P.date_tag('start_date', SELF.start_date, "data-validate"="required", "data-title"=LxERP.t8('Start date'), onchange='kivi.TimeRecording.set_end_date()') %]<br> |
31 | 44 |
[% P.input_tag('start_time', SELF.start_time, type="time", "data-validate"="required", "data-title"=LxERP.t8('Start time')) %] |
... | ... | |
36 | 49 |
[% P.input_tag('end_time', SELF.end_time, type="time") %] |
37 | 50 |
[% P.button_tag('kivi.TimeRecording.set_current_date_time("end")', LxERP.t8('now')) %] |
38 | 51 |
</td> |
52 |
[%- END %] |
|
39 | 53 |
<td>[% P.customer_vendor.picker('time_recording.customer_id', SELF.time_recording.customer_id, type='customer', style='width: 300px', "data-validate"="required", "data-title"=LxERP.t8('Customer')) %]</td> |
40 | 54 |
<td>[% P.select_tag('time_recording.part_id', SELF.all_time_recording_articles, default=SELF.time_recording.part_id, with_empty=1, value_key='id', title_key='description') %]</td> |
41 | 55 |
<td>[% P.project.picker('time_recording.project_id', SELF.time_recording.project_id, style='width: 300px') %]</td> |
Auch abrufbar als: Unified diff
Zeiterfassung: Datum/Dauer statt Start/Ende wählbar (Benutzereinstellung)