From c43a663ca1dfc5403f99c1d2d00a7fc3a49ec1f7 Mon Sep 17 00:00:00 2001 From: Tomas Cohen Arazi Date: Tue, 12 Aug 2025 14:25:37 -0300 Subject: [PATCH] Bug 40636: Add `Koha::Calendar->has_business_days_between()` method This patch adds a new method to Koha::Calendar that returns a boolean indicating whether there are any business days between two given dates (exclusive). Implementation details: * Returns 1 if any business days exist between start and end dates * Returns 0 for consecutive days, same dates, or only holidays between * Uses exclusive range (does not include start or end dates) * Handles date comparison and timezone normalization * Efficient early return when first business day is found This is designed specifically for use cases like detecting missed business days due to server downtime or scheduling issues. To test: 1. Apply patch 2. Run: $ ktd --shell k$ cd /kohadevbox/koha k$ prove t/db_dependent/Koha/Calendar.t => SUCCESS: All tests pass demonstrating correct behavior 3. Sign off :-D --- Koha/Calendar.pm | 38 +++++++++ t/db_dependent/Koha/Calendar.t | 138 +++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100755 t/db_dependent/Koha/Calendar.t diff --git a/Koha/Calendar.pm b/Koha/Calendar.pm index 5223220c86e..29ab104f00d 100644 --- a/Koha/Calendar.pm +++ b/Koha/Calendar.pm @@ -383,6 +383,28 @@ sub hours_between { return $duration; } +sub has_business_days_between { + my ( $self, $start_dt, $end_dt ) = @_; + + # Clone dates to avoid modifying originals + my $current_date = $start_dt->clone->set_time_zone('floating'); + my $end_date = $end_dt->clone->set_time_zone('floating'); + + # Ensure start is before end + if ( $current_date->compare($end_date) >= 0 ) { + return 0; # Same date or start after end + } + + # Check each day between start and end (exclusive) + $current_date->add( days => 1 ); + while ( $current_date->compare($end_date) < 0 ) { + return 1 unless $self->is_holiday($current_date); + $current_date->add( days => 1 ); + } + + return 0; # No business days found +} + sub set_daysmode { my ( $self, $mode ) = @_; @@ -506,6 +528,22 @@ relative order of the parameters. Note: This routine assumes neither the passed start_dt nor end_dt can be a closed day +=head2 has_business_days_between + +$boolean = $calendar->has_business_days_between($start_dt, $end_dt); + +Passed two DateTime objects, returns 1 if there are any business days (non-holidays) +between the start and end dates (exclusive), 0 otherwise. + +This method is useful for determining if business days were missed between two dates, +such as when checking if server downtime caused business days to be skipped. + +Examples: +- Monday to Wednesday with Tuesday as business day: returns 1 +- Monday to Tuesday (consecutive days): returns 0 +- Friday to Sunday with Saturday as holiday: returns 0 +- Same date: returns 0 + =head2 next_open_days $datetime = $calendar->next_open_days($duedate_dt, $to_add) diff --git a/t/db_dependent/Koha/Calendar.t b/t/db_dependent/Koha/Calendar.t new file mode 100755 index 00000000000..1971e1de851 --- /dev/null +++ b/t/db_dependent/Koha/Calendar.t @@ -0,0 +1,138 @@ +#!/usr/bin/perl + +# Copyright 2025 Koha Development team +# +# This file is part of Koha +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use Test::More tests => 3; +use Test::Exception; +use Test::NoWarnings; + +use Koha::Database; +use Koha::DateUtils qw( dt_from_string ); +use t::lib::TestBuilder; + +BEGIN { + use_ok('Koha::Calendar'); +} + +my $schema = Koha::Database->schema; +my $builder = t::lib::TestBuilder->new; + +subtest 'has_business_days_between' => sub { + + plan tests => 6; + + $schema->storage->txn_begin; + + my $library = $builder->build_object( { class => 'Koha::Libraries' } ); + my $branchcode = $library->branchcode; + + # Create test dates + my $monday = dt_from_string('2024-01-01'); # Monday + my $tuesday = dt_from_string('2024-01-02'); # Tuesday + my $wednesday = dt_from_string('2024-01-03'); # Wednesday + my $thursday = dt_from_string('2024-01-04'); # Thursday + my $friday = dt_from_string('2024-01-05'); # Friday + my $saturday = dt_from_string('2024-01-06'); # Saturday + my $sunday = dt_from_string('2024-01-07'); # Sunday + + # Make Wednesday a holiday + my $wednesday_holiday = $builder->build( + { + source => 'SpecialHoliday', + value => { + branchcode => $branchcode, + day => $wednesday->day, + month => $wednesday->month, + year => $wednesday->year, + title => 'Wednesday Holiday', + isexception => 0 + }, + } + ); + + # Make Saturday and Sunday holidays (weekend) + my $saturday_holiday = $builder->build( + { + source => 'SpecialHoliday', + value => { + branchcode => $branchcode, + day => $saturday->day, + month => $saturday->month, + year => $saturday->year, + title => 'Saturday Holiday', + isexception => 0 + }, + } + ); + + my $sunday_holiday = $builder->build( + { + source => 'SpecialHoliday', + value => { + branchcode => $branchcode, + day => $sunday->day, + month => $sunday->month, + year => $sunday->year, + title => 'Sunday Holiday', + isexception => 0 + }, + } + ); + + my $calendar = Koha::Calendar->new( branchcode => $branchcode ); + + # Test 1: Business day between two business days + is( + $calendar->has_business_days_between( $monday, $wednesday ), 1, + 'Should find business day (Tuesday) between Monday and Wednesday' + ); + + # Test 2: No business days between consecutive business days + is( + $calendar->has_business_days_between( $monday, $tuesday ), 0, + 'Should find no business days between consecutive days' + ); + + # Test 3: Holiday between two business days + is( + $calendar->has_business_days_between( $tuesday, $thursday ), 0, + 'Should find no business days when only holiday (Wednesday) is between' + ); + + # Test 4: Multiple days with business days + is( + $calendar->has_business_days_between( $monday, $friday ), 1, + 'Should find business days between Monday and Friday' + ); + + # Test 5: Only holidays between dates + is( + $calendar->has_business_days_between( $friday, $sunday ), 0, + 'Should find no business days between Friday and Sunday (Saturday is holiday)' + ); + + # Test 6: Same date + is( + $calendar->has_business_days_between( $monday, $monday ), 0, + 'Should find no business days between same date' + ); + + $schema->storage->txn_rollback; +}; -- 2.50.1