Revision c553e92f
Von Bernd Bleßmann vor mehr als 4 Jahren hinzugefügt
SL/Controller/TimeRecording.pm | ||
---|---|---|
package SL::Controller::TimeRecording;
|
||
|
||
use strict;
|
||
use parent qw(SL::Controller::Base);
|
||
|
||
use DateTime;
|
||
use English qw(-no_match_vars);
|
||
use POSIX qw(strftime);
|
||
|
||
use SL::Controller::Helper::GetModels;
|
||
use SL::Controller::Helper::ReportGenerator;
|
||
use SL::DB::Customer;
|
||
use SL::DB::Employee;
|
||
use SL::DB::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_time_recording_types all_employees) ],
|
||
);
|
||
|
||
|
||
# safety
|
||
#__PACKAGE__->run_before('check_auth');
|
||
|
||
#
|
||
# actions
|
||
#
|
||
|
||
my %sort_columns = (
|
||
start_time => t8('Start'),
|
||
end_time => t8('End'),
|
||
customer => t8('Customer'),
|
||
type => t8('Type'),
|
||
project => t8('Project'),
|
||
description => t8('Description'),
|
||
staff_member => t8('Mitarbeiter'),
|
||
duration => t8('Duration'),
|
||
);
|
||
|
||
sub action_list {
|
||
my ($self, %params) = @_;
|
||
|
||
$self->setup_list_action_bar;
|
||
$self->make_filter_summary;
|
||
$self->prepare_report;
|
||
|
||
$self->report_generator_list_objects(report => $self->{report}, objects => $self->models->get);
|
||
}
|
||
|
||
sub action_edit {
|
||
my ($self) = @_;
|
||
|
||
$::request->{layout}->use_javascript("${_}.js") for qw(kivi.TimeRecording ckeditor/ckeditor ckeditor/adapters/jquery kivi.Validator);
|
||
|
||
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;
|
||
}
|
||
if ($self->time_recording->end_time) {
|
||
$self->{end_date} = $self->time_recording->end_time->to_kivitendo;
|
||
$self->{end_time} = $self->time_recording->end_time->to_kivitendo_time;
|
||
}
|
||
|
||
$self->setup_edit_action_bar;
|
||
|
||
$self->render('time_recording/form',
|
||
title => t8('Time Recording'),
|
||
);
|
||
}
|
||
|
||
sub action_save {
|
||
my ($self) = @_;
|
||
|
||
my @errors = $self->time_recording->validate;
|
||
if (@errors) {
|
||
$::form->error(t8('Saving the time recording entry failed: #1', join '<br>', @errors));
|
||
return;
|
||
}
|
||
|
||
if ( !eval { $self->time_recording->save; 1; } ) {
|
||
$::form->error(t8('Saving the time recording entry failed: #1', $EVAL_ERROR));
|
||
return;
|
||
}
|
||
|
||
$self->redirect_to(safe_callback());
|
||
}
|
||
|
||
sub action_delete {
|
||
my ($self) = @_;
|
||
|
||
$self->time_recording->delete;
|
||
|
||
$self->redirect_to(safe_callback());
|
||
}
|
||
|
||
sub init_time_recording {
|
||
my $time_recording = ($::form->{id}) ? SL::DB::TimeRecording->new(id => $::form->{id})->load
|
||
: 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;
|
||
}
|
||
}
|
||
}
|
||
|
||
$attributes{staff_member_id} = $attributes{employee_id} = SL::DB::Manager::Employee->current->id;
|
||
|
||
$time_recording->assign_attributes(%attributes);
|
||
|
||
return $time_recording;
|
||
}
|
||
|
||
sub init_models {
|
||
SL::Controller::Helper::GetModels->new(
|
||
controller => $_[0],
|
||
sorted => \%sort_columns,
|
||
disable_plugin => 'paginated',
|
||
with_objects => [ 'customer', 'type', 'project', 'staff_member', 'employee' ],
|
||
);
|
||
}
|
||
|
||
sub init_all_time_recording_types {
|
||
SL::DB::Manager::TimeRecordingType->get_all_sorted(query => [obsolete => 0]);
|
||
}
|
||
|
||
sub init_all_employees {
|
||
SL::DB::Manager::Employee->get_all_sorted;
|
||
}
|
||
|
||
sub prepare_report {
|
||
my ($self) = @_;
|
||
|
||
my $report = SL::ReportGenerator->new(\%::myconfig, $::form);
|
||
$self->{report} = $report;
|
||
|
||
my @columns = qw(start_time end_time customer type project description staff_member duration);
|
||
|
||
my %column_defs = (
|
||
start_time => { text => t8('Start'), sub => sub { $_[0]->start_time_as_timestamp },
|
||
obj_link => sub { $self->url_for(action => 'edit', 'id' => $_[0]->id, callback => $self->models->get_callback) } },
|
||
end_time => { text => t8('End'), sub => sub { $_[0]->end_time_as_timestamp },
|
||
obj_link => sub { $self->url_for(action => 'edit', 'id' => $_[0]->id, callback => $self->models->get_callback) } },
|
||
customer => { text => t8('Customer'), sub => sub { $_[0]->customer->displayable_name } },
|
||
type => { text => t8('Type'), sub => sub { $_[0]->type && $_[0]->type->abbreviation } },
|
||
project => { text => t8('Project'), sub => sub { $_[0]->project && $_[0]->project->displayable_name } },
|
||
description => { text => t8('Description'), sub => sub { $_[0]->description_as_stripped_html },
|
||
raw_data => sub { $_[0]->description_as_restricted_html }, # raw_data only used for html(?)
|
||
obj_link => sub { $self->url_for(action => 'edit', 'id' => $_[0]->id, callback => $self->models->get_callback) } },
|
||
staff_member => { text => t8('Mitarbeiter'), sub => sub { $_[0]->staff_member->safe_name } },
|
||
duration => { text => t8('Duration'), sub => sub { $_[0]->duration_as_duration_string },
|
||
align => 'right'},
|
||
);
|
||
|
||
$report->set_options(
|
||
controller_class => 'TimeRecording',
|
||
std_column_visibility => 1,
|
||
output_format => 'HTML',
|
||
title => t8('Time Recordings'),
|
||
allow_pdf_export => 1,
|
||
allow_csv_export => 1,
|
||
);
|
||
|
||
$report->set_columns(%column_defs);
|
||
$report->set_column_order(@columns);
|
||
$report->set_export_options(qw(list filter));
|
||
$report->set_options_from_form;
|
||
|
||
$self->models->disable_plugin('paginated') if $report->{options}{output_format} =~ /^(pdf|csv)$/i;
|
||
#$self->models->add_additional_url_params();
|
||
$self->models->finalize;
|
||
$self->models->set_report_generator_sort_options(report => $report, sortable_columns => [keys %sort_columns]);
|
||
|
||
$report->set_options(
|
||
raw_top_info_text => $self->render('time_recording/report_top', { output => 0 }),
|
||
raw_bottom_info_text => $self->render('time_recording/report_bottom', { output => 0 }, models => $self->models),
|
||
attachment_basename => t8('time_recordings') . strftime('_%Y%m%d', localtime time),
|
||
);
|
||
}
|
||
|
||
sub make_filter_summary {
|
||
my ($self) = @_;
|
||
|
||
my $filter = $::form->{filter} || {};
|
||
my @filter_strings;
|
||
|
||
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->{"customer"}->{"name:substr::ilike"}, t8('Customer') ],
|
||
[ $filter->{"customer"}->{"customernumber:substr::ilike"}, t8('Customer Number') ],
|
||
[ $staff_member, t8('Mitarbeiter') ],
|
||
);
|
||
|
||
for (@filters) {
|
||
push @filter_strings, "$_->[1]: $_->[0]" if $_->[0];
|
||
}
|
||
|
||
$self->{filter_summary} = join ', ', @filter_strings;
|
||
}
|
||
|
||
sub setup_list_action_bar {
|
||
my ($self) = @_;
|
||
|
||
for my $bar ($::request->layout->get('actionbar')) {
|
||
$bar->add(
|
||
action => [
|
||
t8('Update'),
|
||
submit => [ '#filter_form', { action => 'TimeRecording/list' } ],
|
||
accesskey => 'enter',
|
||
],
|
||
action => [
|
||
t8('Add'),
|
||
link => $self->url_for(action => 'edit', callback => $self->models->get_callback),
|
||
],
|
||
);
|
||
}
|
||
}
|
||
|
||
sub setup_edit_action_bar {
|
||
my ($self) = @_;
|
||
|
||
for my $bar ($::request->layout->get('actionbar')) {
|
||
$bar->add(
|
||
action => [
|
||
t8('Save'),
|
||
submit => [ '#form', { action => 'TimeRecording/save' } ],
|
||
checks => [ 'kivi.validate_form' ],
|
||
],
|
||
action => [
|
||
t8('Delete'),
|
||
submit => [ '#form', { action => 'TimeRecording/delete' } ],
|
||
only_if => $self->time_recording->id,
|
||
],
|
||
action => [
|
||
t8('Cancel'),
|
||
link => $self->url_for(safe_callback()),
|
||
],
|
||
);
|
||
}
|
||
}
|
||
|
||
sub safe_callback {
|
||
$::form->{callback} || (action => 'list')
|
||
}
|
||
|
||
1;
|
SL/DB/Manager/TimeRecording.pm | ||
---|---|---|
|
||
use parent qw(SL::DB::Helper::Manager);
|
||
|
||
use SL::DB::Helper::Sorted;
|
||
|
||
sub object_class { 'SL::DB::TimeRecording' }
|
||
|
||
__PACKAGE__->make_manager_methods;
|
||
|
||
|
||
sub _sort_spec {
|
||
return ( default => [ 'start_time', 1 ],
|
||
columns => { SIMPLE => 'ALL' ,
|
||
customer => [ 'lower(customer.name)', ],
|
||
}
|
||
);
|
||
}
|
||
|
||
|
||
1;
|
SL/DB/TimeRecording.pm | ||
---|---|---|
|
||
use strict;
|
||
|
||
use SL::Locale::String qw(t8);
|
||
|
||
use SL::DB::Helper::AttrDuration;
|
||
use SL::DB::Helper::AttrHTML;
|
||
|
||
use SL::DB::MetaSetup::TimeRecording;
|
||
use SL::DB::Manager::TimeRecording;
|
||
|
||
__PACKAGE__->meta->initialize;
|
||
|
||
__PACKAGE__->attr_duration_minutes(qw(duration));
|
||
|
||
__PACKAGE__->attr_html('description');
|
||
|
||
__PACKAGE__->before_save('_before_save_check_valid');
|
||
|
||
sub _before_save_check_valid {
|
||
my ($self) = @_;
|
||
|
||
my @errors = $self->validate;
|
||
return (scalar @errors == 0);
|
||
}
|
||
|
||
sub validate {
|
||
my ($self) = @_;
|
||
|
||
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;
|
||
push @errors, t8('Description must not be empty.') if !$self->description;
|
||
push @errors, t8('Start time must be earlier than end time.') if $self->is_time_in_wrong_order;
|
||
|
||
my $conflict = $self->is_time_overlapping;
|
||
push @errors, t8('Entry overlaps with "#1".', $conflict->displayable_times) if $conflict;
|
||
|
||
return @errors;
|
||
}
|
||
|
||
sub is_time_overlapping {
|
||
my ($self) = @_;
|
||
|
||
# Do not allow overlapping time periods.
|
||
# Start time can be equal to another end time
|
||
# (an end time can be equal to another start time)
|
||
|
||
# We cannot check if no staff member is given.
|
||
return if !$self->staff_member_id;
|
||
|
||
# If no start time and no end time are given, there is no overlapping.
|
||
return if !($self->start_time || $self->end_time);
|
||
|
||
my $conflicting;
|
||
|
||
# Start time or end time can be undefined.
|
||
if (!$self->start_time) {
|
||
$conflicting = SL::DB::Manager::TimeRecording->get_all(where => [ and => [ '!id' => $self->id,
|
||
staff_member_id => $self->staff_member_id,
|
||
start_time => {lt => $self->end_time},
|
||
end_time => {ge => $self->end_time} ] ],
|
||
sort_by => 'start_time DESC',
|
||
limit => 1);
|
||
} elsif (!$self->end_time) {
|
||
$conflicting = SL::DB::Manager::TimeRecording->get_all(where => [ and => [ '!id' => $self->id,
|
||
staff_member_id => $self->staff_member_id,
|
||
or => [ and => [start_time => {le => $self->start_time},
|
||
end_time => {gt => $self->start_time} ],
|
||
start_time => $self->start_time,
|
||
],
|
||
],
|
||
],
|
||
sort_by => 'start_time DESC',
|
||
limit => 1);
|
||
} else {
|
||
$conflicting = SL::DB::Manager::TimeRecording->get_all(where => [ and => [ '!id' => $self->id,
|
||
staff_member_id => $self->staff_member_id,
|
||
or => [ and => [ start_time => {lt => $self->end_time},
|
||
end_time => {gt => $self->start_time} ] ,
|
||
or => [ start_time => $self->start_time,
|
||
end_time => $self->end_time, ],
|
||
]
|
||
]
|
||
],
|
||
sort_by => 'start_time DESC',
|
||
limit => 1);
|
||
}
|
||
|
||
return $conflicting->[0] if @$conflicting;
|
||
return;
|
||
}
|
||
|
||
sub is_time_in_wrong_order {
|
||
my ($self) = @_;
|
||
|
||
if ($self->start_time && $self->end_time
|
||
&& $self->start_time >= $self->end_time) {
|
||
return 1;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
sub displayable_times {
|
||
my ($self) = @_;
|
||
|
||
# placeholder
|
||
my $ph = $::locale->format_date_object(DateTime->new(year => 1111, month => 11, day => 11, hour => 11, minute => 11), precision => 'minute');
|
||
$ph =~ s{1}{-}g;
|
||
|
||
return ($self->start_time_as_timestamp||$ph) . ' - ' . ($self->end_time_as_timestamp||$ph);
|
||
}
|
||
|
||
sub duration {
|
||
my ($self) = @_;
|
||
|
||
if ($self->start_time && $self->end_time) {
|
||
return ($self->end_time->subtract_datetime_absolute($self->start_time))->seconds/60.0;
|
||
} else {
|
||
return;
|
||
}
|
||
}
|
||
|
||
1;
|
SL/Dev/ALL.pm | ||
---|---|---|
use SL::Dev::Record;
|
||
use SL::Dev::Payment;
|
||
use SL::Dev::Shop;
|
||
use SL::Dev::TimeRecording;
|
||
|
||
sub import {
|
||
no strict "refs";
|
||
for (qw(Part CustomerVendor Inventory Record Payment Shop)) {
|
||
for (qw(Part CustomerVendor Inventory Record Payment Shop TimeRecording)) {
|
||
Exporter::export_to_level("SL::Dev::$_", 1, @_);
|
||
}
|
||
}
|
SL/Dev/TimeRecording.pm | ||
---|---|---|
package SL::Dev::TimeRecording;
|
||
|
||
use strict;
|
||
use base qw(Exporter);
|
||
our @EXPORT_OK = qw(new_time_recording);
|
||
our %EXPORT_TAGS = (ALL => \@EXPORT_OK);
|
||
|
||
use DateTime;
|
||
|
||
use SL::DB::TimeRecording;
|
||
|
||
use SL::DB::Employee;
|
||
use SL::Dev::CustomerVendor qw(new_customer);
|
||
|
||
|
||
sub new_time_recording {
|
||
my (%params) = @_;
|
||
|
||
my $customer = delete $params{customer} // new_customer(name => 'Testcustomer')->save;
|
||
die "illegal customer" unless defined $customer && ref($customer) eq 'SL::DB::Customer';
|
||
|
||
my $employee = $params{employee} // SL::DB::Manager::Employee->current;
|
||
my $staff_member = $params{staff_member} // $employee;
|
||
|
||
my $now = DateTime->now_local;
|
||
|
||
my $time_recording = SL::DB::TimeRecording->new(
|
||
start_time => $now,
|
||
end_time => $now->add(hours => 1),
|
||
customer => $customer,
|
||
description => '<p>this and that</p>',
|
||
staff_member => $staff_member,
|
||
employee => $employee,
|
||
%params,
|
||
);
|
||
|
||
return $time_recording;
|
||
}
|
||
|
||
|
||
1;
|
js/kivi.TimeRecording.js | ||
---|---|---|
namespace('kivi.TimeRecording', function(ns) {
|
||
'use strict';
|
||
|
||
ns.set_end_date = function() {
|
||
if ($('#start_date').val() !== '' && $('#end_date').val() === '') {
|
||
var kivi_start_date = kivi.format_date(kivi.parse_date($('#start_date').val()));
|
||
$('#end_date').val(kivi_start_date);
|
||
}
|
||
};
|
||
|
||
ns.set_current_date_time = function(what) {
|
||
if (what !== 'start' && what !== 'end') return;
|
||
|
||
var $date = $('#' + what + '_date');
|
||
var $time = $('#' + what + '_time');
|
||
var date = new Date();
|
||
|
||
$date.val(kivi.format_date(date));
|
||
$time.val(kivi.format_time(date));
|
||
};
|
||
|
||
});
|
menus/user/10-time-recording.yaml | ||
---|---|---|
params:
|
||
action: SimpleSystemSetting/list
|
||
type: time_recording_type
|
||
- parent: productivity
|
||
id: productivity_time_recording
|
||
name: Time Recording
|
||
order: 350
|
||
params:
|
||
action: TimeRecording/edit
|
||
- parent: productivity_reports
|
||
id: productivity_reports_time_recording
|
||
name: Time Recording
|
||
order: 300
|
||
params:
|
||
action: TimeRecording/list
|
t/db/time_recordig.t | ||
---|---|---|
use Test::More tests => 40;
|
||
|
||
use strict;
|
||
|
||
use lib 't';
|
||
use utf8;
|
||
|
||
use Support::TestSetup;
|
||
use Test::Exception;
|
||
use DateTime;
|
||
|
||
use_ok 'SL::DB::TimeRecording';
|
||
|
||
use SL::Dev::ALL qw(:ALL);
|
||
|
||
Support::TestSetup::login();
|
||
|
||
my @time_recordings;
|
||
my ($s1, $e1, $s2, $e2);
|
||
|
||
sub clear_up {
|
||
foreach (qw(TimeRecording Customer)) {
|
||
"SL::DB::Manager::${_}"->delete_all(all => 1);
|
||
}
|
||
SL::DB::Manager::Employee->delete_all(where => [ '!login' => 'unittests' ]);
|
||
};
|
||
|
||
########################################
|
||
|
||
$s1 = DateTime->now_local;
|
||
$e1 = $s1->clone;
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1);
|
||
|
||
ok( $time_recordings[0]->is_time_in_wrong_order, 'same start and end detected' );
|
||
ok( !$time_recordings[0]->is_time_overlapping, 'not overlapping if only one time recording entry in db' );
|
||
|
||
###
|
||
$time_recordings[0]->end_time(undef);
|
||
ok( !$time_recordings[0]->is_time_in_wrong_order, 'order ok if no end' );
|
||
|
||
########################################
|
||
# ------------s1-----e1-----
|
||
# --s2---e2-----------------
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 11, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2)->save;
|
||
|
||
ok( !$time_recordings[0]->is_time_overlapping, 'not overlapping: completely before 1' );
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: completely before 2' );
|
||
|
||
|
||
# -------s1-----e1----------
|
||
# --s2---e2-----------------
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2)->save;
|
||
|
||
ok( !$time_recordings[0]->is_time_overlapping, 'not overlapping: before 1' );
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: before 2' );
|
||
|
||
# ---s1-----e1--------------
|
||
# ---------------s2---e2----
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 13, minute => 0);
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2)->save;
|
||
|
||
ok( !$time_recordings[0]->is_time_overlapping, 'not overlapping: completely after 1' );
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: completely after 2' );
|
||
|
||
# ---s1-----e1--------------
|
||
# ----------s2---e2---------
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2)->save;
|
||
|
||
ok( !$time_recordings[0]->is_time_overlapping, 'not overlapping: after 1' );
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: after 2' );
|
||
|
||
# -------s1-----e1----------
|
||
# ---s2-----e2--------------
|
||
# -> overlaps
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 9, minute => 0);
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: start before, end inbetween' );
|
||
|
||
# -------s1-----e1----------
|
||
# -----------s2-----e2------
|
||
# -> overlaps
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: start inbetween, end after' );
|
||
|
||
# ---s1---------e1----------
|
||
# ------s2---e2-------------
|
||
# -> overlaps
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: completely inbetween' );
|
||
|
||
|
||
# ------s1---e1-------------
|
||
# ---s2---------e2----------
|
||
# -> overlaps
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: completely oudside' );
|
||
|
||
|
||
# ---s1---e1----------------
|
||
# ---s2---------e2----------
|
||
# -> overlaps
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: same start, end outside' );
|
||
|
||
# ---s1------e1-------------
|
||
# ------s2---e2-------------
|
||
# -> overlaps
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: start after, same end' );
|
||
|
||
# ---s1------e1-------------
|
||
# ------s2------------------
|
||
# e2 undef
|
||
# -> overlaps
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e2 = undef;
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: start inbetween, no end' );
|
||
|
||
# ---s1------e1-------------
|
||
# ---s2---------------------
|
||
# e2 undef
|
||
# -> overlaps
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e2 = undef;
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: same start, no end' );
|
||
|
||
# -------s1------e1---------
|
||
# ---s2---------------------
|
||
# e2 undef
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e2 = undef;
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: start before, no end' );
|
||
|
||
# -------s1------e1---------
|
||
# -------------------s2-----
|
||
# e2 undef
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 16, minute => 0);
|
||
$e2 = undef;
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: start after, no end' );
|
||
|
||
# -------s1------e1---------
|
||
# ---------------s2---------
|
||
# e2 undef
|
||
# -> does not overlap
|
||
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
$e2 = undef;
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: same start as other end, no end' );
|
||
|
||
# -------s1------e1---------
|
||
# -----------e2-------------
|
||
# s2 undef
|
||
# -> overlaps
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0);
|
||
$s2 = undef;
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: no start, end inbetween' );
|
||
|
||
# -------s1------e1---------
|
||
# ---------------e2---------
|
||
# s2 undef
|
||
# -> overlaps
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0);
|
||
$s2 = undef;
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: no start, same end' );
|
||
|
||
# -------s1------e1---------
|
||
# --e2----------------------
|
||
# s2 undef
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0);
|
||
$s2 = undef;
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no start, end before' );
|
||
|
||
# -------s1------e1---------
|
||
# -------------------e2-----
|
||
# s2 undef
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0);
|
||
$s2 = undef;
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no start, end after' );
|
||
|
||
# -------s1------e1---------
|
||
# -------e2-----------------
|
||
# s2 undef
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0);
|
||
$s2 = undef;
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no start, same end as other start' );
|
||
|
||
# ----s1--------------------
|
||
# ----s2-----e2-------------
|
||
# e1 undef
|
||
# -> overlaps
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = undef;
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: no end in db, same start' );
|
||
|
||
# --------s1----------------
|
||
# ----s2-----e2-------------
|
||
# e1 undef
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = undef;
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, enclosing' );
|
||
|
||
# ---s1---------------------
|
||
# ---------s2-----e2--------
|
||
# e1 undef
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e1 = undef;
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, completely after' );
|
||
|
||
# ---------s1---------------
|
||
# --------------------------
|
||
# e1, s2, e2 undef
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = undef;
|
||
$s2 = undef;
|
||
$e2 = undef;
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, no times in object' );
|
||
|
||
# ---------s1---------------
|
||
# -----s2-------------------
|
||
# e1, e2 undef
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = undef;
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e2 = undef;
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, start before, no end in object' );
|
||
|
||
# ---------s1---------------
|
||
# -------------s2-----------
|
||
# e1, e2 undef
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = undef;
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
$e2 = undef;
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, start after, no end in object' );
|
||
|
||
# ---------s1---------------
|
||
# ---------s2---------------
|
||
# e1, e2 undef
|
||
# -> overlaps
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = undef;
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e2 = undef;
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: no end in db, same start' );
|
||
|
||
# ---------s1---------------
|
||
# ---e2---------------------
|
||
# e1, s2 undef
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = undef;
|
||
$s2 = undef;
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, no start in object, end before' );
|
||
|
||
# ---------s1---------------
|
||
# ---------------e2---------
|
||
# e1, s2 undef
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = undef;
|
||
$s2 = undef;
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, no start in object, end after' );
|
||
|
||
# ---------s1---------------
|
||
# ---------e2---------------
|
||
# e1, s2 undef
|
||
# -> does not overlap
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
$e1 = undef;
|
||
$s2 = undef;
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, no start in object, same end' );
|
||
|
||
########################################
|
||
# not overlapping if different staff_member
|
||
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0);
|
||
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0);
|
||
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 11, minute => 0);
|
||
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0);
|
||
|
||
clear_up;
|
||
|
||
@time_recordings = ();
|
||
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save;
|
||
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2);
|
||
|
||
ok( $time_recordings[1]->is_time_overlapping, 'overlapping if same staff member' );
|
||
$time_recordings[1]->update_attributes(staff_member => SL::DB::Employee->new(
|
||
'login' => 'testuser',
|
||
'name' => 'Test User',
|
||
)->save);
|
||
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping if different staff member' );
|
||
|
||
clear_up;
|
||
|
||
1;
|
||
|
||
|
||
# set emacs to perl mode
|
||
# Local Variables:
|
||
# mode: perl
|
||
# End:
|
templates/webpages/time_recording/_filter.html | ||
---|---|---|
[%- USE T8 %]
|
||
[%- USE L %]
|
||
[%- USE LxERP %]
|
||
[%- USE HTML %]
|
||
<form action='controller.pl' method='post' id='filter_form'>
|
||
<div class='filter_toggle'>
|
||
<a href='#' onClick='javascript:$(".filter_toggle").toggle()'>[% 'Show Filter' | $T8 %]</a>
|
||
[% SELF.filter_summary | html %]
|
||
</div>
|
||
<div class='filter_toggle' style='display:none'>
|
||
<a href='#' onClick='javascript:$(".filter_toggle").toggle()'>[% 'Hide Filter' | $T8 %]</a>
|
||
<table id='filter_table'>
|
||
<tr>
|
||
<th align="right">[% 'Start' | $T8 %] [% 'From Date' | $T8 %]</th>
|
||
<td>[% L.date_tag('filter.start_time:date::ge', filter.start_time_date__ge) %]</td>
|
||
</tr>
|
||
<tr>
|
||
<th align="right">[% 'Start' | $T8 %] [% 'To Date' | $T8 %]</th>
|
||
<td>[% L.date_tag('filter.start_time:date::le', filter.start_time_date__le) %]</td>
|
||
</tr>
|
||
<tr>
|
||
<th align="right">[% 'Customer' | $T8 %]</th>
|
||
<td>[% L.input_tag('filter.customer.name:substr::ilike', filter.customer.name_substr__ilike, size = 20) %]</td>
|
||
</tr>
|
||
<tr>
|
||
<th align="right">[% 'Customer Number' | $T8 %]</th>
|
||
<td>[% L.input_tag('filter.customer.customernumber:substr::ilike', filter.customer.customernumber_substr__ilike, size = 20) %]</td>
|
||
</tr>
|
||
<tr>
|
||
<th align="right">[% 'Mitarbeiter' | $T8 %]</th>
|
||
<td>
|
||
[% L.select_tag('filter.staff_member_id', SELF.all_employees,
|
||
default => filter.staff_member_id,
|
||
title_key => 'name',
|
||
value_key => 'id',
|
||
with_empty => 1,
|
||
style => 'width: 200px') %]
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
|
||
[% L.hidden_tag('sort_by', FORM.sort_by) %]
|
||
[% L.hidden_tag('sort_dir', FORM.sort_dir) %]
|
||
[% L.hidden_tag('page', FORM.page) %]
|
||
[% L.button_tag('$("#filter_form").clearForm()', LxERP.t8('Reset')) %]
|
||
</div>
|
||
|
||
</form>
|
templates/webpages/time_recording/form.html | ||
---|---|---|
[% USE L %]
|
||
[% USE P %]
|
||
[% USE T8 %]
|
||
[% USE LxERP %]
|
||
|
||
<h1>[% title %]</h1>
|
||
|
||
[%- INCLUDE 'common/flash.html' %]
|
||
|
||
<form method="post" action="controller.pl" id="form">
|
||
[% P.hidden_tag('id', SELF.time_recording.id) %]
|
||
[% L.hidden_tag('callback', FORM.callback) %]
|
||
|
||
<table>
|
||
<thead class="listheading">
|
||
<th>[% 'Start' | T8 %]</th>
|
||
<th>[% 'End' | T8 %]</th>
|
||
<th>[% 'Customer' | T8 %]</th>
|
||
<th>[% 'Type' | T8 %]</th>
|
||
<th>[% 'Project' | T8 %]</th>
|
||
<th>[% 'Description' | T8 %]</th>
|
||
</thead>
|
||
<tbody valign="top">
|
||
<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.button_tag('kivi.TimeRecording.set_current_date_time("start")', LxERP.t8('now')) %]
|
||
</td>
|
||
<td>
|
||
[% P.date_tag('end_date', SELF.end_date) %]<br>
|
||
[% P.input_tag('end_time', SELF.end_time, type="time") %]
|
||
[% P.button_tag('kivi.TimeRecording.set_current_date_time("end")', LxERP.t8('now')) %]
|
||
</td>
|
||
<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.type_id', SELF.all_time_recording_types, default=SELF.time_recording.type.id, with_empty=1, title_key='abbreviation') %]</td>
|
||
<td>[% P.project.picker('time_recording.project_id', SELF.time_recording.project_id, style='width: 300px') %]</td>
|
||
<td>[% L.textarea_tag('time_recording.description', SELF.time_recording.description, wrap="soft", style="width: 350px; height: 150px", class="texteditor", "data-validate"="required", "data-title"=LxERP.t8('Description')) %]</td>
|
||
</tbody>
|
||
</table>
|
||
|
||
</form>
|
templates/webpages/time_recording/report_bottom.html | ||
---|---|---|
[% USE HTML%]
|
||
[%- USE T8 %]
|
||
[%- USE L %][%- USE LxERP -%]
|
||
[% L.paginate_controls(models=SELF.models) %]
|
||
<input type="hidden" name="rowcount" value="[% HTML.escape(rowcount) %]">
|
||
[%- FOREACH item = HIDDEN %]
|
||
<input type="hidden" name="[% HTML.escape(item.key) %]" value="[% HTML.escape(item.value) %]">
|
||
[%- END %]
|
||
</form>
|
templates/webpages/time_recording/report_top.html | ||
---|---|---|
[%- PROCESS 'time_recording/_filter.html' filter=SELF.models.filtered.laundered %]
|
||
<hr>
|
Auch abrufbar als: Unified diff
Zeiterfassung: Controller