Revision c553e92f
Von Bernd Bleßmann vor fast 4 Jahren hinzugefügt
SL/Controller/TimeRecording.pm | ||
---|---|---|
1 |
package SL::Controller::TimeRecording; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent qw(SL::Controller::Base); |
|
5 |
|
|
6 |
use DateTime; |
|
7 |
use English qw(-no_match_vars); |
|
8 |
use POSIX qw(strftime); |
|
9 |
|
|
10 |
use SL::Controller::Helper::GetModels; |
|
11 |
use SL::Controller::Helper::ReportGenerator; |
|
12 |
use SL::DB::Customer; |
|
13 |
use SL::DB::Employee; |
|
14 |
use SL::DB::TimeRecording; |
|
15 |
use SL::Locale::String qw(t8); |
|
16 |
use SL::ReportGenerator; |
|
17 |
|
|
18 |
use Rose::Object::MakeMethods::Generic |
|
19 |
( |
|
20 |
# scalar => [ qw() ], |
|
21 |
'scalar --get_set_init' => [ qw(time_recording models all_time_recording_types all_employees) ], |
|
22 |
); |
|
23 |
|
|
24 |
|
|
25 |
# safety |
|
26 |
#__PACKAGE__->run_before('check_auth'); |
|
27 |
|
|
28 |
# |
|
29 |
# actions |
|
30 |
# |
|
31 |
|
|
32 |
my %sort_columns = ( |
|
33 |
start_time => t8('Start'), |
|
34 |
end_time => t8('End'), |
|
35 |
customer => t8('Customer'), |
|
36 |
type => t8('Type'), |
|
37 |
project => t8('Project'), |
|
38 |
description => t8('Description'), |
|
39 |
staff_member => t8('Mitarbeiter'), |
|
40 |
duration => t8('Duration'), |
|
41 |
); |
|
42 |
|
|
43 |
sub action_list { |
|
44 |
my ($self, %params) = @_; |
|
45 |
|
|
46 |
$self->setup_list_action_bar; |
|
47 |
$self->make_filter_summary; |
|
48 |
$self->prepare_report; |
|
49 |
|
|
50 |
$self->report_generator_list_objects(report => $self->{report}, objects => $self->models->get); |
|
51 |
} |
|
52 |
|
|
53 |
sub action_edit { |
|
54 |
my ($self) = @_; |
|
55 |
|
|
56 |
$::request->{layout}->use_javascript("${_}.js") for qw(kivi.TimeRecording ckeditor/ckeditor ckeditor/adapters/jquery kivi.Validator); |
|
57 |
|
|
58 |
if ($self->time_recording->start_time) { |
|
59 |
$self->{start_date} = $self->time_recording->start_time->to_kivitendo; |
|
60 |
$self->{start_time} = $self->time_recording->start_time->to_kivitendo_time; |
|
61 |
} |
|
62 |
if ($self->time_recording->end_time) { |
|
63 |
$self->{end_date} = $self->time_recording->end_time->to_kivitendo; |
|
64 |
$self->{end_time} = $self->time_recording->end_time->to_kivitendo_time; |
|
65 |
} |
|
66 |
|
|
67 |
$self->setup_edit_action_bar; |
|
68 |
|
|
69 |
$self->render('time_recording/form', |
|
70 |
title => t8('Time Recording'), |
|
71 |
); |
|
72 |
} |
|
73 |
|
|
74 |
sub action_save { |
|
75 |
my ($self) = @_; |
|
76 |
|
|
77 |
my @errors = $self->time_recording->validate; |
|
78 |
if (@errors) { |
|
79 |
$::form->error(t8('Saving the time recording entry failed: #1', join '<br>', @errors)); |
|
80 |
return; |
|
81 |
} |
|
82 |
|
|
83 |
if ( !eval { $self->time_recording->save; 1; } ) { |
|
84 |
$::form->error(t8('Saving the time recording entry failed: #1', $EVAL_ERROR)); |
|
85 |
return; |
|
86 |
} |
|
87 |
|
|
88 |
$self->redirect_to(safe_callback()); |
|
89 |
} |
|
90 |
|
|
91 |
sub action_delete { |
|
92 |
my ($self) = @_; |
|
93 |
|
|
94 |
$self->time_recording->delete; |
|
95 |
|
|
96 |
$self->redirect_to(safe_callback()); |
|
97 |
} |
|
98 |
|
|
99 |
sub init_time_recording { |
|
100 |
my $time_recording = ($::form->{id}) ? SL::DB::TimeRecording->new(id => $::form->{id})->load |
|
101 |
: SL::DB::TimeRecording->new(start_time => DateTime->now_local); |
|
102 |
|
|
103 |
my %attributes = %{ $::form->{time_recording} || {} }; |
|
104 |
|
|
105 |
foreach my $type (qw(start end)) { |
|
106 |
if ($::form->{$type . '_date'}) { |
|
107 |
my $date = DateTime->from_kivitendo($::form->{$type . '_date'}); |
|
108 |
$attributes{$type . '_time'} = $date->clone; |
|
109 |
if ($::form->{$type . '_time'}) { |
|
110 |
my ($hour, $min) = split ':', $::form->{$type . '_time'}; |
|
111 |
$attributes{$type . '_time'}->set_hour($hour) if $hour; |
|
112 |
$attributes{$type . '_time'}->set_minute($min) if $min; |
|
113 |
} |
|
114 |
} |
|
115 |
} |
|
116 |
|
|
117 |
$attributes{staff_member_id} = $attributes{employee_id} = SL::DB::Manager::Employee->current->id; |
|
118 |
|
|
119 |
$time_recording->assign_attributes(%attributes); |
|
120 |
|
|
121 |
return $time_recording; |
|
122 |
} |
|
123 |
|
|
124 |
sub init_models { |
|
125 |
SL::Controller::Helper::GetModels->new( |
|
126 |
controller => $_[0], |
|
127 |
sorted => \%sort_columns, |
|
128 |
disable_plugin => 'paginated', |
|
129 |
with_objects => [ 'customer', 'type', 'project', 'staff_member', 'employee' ], |
|
130 |
); |
|
131 |
} |
|
132 |
|
|
133 |
sub init_all_time_recording_types { |
|
134 |
SL::DB::Manager::TimeRecordingType->get_all_sorted(query => [obsolete => 0]); |
|
135 |
} |
|
136 |
|
|
137 |
sub init_all_employees { |
|
138 |
SL::DB::Manager::Employee->get_all_sorted; |
|
139 |
} |
|
140 |
|
|
141 |
sub prepare_report { |
|
142 |
my ($self) = @_; |
|
143 |
|
|
144 |
my $report = SL::ReportGenerator->new(\%::myconfig, $::form); |
|
145 |
$self->{report} = $report; |
|
146 |
|
|
147 |
my @columns = qw(start_time end_time customer type project description staff_member duration); |
|
148 |
|
|
149 |
my %column_defs = ( |
|
150 |
start_time => { text => t8('Start'), sub => sub { $_[0]->start_time_as_timestamp }, |
|
151 |
obj_link => sub { $self->url_for(action => 'edit', 'id' => $_[0]->id, callback => $self->models->get_callback) } }, |
|
152 |
end_time => { text => t8('End'), sub => sub { $_[0]->end_time_as_timestamp }, |
|
153 |
obj_link => sub { $self->url_for(action => 'edit', 'id' => $_[0]->id, callback => $self->models->get_callback) } }, |
|
154 |
customer => { text => t8('Customer'), sub => sub { $_[0]->customer->displayable_name } }, |
|
155 |
type => { text => t8('Type'), sub => sub { $_[0]->type && $_[0]->type->abbreviation } }, |
|
156 |
project => { text => t8('Project'), sub => sub { $_[0]->project && $_[0]->project->displayable_name } }, |
|
157 |
description => { text => t8('Description'), sub => sub { $_[0]->description_as_stripped_html }, |
|
158 |
raw_data => sub { $_[0]->description_as_restricted_html }, # raw_data only used for html(?) |
|
159 |
obj_link => sub { $self->url_for(action => 'edit', 'id' => $_[0]->id, callback => $self->models->get_callback) } }, |
|
160 |
staff_member => { text => t8('Mitarbeiter'), sub => sub { $_[0]->staff_member->safe_name } }, |
|
161 |
duration => { text => t8('Duration'), sub => sub { $_[0]->duration_as_duration_string }, |
|
162 |
align => 'right'}, |
|
163 |
); |
|
164 |
|
|
165 |
$report->set_options( |
|
166 |
controller_class => 'TimeRecording', |
|
167 |
std_column_visibility => 1, |
|
168 |
output_format => 'HTML', |
|
169 |
title => t8('Time Recordings'), |
|
170 |
allow_pdf_export => 1, |
|
171 |
allow_csv_export => 1, |
|
172 |
); |
|
173 |
|
|
174 |
$report->set_columns(%column_defs); |
|
175 |
$report->set_column_order(@columns); |
|
176 |
$report->set_export_options(qw(list filter)); |
|
177 |
$report->set_options_from_form; |
|
178 |
|
|
179 |
$self->models->disable_plugin('paginated') if $report->{options}{output_format} =~ /^(pdf|csv)$/i; |
|
180 |
#$self->models->add_additional_url_params(); |
|
181 |
$self->models->finalize; |
|
182 |
$self->models->set_report_generator_sort_options(report => $report, sortable_columns => [keys %sort_columns]); |
|
183 |
|
|
184 |
$report->set_options( |
|
185 |
raw_top_info_text => $self->render('time_recording/report_top', { output => 0 }), |
|
186 |
raw_bottom_info_text => $self->render('time_recording/report_bottom', { output => 0 }, models => $self->models), |
|
187 |
attachment_basename => t8('time_recordings') . strftime('_%Y%m%d', localtime time), |
|
188 |
); |
|
189 |
} |
|
190 |
|
|
191 |
sub make_filter_summary { |
|
192 |
my ($self) = @_; |
|
193 |
|
|
194 |
my $filter = $::form->{filter} || {}; |
|
195 |
my @filter_strings; |
|
196 |
|
|
197 |
my $staff_member = $filter->{staff_member_id} ? SL::DB::Employee->new(id => $filter->{staff_member_id})->load->safe_name : ''; |
|
198 |
|
|
199 |
my @filters = ( |
|
200 |
[ $filter->{"start_time:date::ge"}, t8('From Start') ], |
|
201 |
[ $filter->{"start_time:date::le"}, t8('To Start') ], |
|
202 |
[ $filter->{"customer"}->{"name:substr::ilike"}, t8('Customer') ], |
|
203 |
[ $filter->{"customer"}->{"customernumber:substr::ilike"}, t8('Customer Number') ], |
|
204 |
[ $staff_member, t8('Mitarbeiter') ], |
|
205 |
); |
|
206 |
|
|
207 |
for (@filters) { |
|
208 |
push @filter_strings, "$_->[1]: $_->[0]" if $_->[0]; |
|
209 |
} |
|
210 |
|
|
211 |
$self->{filter_summary} = join ', ', @filter_strings; |
|
212 |
} |
|
213 |
|
|
214 |
sub setup_list_action_bar { |
|
215 |
my ($self) = @_; |
|
216 |
|
|
217 |
for my $bar ($::request->layout->get('actionbar')) { |
|
218 |
$bar->add( |
|
219 |
action => [ |
|
220 |
t8('Update'), |
|
221 |
submit => [ '#filter_form', { action => 'TimeRecording/list' } ], |
|
222 |
accesskey => 'enter', |
|
223 |
], |
|
224 |
action => [ |
|
225 |
t8('Add'), |
|
226 |
link => $self->url_for(action => 'edit', callback => $self->models->get_callback), |
|
227 |
], |
|
228 |
); |
|
229 |
} |
|
230 |
} |
|
231 |
|
|
232 |
sub setup_edit_action_bar { |
|
233 |
my ($self) = @_; |
|
234 |
|
|
235 |
for my $bar ($::request->layout->get('actionbar')) { |
|
236 |
$bar->add( |
|
237 |
action => [ |
|
238 |
t8('Save'), |
|
239 |
submit => [ '#form', { action => 'TimeRecording/save' } ], |
|
240 |
checks => [ 'kivi.validate_form' ], |
|
241 |
], |
|
242 |
action => [ |
|
243 |
t8('Delete'), |
|
244 |
submit => [ '#form', { action => 'TimeRecording/delete' } ], |
|
245 |
only_if => $self->time_recording->id, |
|
246 |
], |
|
247 |
action => [ |
|
248 |
t8('Cancel'), |
|
249 |
link => $self->url_for(safe_callback()), |
|
250 |
], |
|
251 |
); |
|
252 |
} |
|
253 |
} |
|
254 |
|
|
255 |
sub safe_callback { |
|
256 |
$::form->{callback} || (action => 'list') |
|
257 |
} |
|
258 |
|
|
259 |
1; |
SL/DB/Manager/TimeRecording.pm | ||
---|---|---|
7 | 7 |
|
8 | 8 |
use parent qw(SL::DB::Helper::Manager); |
9 | 9 |
|
10 |
use SL::DB::Helper::Sorted; |
|
11 |
|
|
10 | 12 |
sub object_class { 'SL::DB::TimeRecording' } |
11 | 13 |
|
12 | 14 |
__PACKAGE__->make_manager_methods; |
13 | 15 |
|
16 |
|
|
17 |
sub _sort_spec { |
|
18 |
return ( default => [ 'start_time', 1 ], |
|
19 |
columns => { SIMPLE => 'ALL' , |
|
20 |
customer => [ 'lower(customer.name)', ], |
|
21 |
} |
|
22 |
); |
|
23 |
} |
|
24 |
|
|
25 |
|
|
14 | 26 |
1; |
SL/DB/TimeRecording.pm | ||
---|---|---|
5 | 5 |
|
6 | 6 |
use strict; |
7 | 7 |
|
8 |
use SL::Locale::String qw(t8); |
|
9 |
|
|
10 |
use SL::DB::Helper::AttrDuration; |
|
11 |
use SL::DB::Helper::AttrHTML; |
|
12 |
|
|
8 | 13 |
use SL::DB::MetaSetup::TimeRecording; |
9 | 14 |
use SL::DB::Manager::TimeRecording; |
10 | 15 |
|
11 | 16 |
__PACKAGE__->meta->initialize; |
12 | 17 |
|
18 |
__PACKAGE__->attr_duration_minutes(qw(duration)); |
|
19 |
|
|
20 |
__PACKAGE__->attr_html('description'); |
|
21 |
|
|
22 |
__PACKAGE__->before_save('_before_save_check_valid'); |
|
23 |
|
|
24 |
sub _before_save_check_valid { |
|
25 |
my ($self) = @_; |
|
26 |
|
|
27 |
my @errors = $self->validate; |
|
28 |
return (scalar @errors == 0); |
|
29 |
} |
|
30 |
|
|
31 |
sub validate { |
|
32 |
my ($self) = @_; |
|
33 |
|
|
34 |
my @errors; |
|
35 |
|
|
36 |
push @errors, t8('Start time must not be empty.') if !$self->start_time; |
|
37 |
push @errors, t8('Customer must not be empty.') if !$self->customer_id; |
|
38 |
push @errors, t8('Staff member must not be empty.') if !$self->staff_member_id; |
|
39 |
push @errors, t8('Employee must not be empty.') if !$self->employee_id; |
|
40 |
push @errors, t8('Description must not be empty.') if !$self->description; |
|
41 |
push @errors, t8('Start time must be earlier than end time.') if $self->is_time_in_wrong_order; |
|
42 |
|
|
43 |
my $conflict = $self->is_time_overlapping; |
|
44 |
push @errors, t8('Entry overlaps with "#1".', $conflict->displayable_times) if $conflict; |
|
45 |
|
|
46 |
return @errors; |
|
47 |
} |
|
48 |
|
|
49 |
sub is_time_overlapping { |
|
50 |
my ($self) = @_; |
|
51 |
|
|
52 |
# Do not allow overlapping time periods. |
|
53 |
# Start time can be equal to another end time |
|
54 |
# (an end time can be equal to another start time) |
|
55 |
|
|
56 |
# We cannot check if no staff member is given. |
|
57 |
return if !$self->staff_member_id; |
|
58 |
|
|
59 |
# If no start time and no end time are given, there is no overlapping. |
|
60 |
return if !($self->start_time || $self->end_time); |
|
61 |
|
|
62 |
my $conflicting; |
|
63 |
|
|
64 |
# Start time or end time can be undefined. |
|
65 |
if (!$self->start_time) { |
|
66 |
$conflicting = SL::DB::Manager::TimeRecording->get_all(where => [ and => [ '!id' => $self->id, |
|
67 |
staff_member_id => $self->staff_member_id, |
|
68 |
start_time => {lt => $self->end_time}, |
|
69 |
end_time => {ge => $self->end_time} ] ], |
|
70 |
sort_by => 'start_time DESC', |
|
71 |
limit => 1); |
|
72 |
} elsif (!$self->end_time) { |
|
73 |
$conflicting = SL::DB::Manager::TimeRecording->get_all(where => [ and => [ '!id' => $self->id, |
|
74 |
staff_member_id => $self->staff_member_id, |
|
75 |
or => [ and => [start_time => {le => $self->start_time}, |
|
76 |
end_time => {gt => $self->start_time} ], |
|
77 |
start_time => $self->start_time, |
|
78 |
], |
|
79 |
], |
|
80 |
], |
|
81 |
sort_by => 'start_time DESC', |
|
82 |
limit => 1); |
|
83 |
} else { |
|
84 |
$conflicting = SL::DB::Manager::TimeRecording->get_all(where => [ and => [ '!id' => $self->id, |
|
85 |
staff_member_id => $self->staff_member_id, |
|
86 |
or => [ and => [ start_time => {lt => $self->end_time}, |
|
87 |
end_time => {gt => $self->start_time} ] , |
|
88 |
or => [ start_time => $self->start_time, |
|
89 |
end_time => $self->end_time, ], |
|
90 |
] |
|
91 |
] |
|
92 |
], |
|
93 |
sort_by => 'start_time DESC', |
|
94 |
limit => 1); |
|
95 |
} |
|
96 |
|
|
97 |
return $conflicting->[0] if @$conflicting; |
|
98 |
return; |
|
99 |
} |
|
100 |
|
|
101 |
sub is_time_in_wrong_order { |
|
102 |
my ($self) = @_; |
|
103 |
|
|
104 |
if ($self->start_time && $self->end_time |
|
105 |
&& $self->start_time >= $self->end_time) { |
|
106 |
return 1; |
|
107 |
} |
|
108 |
|
|
109 |
return; |
|
110 |
} |
|
111 |
|
|
112 |
sub displayable_times { |
|
113 |
my ($self) = @_; |
|
114 |
|
|
115 |
# placeholder |
|
116 |
my $ph = $::locale->format_date_object(DateTime->new(year => 1111, month => 11, day => 11, hour => 11, minute => 11), precision => 'minute'); |
|
117 |
$ph =~ s{1}{-}g; |
|
118 |
|
|
119 |
return ($self->start_time_as_timestamp||$ph) . ' - ' . ($self->end_time_as_timestamp||$ph); |
|
120 |
} |
|
121 |
|
|
122 |
sub duration { |
|
123 |
my ($self) = @_; |
|
124 |
|
|
125 |
if ($self->start_time && $self->end_time) { |
|
126 |
return ($self->end_time->subtract_datetime_absolute($self->start_time))->seconds/60.0; |
|
127 |
} else { |
|
128 |
return; |
|
129 |
} |
|
130 |
} |
|
131 |
|
|
13 | 132 |
1; |
SL/Dev/ALL.pm | ||
---|---|---|
9 | 9 |
use SL::Dev::Record; |
10 | 10 |
use SL::Dev::Payment; |
11 | 11 |
use SL::Dev::Shop; |
12 |
use SL::Dev::TimeRecording; |
|
12 | 13 |
|
13 | 14 |
sub import { |
14 | 15 |
no strict "refs"; |
15 |
for (qw(Part CustomerVendor Inventory Record Payment Shop)) { |
|
16 |
for (qw(Part CustomerVendor Inventory Record Payment Shop TimeRecording)) {
|
|
16 | 17 |
Exporter::export_to_level("SL::Dev::$_", 1, @_); |
17 | 18 |
} |
18 | 19 |
} |
SL/Dev/TimeRecording.pm | ||
---|---|---|
1 |
package SL::Dev::TimeRecording; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use base qw(Exporter); |
|
5 |
our @EXPORT_OK = qw(new_time_recording); |
|
6 |
our %EXPORT_TAGS = (ALL => \@EXPORT_OK); |
|
7 |
|
|
8 |
use DateTime; |
|
9 |
|
|
10 |
use SL::DB::TimeRecording; |
|
11 |
|
|
12 |
use SL::DB::Employee; |
|
13 |
use SL::Dev::CustomerVendor qw(new_customer); |
|
14 |
|
|
15 |
|
|
16 |
sub new_time_recording { |
|
17 |
my (%params) = @_; |
|
18 |
|
|
19 |
my $customer = delete $params{customer} // new_customer(name => 'Testcustomer')->save; |
|
20 |
die "illegal customer" unless defined $customer && ref($customer) eq 'SL::DB::Customer'; |
|
21 |
|
|
22 |
my $employee = $params{employee} // SL::DB::Manager::Employee->current; |
|
23 |
my $staff_member = $params{staff_member} // $employee; |
|
24 |
|
|
25 |
my $now = DateTime->now_local; |
|
26 |
|
|
27 |
my $time_recording = SL::DB::TimeRecording->new( |
|
28 |
start_time => $now, |
|
29 |
end_time => $now->add(hours => 1), |
|
30 |
customer => $customer, |
|
31 |
description => '<p>this and that</p>', |
|
32 |
staff_member => $staff_member, |
|
33 |
employee => $employee, |
|
34 |
%params, |
|
35 |
); |
|
36 |
|
|
37 |
return $time_recording; |
|
38 |
} |
|
39 |
|
|
40 |
|
|
41 |
1; |
js/kivi.TimeRecording.js | ||
---|---|---|
1 |
namespace('kivi.TimeRecording', function(ns) { |
|
2 |
'use strict'; |
|
3 |
|
|
4 |
ns.set_end_date = function() { |
|
5 |
if ($('#start_date').val() !== '' && $('#end_date').val() === '') { |
|
6 |
var kivi_start_date = kivi.format_date(kivi.parse_date($('#start_date').val())); |
|
7 |
$('#end_date').val(kivi_start_date); |
|
8 |
} |
|
9 |
}; |
|
10 |
|
|
11 |
ns.set_current_date_time = function(what) { |
|
12 |
if (what !== 'start' && what !== 'end') return; |
|
13 |
|
|
14 |
var $date = $('#' + what + '_date'); |
|
15 |
var $time = $('#' + what + '_time'); |
|
16 |
var date = new Date(); |
|
17 |
|
|
18 |
$date.val(kivi.format_date(date)); |
|
19 |
$time.val(kivi.format_time(date)); |
|
20 |
}; |
|
21 |
|
|
22 |
}); |
menus/user/10-time-recording.yaml | ||
---|---|---|
6 | 6 |
params: |
7 | 7 |
action: SimpleSystemSetting/list |
8 | 8 |
type: time_recording_type |
9 |
- parent: productivity |
|
10 |
id: productivity_time_recording |
|
11 |
name: Time Recording |
|
12 |
order: 350 |
|
13 |
params: |
|
14 |
action: TimeRecording/edit |
|
15 |
- parent: productivity_reports |
|
16 |
id: productivity_reports_time_recording |
|
17 |
name: Time Recording |
|
18 |
order: 300 |
|
19 |
params: |
|
20 |
action: TimeRecording/list |
t/db/time_recordig.t | ||
---|---|---|
1 |
use Test::More tests => 40; |
|
2 |
|
|
3 |
use strict; |
|
4 |
|
|
5 |
use lib 't'; |
|
6 |
use utf8; |
|
7 |
|
|
8 |
use Support::TestSetup; |
|
9 |
use Test::Exception; |
|
10 |
use DateTime; |
|
11 |
|
|
12 |
use_ok 'SL::DB::TimeRecording'; |
|
13 |
|
|
14 |
use SL::Dev::ALL qw(:ALL); |
|
15 |
|
|
16 |
Support::TestSetup::login(); |
|
17 |
|
|
18 |
my @time_recordings; |
|
19 |
my ($s1, $e1, $s2, $e2); |
|
20 |
|
|
21 |
sub clear_up { |
|
22 |
foreach (qw(TimeRecording Customer)) { |
|
23 |
"SL::DB::Manager::${_}"->delete_all(all => 1); |
|
24 |
} |
|
25 |
SL::DB::Manager::Employee->delete_all(where => [ '!login' => 'unittests' ]); |
|
26 |
}; |
|
27 |
|
|
28 |
######################################## |
|
29 |
|
|
30 |
$s1 = DateTime->now_local; |
|
31 |
$e1 = $s1->clone; |
|
32 |
|
|
33 |
clear_up; |
|
34 |
|
|
35 |
@time_recordings = (); |
|
36 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1); |
|
37 |
|
|
38 |
ok( $time_recordings[0]->is_time_in_wrong_order, 'same start and end detected' ); |
|
39 |
ok( !$time_recordings[0]->is_time_overlapping, 'not overlapping if only one time recording entry in db' ); |
|
40 |
|
|
41 |
### |
|
42 |
$time_recordings[0]->end_time(undef); |
|
43 |
ok( !$time_recordings[0]->is_time_in_wrong_order, 'order ok if no end' ); |
|
44 |
|
|
45 |
######################################## |
|
46 |
# ------------s1-----e1----- |
|
47 |
# --s2---e2----------------- |
|
48 |
# -> does not overlap |
|
49 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
50 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0); |
|
51 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
52 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 11, minute => 0); |
|
53 |
|
|
54 |
clear_up; |
|
55 |
|
|
56 |
@time_recordings = (); |
|
57 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
58 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2)->save; |
|
59 |
|
|
60 |
ok( !$time_recordings[0]->is_time_overlapping, 'not overlapping: completely before 1' ); |
|
61 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: completely before 2' ); |
|
62 |
|
|
63 |
|
|
64 |
# -------s1-----e1---------- |
|
65 |
# --s2---e2----------------- |
|
66 |
# -> does not overlap |
|
67 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
68 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0); |
|
69 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
70 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
71 |
|
|
72 |
clear_up; |
|
73 |
|
|
74 |
@time_recordings = (); |
|
75 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
76 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2)->save; |
|
77 |
|
|
78 |
ok( !$time_recordings[0]->is_time_overlapping, 'not overlapping: before 1' ); |
|
79 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: before 2' ); |
|
80 |
|
|
81 |
# ---s1-----e1-------------- |
|
82 |
# ---------------s2---e2---- |
|
83 |
# -> does not overlap |
|
84 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
85 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
86 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 13, minute => 0); |
|
87 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
88 |
|
|
89 |
clear_up; |
|
90 |
|
|
91 |
@time_recordings = (); |
|
92 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
93 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2)->save; |
|
94 |
|
|
95 |
ok( !$time_recordings[0]->is_time_overlapping, 'not overlapping: completely after 1' ); |
|
96 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: completely after 2' ); |
|
97 |
|
|
98 |
# ---s1-----e1-------------- |
|
99 |
# ----------s2---e2--------- |
|
100 |
# -> does not overlap |
|
101 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
102 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
103 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
104 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
105 |
|
|
106 |
clear_up; |
|
107 |
|
|
108 |
@time_recordings = (); |
|
109 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
110 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2)->save; |
|
111 |
|
|
112 |
ok( !$time_recordings[0]->is_time_overlapping, 'not overlapping: after 1' ); |
|
113 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: after 2' ); |
|
114 |
|
|
115 |
# -------s1-----e1---------- |
|
116 |
# ---s2-----e2-------------- |
|
117 |
# -> overlaps |
|
118 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
119 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0); |
|
120 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 9, minute => 0); |
|
121 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
122 |
|
|
123 |
clear_up; |
|
124 |
|
|
125 |
@time_recordings = (); |
|
126 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
127 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
128 |
|
|
129 |
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: start before, end inbetween' ); |
|
130 |
|
|
131 |
# -------s1-----e1---------- |
|
132 |
# -----------s2-----e2------ |
|
133 |
# -> overlaps |
|
134 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
135 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0); |
|
136 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
137 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0); |
|
138 |
|
|
139 |
clear_up; |
|
140 |
|
|
141 |
@time_recordings = (); |
|
142 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
143 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
144 |
|
|
145 |
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: start inbetween, end after' ); |
|
146 |
|
|
147 |
# ---s1---------e1---------- |
|
148 |
# ------s2---e2------------- |
|
149 |
# -> overlaps |
|
150 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
151 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0); |
|
152 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
153 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0); |
|
154 |
|
|
155 |
clear_up; |
|
156 |
|
|
157 |
@time_recordings = (); |
|
158 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
159 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
160 |
|
|
161 |
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: completely inbetween' ); |
|
162 |
|
|
163 |
|
|
164 |
# ------s1---e1------------- |
|
165 |
# ---s2---------e2---------- |
|
166 |
# -> overlaps |
|
167 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
168 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
169 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
170 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0); |
|
171 |
|
|
172 |
clear_up; |
|
173 |
|
|
174 |
@time_recordings = (); |
|
175 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
176 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
177 |
|
|
178 |
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: completely oudside' ); |
|
179 |
|
|
180 |
|
|
181 |
# ---s1---e1---------------- |
|
182 |
# ---s2---------e2---------- |
|
183 |
# -> overlaps |
|
184 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
185 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
186 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
187 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0); |
|
188 |
|
|
189 |
clear_up; |
|
190 |
|
|
191 |
@time_recordings = (); |
|
192 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
193 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
194 |
|
|
195 |
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: same start, end outside' ); |
|
196 |
|
|
197 |
# ---s1------e1------------- |
|
198 |
# ------s2---e2------------- |
|
199 |
# -> overlaps |
|
200 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
201 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
202 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
203 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
204 |
|
|
205 |
clear_up; |
|
206 |
|
|
207 |
@time_recordings = (); |
|
208 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
209 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
210 |
|
|
211 |
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: start after, same end' ); |
|
212 |
|
|
213 |
# ---s1------e1------------- |
|
214 |
# ------s2------------------ |
|
215 |
# e2 undef |
|
216 |
# -> overlaps |
|
217 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
218 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
219 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
220 |
$e2 = undef; |
|
221 |
|
|
222 |
clear_up; |
|
223 |
|
|
224 |
@time_recordings = (); |
|
225 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
226 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
227 |
|
|
228 |
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: start inbetween, no end' ); |
|
229 |
|
|
230 |
# ---s1------e1------------- |
|
231 |
# ---s2--------------------- |
|
232 |
# e2 undef |
|
233 |
# -> overlaps |
|
234 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
235 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
236 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
237 |
$e2 = undef; |
|
238 |
|
|
239 |
clear_up; |
|
240 |
|
|
241 |
@time_recordings = (); |
|
242 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
243 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
244 |
|
|
245 |
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: same start, no end' ); |
|
246 |
|
|
247 |
# -------s1------e1--------- |
|
248 |
# ---s2--------------------- |
|
249 |
# e2 undef |
|
250 |
# -> does not overlap |
|
251 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
252 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
253 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
254 |
$e2 = undef; |
|
255 |
|
|
256 |
clear_up; |
|
257 |
|
|
258 |
@time_recordings = (); |
|
259 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
260 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
261 |
|
|
262 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: start before, no end' ); |
|
263 |
|
|
264 |
# -------s1------e1--------- |
|
265 |
# -------------------s2----- |
|
266 |
# e2 undef |
|
267 |
# -> does not overlap |
|
268 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
269 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
270 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 16, minute => 0); |
|
271 |
$e2 = undef; |
|
272 |
|
|
273 |
clear_up; |
|
274 |
|
|
275 |
@time_recordings = (); |
|
276 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
277 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
278 |
|
|
279 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: start after, no end' ); |
|
280 |
|
|
281 |
# -------s1------e1--------- |
|
282 |
# ---------------s2--------- |
|
283 |
# e2 undef |
|
284 |
# -> does not overlap |
|
285 |
|
|
286 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
287 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
288 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
289 |
$e2 = undef; |
|
290 |
|
|
291 |
clear_up; |
|
292 |
|
|
293 |
@time_recordings = (); |
|
294 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
295 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
296 |
|
|
297 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: same start as other end, no end' ); |
|
298 |
|
|
299 |
# -------s1------e1--------- |
|
300 |
# -----------e2------------- |
|
301 |
# s2 undef |
|
302 |
# -> overlaps |
|
303 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
304 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0); |
|
305 |
$s2 = undef; |
|
306 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
307 |
|
|
308 |
clear_up; |
|
309 |
|
|
310 |
@time_recordings = (); |
|
311 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
312 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
313 |
|
|
314 |
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: no start, end inbetween' ); |
|
315 |
|
|
316 |
# -------s1------e1--------- |
|
317 |
# ---------------e2--------- |
|
318 |
# s2 undef |
|
319 |
# -> overlaps |
|
320 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
321 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0); |
|
322 |
$s2 = undef; |
|
323 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0); |
|
324 |
|
|
325 |
clear_up; |
|
326 |
|
|
327 |
@time_recordings = (); |
|
328 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
329 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
330 |
|
|
331 |
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: no start, same end' ); |
|
332 |
|
|
333 |
# -------s1------e1--------- |
|
334 |
# --e2---------------------- |
|
335 |
# s2 undef |
|
336 |
# -> does not overlap |
|
337 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
338 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0); |
|
339 |
$s2 = undef; |
|
340 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
341 |
|
|
342 |
clear_up; |
|
343 |
|
|
344 |
@time_recordings = (); |
|
345 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
346 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
347 |
|
|
348 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no start, end before' ); |
|
349 |
|
|
350 |
# -------s1------e1--------- |
|
351 |
# -------------------e2----- |
|
352 |
# s2 undef |
|
353 |
# -> does not overlap |
|
354 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
355 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0); |
|
356 |
$s2 = undef; |
|
357 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 17, minute => 0); |
|
358 |
|
|
359 |
clear_up; |
|
360 |
|
|
361 |
@time_recordings = (); |
|
362 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
363 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
364 |
|
|
365 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no start, end after' ); |
|
366 |
|
|
367 |
# -------s1------e1--------- |
|
368 |
# -------e2----------------- |
|
369 |
# s2 undef |
|
370 |
# -> does not overlap |
|
371 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
372 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0); |
|
373 |
$s2 = undef; |
|
374 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
375 |
|
|
376 |
clear_up; |
|
377 |
|
|
378 |
@time_recordings = (); |
|
379 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
380 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
381 |
|
|
382 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no start, same end as other start' ); |
|
383 |
|
|
384 |
# ----s1-------------------- |
|
385 |
# ----s2-----e2------------- |
|
386 |
# e1 undef |
|
387 |
# -> overlaps |
|
388 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
389 |
$e1 = undef; |
|
390 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
391 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
392 |
|
|
393 |
clear_up; |
|
394 |
|
|
395 |
@time_recordings = (); |
|
396 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
397 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
398 |
|
|
399 |
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: no end in db, same start' ); |
|
400 |
|
|
401 |
# --------s1---------------- |
|
402 |
# ----s2-----e2------------- |
|
403 |
# e1 undef |
|
404 |
# -> does not overlap |
|
405 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
406 |
$e1 = undef; |
|
407 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
408 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
409 |
|
|
410 |
clear_up; |
|
411 |
|
|
412 |
@time_recordings = (); |
|
413 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
414 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
415 |
|
|
416 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, enclosing' ); |
|
417 |
|
|
418 |
# ---s1--------------------- |
|
419 |
# ---------s2-----e2-------- |
|
420 |
# e1 undef |
|
421 |
# -> does not overlap |
|
422 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
423 |
$e1 = undef; |
|
424 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
425 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
426 |
|
|
427 |
clear_up; |
|
428 |
|
|
429 |
@time_recordings = (); |
|
430 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
431 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
432 |
|
|
433 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, completely after' ); |
|
434 |
|
|
435 |
# ---------s1--------------- |
|
436 |
# -------------------------- |
|
437 |
# e1, s2, e2 undef |
|
438 |
# -> does not overlap |
|
439 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
440 |
$e1 = undef; |
|
441 |
$s2 = undef; |
|
442 |
$e2 = undef; |
|
443 |
|
|
444 |
clear_up; |
|
445 |
|
|
446 |
@time_recordings = (); |
|
447 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
448 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
449 |
|
|
450 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, no times in object' ); |
|
451 |
|
|
452 |
# ---------s1--------------- |
|
453 |
# -----s2------------------- |
|
454 |
# e1, e2 undef |
|
455 |
# -> does not overlap |
|
456 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
457 |
$e1 = undef; |
|
458 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
459 |
$e2 = undef; |
|
460 |
|
|
461 |
clear_up; |
|
462 |
|
|
463 |
@time_recordings = (); |
|
464 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
465 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
466 |
|
|
467 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, start before, no end in object' ); |
|
468 |
|
|
469 |
# ---------s1--------------- |
|
470 |
# -------------s2----------- |
|
471 |
# e1, e2 undef |
|
472 |
# -> does not overlap |
|
473 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
474 |
$e1 = undef; |
|
475 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
476 |
$e2 = undef; |
|
477 |
|
|
478 |
clear_up; |
|
479 |
|
|
480 |
@time_recordings = (); |
|
481 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
482 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
483 |
|
|
484 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, start after, no end in object' ); |
|
485 |
|
|
486 |
# ---------s1--------------- |
|
487 |
# ---------s2--------------- |
|
488 |
# e1, e2 undef |
|
489 |
# -> overlaps |
|
490 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
491 |
$e1 = undef; |
|
492 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
493 |
$e2 = undef; |
|
494 |
|
|
495 |
clear_up; |
|
496 |
|
|
497 |
@time_recordings = (); |
|
498 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
499 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
500 |
|
|
501 |
ok( $time_recordings[1]->is_time_overlapping, 'overlapping: no end in db, same start' ); |
|
502 |
|
|
503 |
# ---------s1--------------- |
|
504 |
# ---e2--------------------- |
|
505 |
# e1, s2 undef |
|
506 |
# -> does not overlap |
|
507 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
508 |
$e1 = undef; |
|
509 |
$s2 = undef; |
|
510 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
511 |
|
|
512 |
clear_up; |
|
513 |
|
|
514 |
@time_recordings = (); |
|
515 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
516 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
517 |
|
|
518 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, no start in object, end before' ); |
|
519 |
|
|
520 |
# ---------s1--------------- |
|
521 |
# ---------------e2--------- |
|
522 |
# e1, s2 undef |
|
523 |
# -> does not overlap |
|
524 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
525 |
$e1 = undef; |
|
526 |
$s2 = undef; |
|
527 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
528 |
|
|
529 |
clear_up; |
|
530 |
|
|
531 |
@time_recordings = (); |
|
532 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
533 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
534 |
|
|
535 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, no start in object, end after' ); |
|
536 |
|
|
537 |
# ---------s1--------------- |
|
538 |
# ---------e2--------------- |
|
539 |
# e1, s2 undef |
|
540 |
# -> does not overlap |
|
541 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
542 |
$e1 = undef; |
|
543 |
$s2 = undef; |
|
544 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 12, minute => 0); |
|
545 |
|
|
546 |
clear_up; |
|
547 |
|
|
548 |
@time_recordings = (); |
|
549 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
550 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
551 |
|
|
552 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping: no end in db, no start in object, same end' ); |
|
553 |
|
|
554 |
######################################## |
|
555 |
# not overlapping if different staff_member |
|
556 |
$s1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 10, minute => 0); |
|
557 |
$e1 = DateTime->new(year => 2020, month => 11, day => 15, hour => 15, minute => 0); |
|
558 |
$s2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 11, minute => 0); |
|
559 |
$e2 = DateTime->new(year => 2020, month => 11, day => 15, hour => 14, minute => 0); |
|
560 |
|
|
561 |
clear_up; |
|
562 |
|
|
563 |
@time_recordings = (); |
|
564 |
push @time_recordings, new_time_recording(start_time => $s1, end_time => $e1)->save; |
|
565 |
push @time_recordings, new_time_recording(start_time => $s2, end_time => $e2); |
|
566 |
|
|
567 |
ok( $time_recordings[1]->is_time_overlapping, 'overlapping if same staff member' ); |
|
568 |
$time_recordings[1]->update_attributes(staff_member => SL::DB::Employee->new( |
|
569 |
'login' => 'testuser', |
|
570 |
'name' => 'Test User', |
|
571 |
)->save); |
|
572 |
ok( !$time_recordings[1]->is_time_overlapping, 'not overlapping if different staff member' ); |
|
573 |
|
|
574 |
clear_up; |
|
575 |
|
|
576 |
1; |
|
577 |
|
|
578 |
|
|
579 |
# set emacs to perl mode |
|
580 |
# Local Variables: |
|
581 |
# mode: perl |
|
582 |
# End: |
templates/webpages/time_recording/_filter.html | ||
---|---|---|
1 |
[%- USE T8 %] |
|
2 |
[%- USE L %] |
|
3 |
[%- USE LxERP %] |
|
4 |
[%- USE HTML %] |
|
5 |
<form action='controller.pl' method='post' id='filter_form'> |
|
6 |
<div class='filter_toggle'> |
|
7 |
<a href='#' onClick='javascript:$(".filter_toggle").toggle()'>[% 'Show Filter' | $T8 %]</a> |
|
8 |
[% SELF.filter_summary | html %] |
|
9 |
</div> |
|
10 |
<div class='filter_toggle' style='display:none'> |
|
11 |
<a href='#' onClick='javascript:$(".filter_toggle").toggle()'>[% 'Hide Filter' | $T8 %]</a> |
|
12 |
<table id='filter_table'> |
|
13 |
<tr> |
|
14 |
<th align="right">[% 'Start' | $T8 %] [% 'From Date' | $T8 %]</th> |
|
15 |
<td>[% L.date_tag('filter.start_time:date::ge', filter.start_time_date__ge) %]</td> |
|
16 |
</tr> |
|
17 |
<tr> |
|
18 |
<th align="right">[% 'Start' | $T8 %] [% 'To Date' | $T8 %]</th> |
|
19 |
<td>[% L.date_tag('filter.start_time:date::le', filter.start_time_date__le) %]</td> |
|
20 |
</tr> |
|
21 |
<tr> |
|
22 |
<th align="right">[% 'Customer' | $T8 %]</th> |
|
23 |
<td>[% L.input_tag('filter.customer.name:substr::ilike', filter.customer.name_substr__ilike, size = 20) %]</td> |
|
24 |
</tr> |
|
25 |
<tr> |
|
26 |
<th align="right">[% 'Customer Number' | $T8 %]</th> |
|
27 |
<td>[% L.input_tag('filter.customer.customernumber:substr::ilike', filter.customer.customernumber_substr__ilike, size = 20) %]</td> |
|
28 |
</tr> |
|
29 |
<tr> |
|
30 |
<th align="right">[% 'Mitarbeiter' | $T8 %]</th> |
|
31 |
<td> |
|
32 |
[% L.select_tag('filter.staff_member_id', SELF.all_employees, |
|
33 |
default => filter.staff_member_id, |
|
34 |
title_key => 'name', |
|
35 |
value_key => 'id', |
|
36 |
with_empty => 1, |
|
37 |
style => 'width: 200px') %] |
|
38 |
</td> |
|
39 |
</tr> |
|
40 |
</table> |
|
41 |
|
|
42 |
[% L.hidden_tag('sort_by', FORM.sort_by) %] |
|
43 |
[% L.hidden_tag('sort_dir', FORM.sort_dir) %] |
|
44 |
[% L.hidden_tag('page', FORM.page) %] |
|
45 |
[% L.button_tag('$("#filter_form").clearForm()', LxERP.t8('Reset')) %] |
|
46 |
</div> |
|
47 |
|
|
48 |
</form> |
templates/webpages/time_recording/form.html | ||
---|---|---|
1 |
[% USE L %] |
|
2 |
[% USE P %] |
|
3 |
[% USE T8 %] |
|
4 |
[% USE LxERP %] |
|
5 |
|
|
6 |
<h1>[% title %]</h1> |
|
7 |
|
|
8 |
[%- INCLUDE 'common/flash.html' %] |
|
9 |
|
|
10 |
<form method="post" action="controller.pl" id="form"> |
|
11 |
[% P.hidden_tag('id', SELF.time_recording.id) %] |
|
12 |
[% L.hidden_tag('callback', FORM.callback) %] |
|
13 |
|
|
14 |
<table> |
|
15 |
<thead class="listheading"> |
|
16 |
<th>[% 'Start' | T8 %]</th> |
|
17 |
<th>[% 'End' | T8 %]</th> |
|
18 |
<th>[% 'Customer' | T8 %]</th> |
|
19 |
<th>[% 'Type' | T8 %]</th> |
|
20 |
<th>[% 'Project' | T8 %]</th> |
|
21 |
<th>[% 'Description' | T8 %]</th> |
|
22 |
</thead> |
|
23 |
<tbody valign="top"> |
|
24 |
<td> |
|
25 |
[% P.date_tag('start_date', SELF.start_date, "data-validate"="required", "data-title"=LxERP.t8('Start date'), onchange='kivi.TimeRecording.set_end_date()') %]<br> |
|
26 |
[% P.input_tag('start_time', SELF.start_time, type="time", "data-validate"="required", "data-title"=LxERP.t8('Start time')) %] |
|
27 |
[% P.button_tag('kivi.TimeRecording.set_current_date_time("start")', LxERP.t8('now')) %] |
|
28 |
</td> |
|
29 |
<td> |
|
30 |
[% P.date_tag('end_date', SELF.end_date) %]<br> |
|
31 |
[% P.input_tag('end_time', SELF.end_time, type="time") %] |
|
32 |
[% P.button_tag('kivi.TimeRecording.set_current_date_time("end")', LxERP.t8('now')) %] |
|
33 |
</td> |
|
34 |
<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> |
|
35 |
<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> |
|
36 |
<td>[% P.project.picker('time_recording.project_id', SELF.time_recording.project_id, style='width: 300px') %]</td> |
|
37 |
<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> |
|
38 |
</tbody> |
|
39 |
</table> |
|
40 |
|
|
41 |
</form> |
templates/webpages/time_recording/report_bottom.html | ||
---|---|---|
1 |
[% USE HTML%] |
|
2 |
[%- USE T8 %] |
|
3 |
[%- USE L %][%- USE LxERP -%] |
|
4 |
[% L.paginate_controls(models=SELF.models) %] |
|
5 |
<input type="hidden" name="rowcount" value="[% HTML.escape(rowcount) %]"> |
|
6 |
[%- FOREACH item = HIDDEN %] |
|
7 |
<input type="hidden" name="[% HTML.escape(item.key) %]" value="[% HTML.escape(item.value) %]"> |
|
8 |
[%- END %] |
|
9 |
</form> |
templates/webpages/time_recording/report_top.html | ||
---|---|---|
1 |
[%- PROCESS 'time_recording/_filter.html' filter=SELF.models.filtered.laundered %] |
|
2 |
<hr> |
Auch abrufbar als: Unified diff
Zeiterfassung: Controller