Revision 33823a77
Von Bernd Bleßmann vor fast 4 Jahren hinzugefügt
SL/AM.pm | ||
---|---|---|
use SL::GenericTranslations;
|
||
use SL::Helper::UserPreferences::PositionsScrollbar;
|
||
use SL::Helper::UserPreferences::PartPickerSearch;
|
||
use SL::Helper::UserPreferences::TimeRecording;
|
||
use SL::Helper::UserPreferences::UpdatePositions;
|
||
|
||
use strict;
|
||
... | ... | |
SL::Helper::UserPreferences::UpdatePositions->new()->get_show_update_button();
|
||
}
|
||
|
||
sub time_recording_use_duration {
|
||
SL::Helper::UserPreferences::TimeRecording->new()->get_use_duration();
|
||
}
|
||
|
||
sub save_preferences {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
... | ... | |
if (exists $form->{positions_show_update_button}) {
|
||
SL::Helper::UserPreferences::UpdatePositions->new()->store_show_update_button($form->{positions_show_update_button})
|
||
}
|
||
if (exists $form->{time_recording_use_duration}) {
|
||
SL::Helper::UserPreferences::TimeRecording->new()->store_use_duration($form->{time_recording_use_duration})
|
||
}
|
||
|
||
$main::lxdebug->leave_sub();
|
||
|
SL/Controller/TimeRecording.pm | ||
---|---|---|
use SL::DB::Part;
|
||
use SL::DB::TimeRecording;
|
||
use SL::DB::TimeRecordingArticle;
|
||
use SL::Helper::Flash qw(flash);
|
||
use SL::Helper::UserPreferences::TimeRecording;
|
||
use SL::Locale::String qw(t8);
|
||
use SL::ReportGenerator;
|
||
|
||
use Rose::Object::MakeMethods::Generic
|
||
(
|
||
# scalar => [ qw() ],
|
||
'scalar --get_set_init' => [ qw(time_recording models all_employees all_time_recording_articles can_view_all can_edit_all) ],
|
||
'scalar --get_set_init' => [ qw(time_recording models all_employees all_time_recording_articles can_view_all can_edit_all use_duration) ],
|
||
);
|
||
|
||
|
||
... | ... | |
|
||
$::request->{layout}->use_javascript("${_}.js") for qw(kivi.TimeRecording ckeditor/ckeditor ckeditor/adapters/jquery kivi.Validator);
|
||
|
||
if ($self->use_duration) {
|
||
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;
|
||
} else {
|
||
flash('warning', t8('This entry is using date and duration. This information will be overwritten on saving.')) if $self->time_recording->is_duration_used;
|
||
}
|
||
|
||
if ($self->time_recording->start_time) {
|
||
$self->{start_date} = $self->time_recording->start_time->to_kivitendo;
|
||
$self->{start_time} = $self->time_recording->start_time->to_kivitendo_time;
|
||
... | ... | |
sub action_save {
|
||
my ($self) = @_;
|
||
|
||
if ($self->use_duration) {
|
||
$self->time_recording->start_date(undef);
|
||
$self->time_recording->end_date(undef);
|
||
}
|
||
|
||
my @errors = $self->time_recording->validate;
|
||
if (@errors) {
|
||
$::form->error(t8('Saving the time recording entry failed: #1', join '<br>', @errors));
|
||
... | ... | |
}
|
||
|
||
sub init_time_recording {
|
||
my ($self) = @_;
|
||
|
||
my $is_new = !$::form->{id};
|
||
my $time_recording = $is_new ? SL::DB::TimeRecording->new(start_time => DateTime->now_local)
|
||
: SL::DB::TimeRecording->new(id => $::form->{id})->load;
|
||
my $time_recording = !$is_new ? SL::DB::TimeRecording->new(id => $::form->{id})->load
|
||
: $self->use_duration ? SL::DB::TimeRecording->new(date => DateTime->today_local)
|
||
: SL::DB::TimeRecording->new(start_time => DateTime->now_local);
|
||
|
||
my %attributes = %{ $::form->{time_recording} || {} };
|
||
|
||
foreach my $type (qw(start end)) {
|
||
if ($::form->{$type . '_date'}) {
|
||
my $date = DateTime->from_kivitendo($::form->{$type . '_date'});
|
||
$attributes{$type . '_time'} = $date->clone;
|
||
if ($::form->{$type . '_time'}) {
|
||
my ($hour, $min) = split ':', $::form->{$type . '_time'};
|
||
$attributes{$type . '_time'}->set_hour($hour) if $hour;
|
||
$attributes{$type . '_time'}->set_minute($min) if $min;
|
||
if (!$self->use_duration) {
|
||
foreach my $type (qw(start end)) {
|
||
if ($::form->{$type . '_date'}) {
|
||
my $date = DateTime->from_kivitendo($::form->{$type . '_date'});
|
||
$attributes{$type . '_time'} = $date->clone;
|
||
if ($::form->{$type . '_time'}) {
|
||
my ($hour, $min) = split ':', $::form->{$type . '_time'};
|
||
$attributes{$type . '_time'}->set_hour($hour) if $hour;
|
||
$attributes{$type . '_time'}->set_minute($min) if $min;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
# do not overwright staff member if you do not have the right
|
||
# do not overwrite staff member if you do not have the right
|
||
delete $attributes{staff_member_id} if !$_[0]->can_edit_all;
|
||
$attributes{staff_member_id} = SL::DB::Manager::Employee->current->id if $is_new;
|
||
|
||
... | ... | |
return $res;
|
||
}
|
||
|
||
sub init_use_duration {
|
||
return SL::Helper::UserPreferences::TimeRecording->new()->get_use_duration();
|
||
}
|
||
|
||
sub check_auth {
|
||
$::auth->assert('time_recording');
|
||
}
|
||
... | ... | |
my $staff_member = $filter->{staff_member_id} ? SL::DB::Employee->new(id => $filter->{staff_member_id})->load->safe_name : '';
|
||
|
||
my @filters = (
|
||
[ $filter->{"start_time:date::ge"}, t8('From Start') ],
|
||
[ $filter->{"start_time:date::le"}, t8('To Start') ],
|
||
[ $filter->{"date:date::ge"}, t8('From Date') ],
|
||
[ $filter->{"date:date::le"}, t8('To Date') ],
|
||
[ $filter->{"customer"}->{"name:substr::ilike"}, t8('Customer') ],
|
||
[ $filter->{"customer"}->{"customernumber:substr::ilike"}, t8('Customer Number') ],
|
||
[ $staff_member, t8('Mitarbeiter') ],
|
SL/DB/TimeRecording.pm | ||
---|---|---|
|
||
my @errors;
|
||
|
||
push @errors, t8('Start time must not be empty.') if !$self->start_time;
|
||
push @errors, t8('Customer must not be empty.') if !$self->customer_id;
|
||
push @errors, t8('Staff member must not be empty.') if !$self->staff_member_id;
|
||
push @errors, t8('Employee must not be empty.') if !$self->employee_id;
|
||
... | ... | |
return;
|
||
}
|
||
|
||
sub is_duration_used {
|
||
return !$_[0]->start_time;
|
||
}
|
||
|
||
sub displayable_times {
|
||
my ($self) = @_;
|
||
|
SL/Helper/UserPreferences/TimeRecording.pm | ||
---|---|---|
package SL::Helper::UserPreferences::TimeRecording;
|
||
|
||
use strict;
|
||
use parent qw(Rose::Object);
|
||
|
||
use Carp;
|
||
use List::MoreUtils qw(none);
|
||
|
||
use SL::Helper::UserPreferences;
|
||
|
||
use Rose::Object::MakeMethods::Generic (
|
||
'scalar --get_set_init' => [ qw(user_prefs) ],
|
||
);
|
||
|
||
sub get_use_duration {
|
||
!!$_[0]->user_prefs->get('use_duration');
|
||
}
|
||
|
||
sub store_use_duration {
|
||
$_[0]->user_prefs->store('use_duration', $_[1]);
|
||
}
|
||
|
||
sub init_user_prefs {
|
||
SL::Helper::UserPreferences->new(
|
||
namespace => $_[0]->namespace,
|
||
)
|
||
}
|
||
|
||
# read only stuff
|
||
sub namespace { 'TimeRecording' }
|
||
sub version { 1 }
|
||
|
||
1;
|
||
|
||
__END__
|
||
|
||
=pod
|
||
|
||
=encoding utf-8
|
||
|
||
=head1 NAME
|
||
|
||
SL::Helper::UserPreferences::TimeRecording - preferences intended
|
||
to store user settings for using the time recording functionality.
|
||
|
||
=head1 SYNOPSIS
|
||
|
||
use SL::Helper::UserPreferences::TimeRecording;
|
||
my $prefs = SL::Helper::UserPreferences::TimeRecording->new();
|
||
|
||
$prefs->store_use_duration(1);
|
||
my $value = $prefs->get_use_duration;
|
||
|
||
=head1 DESCRIPTION
|
||
|
||
This module manages storing the user's choise for settings for
|
||
the time recording controller.
|
||
For now it can be choosen if an entry is done by entering start and
|
||
end time or a date and a duration.
|
||
|
||
=head1 BUGS
|
||
|
||
None yet :)
|
||
|
||
=head1 AUTHOR
|
||
|
||
Bernd Bleßmann E<lt>bernd@kivitendo-premium.deE<gt>
|
||
|
||
=cut
|
bin/mozilla/am.pl | ||
---|---|---|
$form->{purchase_search_makemodel} = AM->purchase_search_makemodel();
|
||
$form->{sales_search_customer_partnumber} = AM->sales_search_customer_partnumber();
|
||
$form->{positions_show_update_button} = AM->positions_show_update_button();
|
||
$form->{time_recording_use_duration} = AM->time_recording_use_duration();
|
||
|
||
$myconfig{show_form_details} = 1 unless (defined($myconfig{show_form_details}));
|
||
$form->{CAN_CHANGE_PASSWORD} = $main::auth->can_change_password();
|
locale/de/all | ||
---|---|---|
'Fristsetzung' => 'Fristsetzung',
|
||
'From' => 'Von',
|
||
'From Date' => 'Von',
|
||
'From Start' => 'Ab Start',
|
||
'From bin' => 'Ausgelagert',
|
||
'From shop "#1" : #2 ' => 'Shop #1 : #2',
|
||
'From shop #1 : #2 shoporders have been fetched.' => 'Es wurden #2 Bestellungen von #1 geholt.',
|
||
... | ... | |
'Start the correction assistant' => 'Korrekturassistenten starten',
|
||
'Start time' => 'Startzeit',
|
||
'Start time must be earlier than end time.' => 'Startzeit muss vor der Endzeit liegen.',
|
||
'Start time must not be empty.' => 'Startzeit darf nicht leer sein.',
|
||
'Startdate method' => 'Methode zur Ermittlung des Startdatums',
|
||
'Startdate_coa' => 'Gültig ab',
|
||
'Starting Balance' => 'Eröffnungsbilanzwerte',
|
||
... | ... | |
'This discount is only valid in purchase documents' => 'Dieser Rabatt ist nur in Einkaufsdokumenten gültig',
|
||
'This discount is only valid in records with customer or vendor' => 'Dieser Rabatt ist nur in Dokumenten mit Kunde oder Lieferant gültig',
|
||
'This discount is only valid in sales documents' => 'Dieser Rabatt ist nur in Verkaufsdokumenten gültig',
|
||
'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.',
|
||
'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.',
|
||
'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.',
|
||
'This feature especially prevents mistakes by mixing up prior tax and sales tax.' => 'Dieses Feature vermeidet insbesondere Verwechslungen von Umsatz- und Vorsteuer.',
|
||
'This field must not be empty.' => 'Dieses Feld darf nicht leer sein.',
|
||
... | ... | |
'To (email)' => 'An',
|
||
'To (time)' => 'Bis',
|
||
'To Date' => 'Bis',
|
||
'To Start' => 'Bis Start',
|
||
'To continue please change the taxkey 0 to another value.' => 'Um fortzufahren, ändern Sie bitte den Steuerschlüssel 0 auf einen anderen Wert.',
|
||
'To import' => 'Zu importieren',
|
||
'To upload images: Please create shoppart first' => 'Um Bilder hochzuladen bitte Shopartikel zuerst anlegen',
|
||
... | ... | |
'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.',
|
||
'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.',
|
||
'Use as new' => 'Als neu verwenden',
|
||
'Use date and duration for time recordings' => 'Datum und Dauer für Zeiterfassung verwenden',
|
||
'Use default booking group because setting is \'all\'' => 'Standardbuchungsgruppe wird verwendet',
|
||
'Use default booking group because wanted is missing' => 'Fehlende Buchungsgruppe, deshalb Standardbuchungsgruppe',
|
||
'Use default warehouse for assembly transfer' => 'Zum Fertigen Standardlager des Bestandteils verwenden',
|
||
... | ... | |
'http' => 'http',
|
||
'https' => 'https',
|
||
'imported' => 'Importiert',
|
||
'in minutes' => 'in Minuten',
|
||
'inactive' => 'inaktiv',
|
||
'income' => 'Einnahmen-Überschuß-Rechnung',
|
||
'internal error (see details)' => 'Interner Fehler (siehe Details)!',
|
locale/en/all | ||
---|---|---|
'Fristsetzung' => '',
|
||
'From' => '',
|
||
'From Date' => '',
|
||
'From Start' => '',
|
||
'From bin' => '',
|
||
'From shop "#1" : #2 ' => '',
|
||
'From shop #1 : #2 shoporders have been fetched.' => '',
|
||
... | ... | |
'Start the correction assistant' => '',
|
||
'Start time' => '',
|
||
'Start time must be earlier than end time.' => '',
|
||
'Start time must not be empty.' => '',
|
||
'Startdate method' => '',
|
||
'Startdate_coa' => '',
|
||
'Starting Balance' => '',
|
||
... | ... | |
'This discount is only valid in purchase documents' => '',
|
||
'This discount is only valid in records with customer or vendor' => '',
|
||
'This discount is only valid in sales documents' => '',
|
||
'This entry is using date and duration. This information will be overwritten on saving.' => '',
|
||
'This entry is using start and end time. This information will be overwritten on saving.' => '',
|
||
'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.' => '',
|
||
'This feature especially prevents mistakes by mixing up prior tax and sales tax.' => '',
|
||
'This field must not be empty.' => '',
|
||
... | ... | |
'To (email)' => '',
|
||
'To (time)' => '',
|
||
'To Date' => '',
|
||
'To Start' => '',
|
||
'To continue please change the taxkey 0 to another value.' => '',
|
||
'To import' => '',
|
||
'To upload images: Please create shoppart first' => '',
|
||
... | ... | |
'Use a text field to enter (new) contact titles if enabled. Otherwise, only a drop down box is offered.' => '',
|
||
'Use a text field to enter (new) greetings if enabled. Otherwise, only a drop down box is offered.' => '',
|
||
'Use as new' => '',
|
||
'Use date and duration for time recordings' => '',
|
||
'Use default booking group because setting is \'all\'' => '',
|
||
'Use default booking group because wanted is missing' => '',
|
||
'Use default warehouse for assembly transfer' => '',
|
||
... | ... | |
'http' => '',
|
||
'https' => '',
|
||
'imported' => '',
|
||
'in minutes' => '',
|
||
'inactive' => '',
|
||
'income' => 'GUV and BWA',
|
||
'internal error (see details)' => '',
|
templates/webpages/am/config.html | ||
---|---|---|
</td>
|
||
</tr>
|
||
|
||
<tr>
|
||
<th align="right">[% 'Use date and duration for time recordings' | $T8 %]</th>
|
||
<td>
|
||
[% L.yes_no_tag('time_recording_use_duration', time_recording_use_duration) %]
|
||
</td>
|
||
</tr>
|
||
|
||
</table>
|
||
</div>
|
||
|
templates/webpages/time_recording/form.html | ||
---|---|---|
<table>
|
||
<thead class="listheading">
|
||
<tr>
|
||
[%- IF SELF.use_duration %]
|
||
<th>[% 'Date' | $T8 %]</th>
|
||
<th>[% 'Duration' | $T8 %] ([% 'in minutes' | $T8 %])</th>
|
||
[%- ELSE %]
|
||
<th>[% 'Start' | $T8 %]</th>
|
||
<th>[% 'End' | $T8 %]</th>
|
||
<th>[% 'End' | $T8 %]</th>
|
||
[%- END %]
|
||
<th>[% 'Customer' | $T8 %]</th>
|
||
<th>[% 'Article' | $T8 %]</th>
|
||
<th>[% 'Project' | $T8 %]</th>
|
||
... | ... | |
</thead>
|
||
<tbody valign="top">
|
||
<tr>
|
||
[%- IF SELF.use_duration %]
|
||
<td>
|
||
[% P.date_tag('time_recording.date_as_date', SELF.time_recording.date_as_date, "data-validate"="required", "data-title"=LxERP.t8('Date')) %]<br>
|
||
</td>
|
||
<td>
|
||
[% P.input_tag('time_recording.duration', SELF.time_recording.duration, size=15) %]
|
||
</td>
|
||
[%- ELSE %]
|
||
<td>
|
||
[% P.date_tag('start_date', SELF.start_date, "data-validate"="required", "data-title"=LxERP.t8('Start date'), onchange='kivi.TimeRecording.set_end_date()') %]<br>
|
||
[% P.input_tag('start_time', SELF.start_time, type="time", "data-validate"="required", "data-title"=LxERP.t8('Start time')) %]
|
||
... | ... | |
[% P.input_tag('end_time', SELF.end_time, type="time") %]
|
||
[% P.button_tag('kivi.TimeRecording.set_current_date_time("end")', LxERP.t8('now')) %]
|
||
</td>
|
||
[%- END %]
|
||
<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>
|
||
<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>
|
||
<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)