File Coverage

File:Koha/Calendar.pm
Coverage:61.2%

linestmtbrancondsubtimecode
1package Koha::Calendar;
2
15
15
15
490
107
494
use strict;
3
15
15
15
150
115
626
use warnings;
4
15
15
15
15
15
15
950
1008
343
142
83
253
use 5.010;
5
6
15
15
15
121
70
415
use DateTime;
7
15
15
15
27909
1022730
1339
use DateTime::Set;
8
15
15
15
302
165
462
use DateTime::Duration;
9
15
15
15
203
134
343
use C4::Context;
10
15
15
15
142
128
1512
use Carp;
11
15
15
15
48211
55059
25030
use Readonly;
12
13sub new {
14
1
24
    my ( $classname, %options ) = @_;
15
1
22
    my $self = {};
16
1
27
    bless $self, $classname;
17
1
21
    for my $o_name ( keys %options ) {
18
1
19
        my $o = lc $o_name;
19
1
21
        $self->{$o} = $options{$o_name};
20    }
21
1
21
    if ( exists $options{TEST_MODE} ) {
22
1
18
        $self->_mockinit();
23
1
10
        return $self;
24    }
25
0
0
    if ( !defined $self->{branchcode} ) {
26
0
0
        croak 'No branchcode argument passed to Koha::Calendar->new';
27    }
28
0
0
    $self->_init();
29
0
0
    return $self;
30}
31
32sub _init {
33
0
0
    my $self = shift;
34
0
0
    my $branch = $self->{branchcode};
35
0
0
    my $dbh = C4::Context->dbh();
36
0
0
    my $repeat_sth = $dbh->prepare(
37'SELECT * from repeatable_holidays WHERE branchcode = ? AND ISNULL(weekday) = ?'
38    );
39
0
0
    $repeat_sth->execute( $branch, 0 );
40
0
0
    $self->{weekly_closed_days} = [ 0, 0, 0, 0, 0, 0, 0 ];
41
0
0
    Readonly::Scalar my $sunday => 7;
42
0
0
    while ( my $tuple = $repeat_sth->fetchrow_hashref ) {
43
0
0
        $self->{weekly_closed_days}->[ $tuple->{weekday} ] = 1;
44    }
45
0
0
    $repeat_sth->execute( $branch, 1 );
46
0
0
    $self->{day_month_closed_days} = {};
47
0
0
    while ( my $tuple = $repeat_sth->fetchrow_hashref ) {
48
0
0
        $self->{day_month_closed_days}->{ $tuple->{day} }->{ $tuple->{month} } =
49          1;
50    }
51
0
0
    my $special = $dbh->prepare(
52'SELECT day, month, year, title, description FROM special_holidays WHERE ( branchcode = ? ) AND (isexception = ?)'
53    );
54
0
0
    $special->execute( $branch, 1 );
55
0
0
    my $dates = [];
56
0
0
    while ( my ( $day, $month, $year, $title, $description ) =
57        $special->fetchrow ) {
58
0
0
0
0
        push @{$dates},
59          DateTime->new(
60            day => $day,
61            month => $month,
62            year => $year,
63            time_zone => C4::Context->tz()
64          )->truncate( to => 'day' );
65    }
66
0
0
    $self->{exception_holidays} =
67      DateTime::Set->from_datetimes( dates => $dates );
68
0
0
    $special->execute( $branch, 1 );
69
0
0
    $dates = [];
70
0
0
    while ( my ( $day, $month, $year, $title, $description ) =
71        $special->fetchrow ) {
72
0
0
0
0
        push @{$dates},
73          DateTime->new(
74            day => $day,
75            month => $month,
76            year => $year,
77            time_zone => C4::Context->tz()
78          )->truncate( to => 'day' );
79    }
80
0
0
    $self->{single_holidays} = DateTime::Set->from_datetimes( dates => $dates );
81
0
0
    $self->{days_mode} = C4::Context->preference('useDaysMode');
82
0
0
    return;
83}
84
85sub addDate {
86
2
23
    my ( $self, $startdate, $add_duration, $unit ) = @_;
87
2
28
    my $base_date = $startdate->clone();
88
2
54
    if ( ref $add_duration ne 'DateTime::Duration' ) {
89
2
32
        $add_duration = DateTime::Duration->new( days => $add_duration );
90    }
91
2
271
    $unit ||= q{}; # default days ?
92
2
22
    my $days_mode = $self->{days_mode};
93
2
28
    Readonly::Scalar my $return_by_hour => 10;
94
2
163
    my $day_dur = DateTime::Duration->new( days => 1 );
95
2
424
    if ( $add_duration->is_negative() ) {
96
1
32
        $day_dur->inverse();
97    }
98
2
186
    if ( $days_mode eq 'Datedue' ) {
99
100
0
0
        my $dt = $base_date + $add_duration;
101
0
0
        while ( $self->is_holiday($dt) ) {
102
103            # TODOP if hours set to 10 am
104
0
0
            $dt->add_duration($day_dur);
105
0
0
            if ( $unit eq 'hours' ) {
106
0
0
                $dt->set_hour($return_by_hour); # Staffs specific
107            }
108        }
109
0
0
        return $dt;
110    } elsif ( $days_mode eq 'Calendar' ) {
111
2
16
        if ( $unit eq 'hours' ) {
112
0
0
            $base_date->add_duration($add_duration);
113
0
0
            while ( $self->is_holiday($base_date) ) {
114
0
0
                $base_date->add_duration($day_dur);
115
116            }
117
118        } else {
119
2
25
            my $days = abs $add_duration->in_units('days');
120
2
67
            while ($days) {
121
3
52
                $base_date->add_duration($day_dur);
122
3
4280
                if ( $self->is_holiday($base_date) ) {
123
1
25
                    next;
124                } else {
125
2
13
                    --$days;
126                }
127            }
128        }
129
2
6
        if ( $unit eq 'hours' ) {
130
0
0
            my $dt = $base_date->clone()->subtract( days => 1 );
131
0
0
            if ( $self->is_holiday($dt) ) {
132
0
0
                $base_date->set_hour($return_by_hour); # Staffs specific
133            }
134        }
135
2
19
        return $base_date;
136    } else { # Days
137
0
0
        return $base_date + $add_duration;
138    }
139}
140
141sub is_holiday {
142
8
60
    my ( $self, $dt ) = @_;
143
8
84
    my $dow = $dt->day_of_week;
144
8
126
    if ( $dow == 7 ) {
145
2
28
        $dow = 0;
146    }
147
8
59
    if ( $self->{weekly_closed_days}->[$dow] == 1 ) {
148
2
36
        return 1;
149    }
150
6
47
    $dt->truncate( to => 'days' );
151
6
4533
    my $day = $dt->day;
152
6
80
    my $month = $dt->month;
153
6
74
    if ( exists $self->{day_month_closed_days}->{$month}->{$day} ) {
154
1
14
        return 1;
155    }
156
5
49
    if ( $self->{exception_holidays}->contains($dt) ) {
157
0
0
        return 1;
158    }
159
5
2498
    if ( $self->{single_holidays}->contains($dt) ) {
160
1
1065
        return 1;
161    }
162
163    # damn have to go to work after all
164
4
2714
    return 0;
165}
166
167sub days_between {
168
0
0
    my $self = shift;
169
0
0
    my $start_dt = shift;
170
0
0
    my $end_dt = shift;
171
0
0
    $start_dt->truncate( to => 'hours' );
172
0
0
    $end_dt->truncate( to => 'hours' );
173
174    # start and end should not be closed days
175
0
0
    my $duration = $end_dt - $start_dt;
176
0
0
    $start_dt->truncate( to => 'days' );
177
0
0
    $end_dt->truncate( to => 'days' );
178
0
0
    while ( DateTime->compare( $start_dt, $end_dt ) == -1 ) {
179
0
0
        $start_dt->add( days => 1 );
180
0
0
        if ( $self->is_holiday($start_dt) ) {
181
0
0
            $duration->subtract( days => 1 );
182        }
183    }
184
0
0
    return $duration;
185
186}
187
188sub _mockinit {
189
1
17
    my $self = shift;
190
1
17
    $self->{weekly_closed_days} = [ 1, 0, 0, 0, 0, 0, 0 ]; # Sunday only
191
1
19
    $self->{day_month_closed_days} = { 6 => { 16 => 1, } };
192
1
15
    my $dates = [];
193
1
23
    $self->{exception_holidays} =
194      DateTime::Set->from_datetimes( dates => $dates );
195
1
498
    my $special = DateTime->new(
196        year => 2011,
197        month => 6,
198        day => 1,
199        time_zone => 'Europe/London',
200    );
201
1
1
71915
3
    push @{$dates}, $special;
202
1
26
    $self->{single_holidays} = DateTime::Set->from_datetimes( dates => $dates );
203
1
929
    $self->{days_mode} = 'Calendar';
204
1
5
    return;
205}
206
2071;