@@ -, +, @@ tests --- C4/Circulation.pm | 66 +++++++++++++++++- t/db_dependent/Circulation/CalcDateDue.t | 85 +++++++++++++++++++++++- 2 files changed, 145 insertions(+), 6 deletions(-) --- a/C4/Circulation.pm +++ a/C4/Circulation.pm @@ -63,6 +63,7 @@ use Koha::SearchEngine::Indexer; use Koha::Exceptions::Checkout; use Koha::Plugins; use Koha::Recalls; +use Koha::Library::Hours; use Carp qw( carp ); use List::MoreUtils qw( any ); use Scalar::Util qw( looks_like_number blessed ); @@ -3891,11 +3892,44 @@ sub CalcDateDue { } ); + my $considerlibraryhours = C4::Context->preference('ConsiderLibraryHoursInCirculation'); + + # starter vars so don't do calculations directly to $datedue + my $potential_datedue = $datedue->clone; + my $library_close = $datedue->clone; + my $dayofweek = $datedue->local_day_of_week - 1; + my $todayhours = Koha::Library::Hours->find({ library_id => $branch, day => $dayofweek }); + my @close = undef; + my $tomorrowhours = Koha::Library::Hours->find({ library_id => $branch, day => $dayofweek+1 }); # get open hours of next day + my @open = undef; + if ( $todayhours->close_time and $tomorrowhours->open_time ) { + @close = split( ":", $todayhours->close_time ); + $library_close = $library_close->set( hour => $close[0], minute => $close[1] ); + $potential_datedue = $potential_datedue->add( hours => $loanlength->{$length_key} ); # datedue without consideration for open hours + @open = split( ":", $tomorrowhours->open_time ); + } + # calculate the datedue as normal if ( $daysmode eq 'Days' ) { # ignoring calendar if ( $loanlength->{lengthunit} eq 'hours' ) { - $datedue->add( hours => $loanlength->{$length_key} ); + if ( $potential_datedue > $library_close and $todayhours->close_time and $tomorrowhours->open_time ) { + if ( $considerlibraryhours eq 'close' ) { + # datedue will be after the library closes on that day + # shorten loan period to end when library closes + $datedue->set( hour => $close[0], minute => $close[1] ); + } elsif ( $considerlibraryhours eq 'open' ) { + # datedue will be after the library closes on that day + # extend loan period to when library opens following day + $datedue->add( days => 1 )->set( hour => $open[0], minute => $open[1] ); + } else { + # ignore library open hours + $datedue->add( hours => $loanlength->{$length_key} ); + } + } else { + # due time doesn't conflict with library open hours, don't need to check + $datedue->add( hours => $loanlength->{$length_key} ); + } } else { # days $datedue->add( days => $loanlength->{$length_key} ); $datedue->set_hour(23); @@ -3903,17 +3937,43 @@ sub CalcDateDue { } } else { my $dur; + my $sethours; if ($loanlength->{lengthunit} eq 'hours') { - $dur = DateTime::Duration->new( hours => $loanlength->{$length_key}); + if ( $potential_datedue > $library_close and $todayhours->close_time and $tomorrowhours->open_time ) { + if ( $considerlibraryhours eq 'close' ) { + # datedue will be after the library closes on that day + # shorten loan period to end when library closes + $dur = $potential_datedue->delta_ms( $library_close ); + $sethours = $considerlibraryhours; + } elsif ( $considerlibraryhours eq 'open' ) { + # datedue will be after the library closes on that day + # extend loan period to when library opens following day + my $library_open = $datedue->clone->set( hour => $open[0], minute => $open[1] ); + $dur = $potential_datedue->delta_ms( $library_open )->add( days => 1 ); + $sethours = $considerlibraryhours; + } else { + # ignore library open hours + $dur = DateTime::Duration->new( hours => $loanlength->{$length_key} ); + } + } else { + # due time doesn't conflict with library open hours, don't need to check + $dur = DateTime::Duration->new( hours => $loanlength->{$length_key} ); + } } else { # days - $dur = DateTime::Duration->new( days => $loanlength->{$length_key}); + $dur = DateTime::Duration->new( days => $loanlength->{$length_key} ); } my $calendar = Koha::Calendar->new( branchcode => $branch, days_mode => $daysmode ); $datedue = $calendar->addDuration( $datedue, $dur, $loanlength->{lengthunit} ); if ($loanlength->{lengthunit} eq 'days') { $datedue->set_hour(23); $datedue->set_minute(59); + } else { + if ( $sethours and $sethours eq 'close' ) { + $datedue->set( hour => $close[0], minute => $close[1] ); + } elsif ( $sethours and $sethours eq 'open' ) { + $datedue->set( hour => $open[0], minute => $open[1] ); + } } } --- a/t/db_dependent/Circulation/CalcDateDue.t +++ a/t/db_dependent/Circulation/CalcDateDue.t @@ -2,14 +2,15 @@ use Modern::Perl; -use Test::More tests => 19; +use Test::More tests => 23; use Test::MockModule; use DBI; use DateTime; use t::lib::Mocks; use t::lib::TestBuilder; use C4::Calendar qw( new insert_single_holiday delete_holiday insert_week_day_holiday ); - +use Koha::DateUtils qw( dt_from_string ); +use Koha::Library::Hours; use Koha::CirculationRules; use_ok('C4::Circulation', qw( CalcDateDue )); @@ -18,7 +19,7 @@ my $schema = Koha::Database->new->schema; $schema->storage->txn_begin; my $builder = t::lib::TestBuilder->new; - +t::lib::Mocks::mock_preference( 'ConsiderLibraryHoursInCirculation', 'ignore' ); my $library = $builder->build_object({ class => 'Koha::Libraries' })->store; my $dateexpiry = '2013-01-01'; my $patron_category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { category_type => 'B' } })->store; @@ -356,5 +357,83 @@ my $renewed_date = $start_date->clone->add( days => 7 ); $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower, 1 ); is( $date->ymd, $renewed_date->ymd, 'Renewal period of "" should trigger fallover to issuelength for renewal' ); +# Testing hourly loans consider library open hours + +my $library1 = $builder->build( { source => 'Branch' } ); +Koha::CirculationRules->set_rules( + { + categorycode => $categorycode, + itemtype => $itemtype, + branchcode => $library1->{branchcode}, + rules => { + issuelength => 3, # loan period is 3 hours + lengthunit => 'hours', + } + } +); + +my $open = DateTime->now->subtract( hours => 4 )->hms; +my $close = DateTime->now->add( hours => 2 )->hms; +my $now = DateTime->now; + +foreach (0..6) { + # library opened 4 hours ago and closes in 2 hours. + Koha::Library::Hour->new({ day => $_, library_id => $library1->{branchcode}, open_time => $open, close_time => $close })->store; +} + +# ignore calendar +t::lib::Mocks::mock_preference('useDaysMode', 'Days'); +t::lib::Mocks::mock_preference('ConsiderLibraryHoursInCirculation', 'close'); +# shorten loan period + +$date = C4::Circulation::CalcDateDue( $now, $itemtype, $library1->{branchcode}, $borrower ); +my $expected_duetime = DateTime->now->add( hours => 2 ); +is( $date, $expected_duetime, "Loan period was shortened because ConsiderLibraryHoursInCirculation is set to close time" ); + +t::lib::Mocks::mock_preference('ConsiderLibraryHoursWhenIssuing', 'open'); +# extend loan period + +$date = C4::Circulation::CalcDateDue( $now, $itemtype, $library1->{branchcode}, $borrower ); +$expected_duetime = DateTime->now->add( days => 1 )->subtract( hours => 4 ); +is( $date, $expected_duetime, "Loan period was extended because ConsiderLibraryHoursInCirculation is set to open time" ); + +my $holiday_tomorrow = DateTime->now->add( days => 1 ); + +# consider calendar +my $library1_calendar = C4::Calendar->new( branchcode => $library1->{branchcode} ); +$library1_calendar->insert_single_holiday( + day => $holiday_tomorrow->day, + month => $holiday_tomorrow->month, + year => $holiday_tomorrow->year, + title => 'testholiday', + description => 'testholiday' +); +Koha::CirculationRules->set_rules( + { + categorycode => $categorycode, + itemtype => $itemtype, + branchcode => $library1->{branchcode}, + rules => { + issuelength => 13, # loan period must cross over into tomorrow + lengthunit => 'hours', + } + } +); + +t::lib::Mocks::mock_preference('useDaysMode', 'Calendar'); +t::lib::Mocks::mock_preference('ConsiderLibraryHoursInCirculation', 'close'); +# shorten loan period + +$date = C4::Circulation::CalcDateDue( $now, $itemtype, $library1->{branchcode}, $borrower ); +$expected_duetime = DateTime->now->add( days => 2, hours => 2 ); +is( $date, $expected_duetime, "Loan period was shortened (but considers the holiday) because ConsiderLibraryHoursInCirculation is set to close time" ); + +t::lib::Mocks::mock_preference( 'ConsiderLibraryHoursInCirculation', 'open' ); +# extend loan period + +$date = C4::Circulation::CalcDateDue( $now, $itemtype, $library1->{branchcode}, $borrower ); +$expected_duetime = DateTime->now->add( days => 2 )->subtract( hours => 4 ); +is( $date, $expected_duetime, "Loan period was extended (but considers the holiday) because ConsiderLibraryHoursInCirculation is set to open time" ); + $cache->clear_from_cache($key); $schema->storage->txn_rollback; --