Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 33823a77

Von Bernd Bleßmann vor fast 4 Jahren hinzugefügt

  • ID 33823a7743de188f6e37802716ee5bd877a3ec5f
  • Vorgänger 22f2c3e8
  • Nachfolger 78edb322

Zeiterfassung: Datum/Dauer statt Start/Ende wählbar (Benutzereinstellung)

Unterschiede anzeigen:

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