| File: | Koha/Calendar.pm |
| Coverage: | 61.2% |
| line | stmt | bran | cond | sub | time | code |
|---|---|---|---|---|---|---|
| 1 | package 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 | ||||||
| 13 | sub 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 | ||||||
| 32 | sub _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 | ||||||
| 85 | sub 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 | ||||||
| 141 | sub 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 | ||||||
| 167 | sub 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 | ||||||
| 188 | sub _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 | ||||||
| 207 | 1; | |||||