View | Details | Raw Unified | Return to bug 40636
Collapse All | Expand All

(-)a/t/db_dependent/Reserves/CancelExpiredReserves.t (-2 / +332 lines)
Lines 1-9 Link Here
1
#!/usr/bin/perl
1
#!/usr/bin/perl
2
2
3
use Modern::Perl;
3
use Modern::Perl;
4
use Test::More tests => 5;
4
use Test::More tests => 6;
5
use Test::NoWarnings;
5
use Test::NoWarnings;
6
use Test::MockModule;
6
use Test::MockModule;
7
use Time::Fake;
7
8
8
use t::lib::Mocks;
9
use t::lib::Mocks;
9
use t::lib::TestBuilder;
10
use t::lib::TestBuilder;
Lines 254-257 subtest 'Test handling of cancellation reason if passed' => sub { Link Here
254
    is( $old_reserve->cancellation_reason, 'EXPIRED', "Hold cancellation_reason was set correctly" );
255
    is( $old_reserve->cancellation_reason, 'EXPIRED', "Hold cancellation_reason was set correctly" );
255
};
256
};
256
257
258
subtest 'Holiday logic edge cases' => sub {
259
260
    plan tests => 6;
261
262
    # This test demonstrates the bug where CancelExpiredReserves checks if TODAY is a holiday
263
    # instead of implementing proper grace period logic.
264
    #
265
    # The correct behavior should be:
266
    # - Don't run cleanup when script runs on holidays (regardless of when holds expired)
267
    # - Cancel expired holds (including those that expired on holidays) when script runs on business days
268
    # - This provides a grace period for holds that expired on holidays while ensuring cleanup happens
269
270
    # Clear any existing holidays from previous tests
271
    Koha::Caches->get_instance()->flush_all();
272
273
    my $builder = t::lib::TestBuilder->new();
274
275
    t::lib::Mocks::mock_preference( 'ExpireReservesOnHolidays',     0 );
276
    t::lib::Mocks::mock_preference( 'ExpireReservesMaxPickUpDelay', 1 );
277
278
    # Create a library for our tests
279
    my $library    = $builder->build( { source => 'Branch' } );
280
    my $branchcode = $library->{branchcode};
281
282
    # Scenario 1: Hold expired on Monday (non-holiday), script runs on Tuesday (holiday)
283
    # The hold should NOT be cancelled because we don't run cleanup on holidays
284
285
    my $monday  = dt_from_string('2024-01-15');    # Monday
286
    my $tuesday = dt_from_string('2024-01-16');    # Tuesday
287
288
    # Create a hold that expired on Monday
289
    my $hold_expired_on_monday = $builder->build(
290
        {
291
            source => 'Reserve',
292
            value  => {
293
                branchcode             => $branchcode,
294
                patron_expiration_date => $monday->ymd,
295
                expirationdate         => $monday->ymd,
296
                cancellationdate       => undef,
297
                priority               => 0,
298
                found                  => 'W',
299
            },
300
        }
301
    );
302
303
    # Make Tuesday a holiday
304
    my $tuesday_holiday = $builder->build(
305
        {
306
            source => 'SpecialHoliday',
307
            value  => {
308
                branchcode  => $branchcode,
309
                day         => $tuesday->day,
310
                month       => $tuesday->month,
311
                year        => $tuesday->year,
312
                title       => 'Tuesday Holiday',
313
                isexception => 0
314
            },
315
        }
316
    );
317
318
    # Clear cache so holiday is recognized
319
    Koha::Caches->get_instance()->flush_all();
320
321
    # Set fake time to Tuesday (the holiday)
322
    Time::Fake->offset( $tuesday->epoch );
323
324
    # The hold should NOT be cancelled because we don't run cleanup on holidays
325
    # (even though it expired on a non-holiday)
326
    CancelExpiredReserves();
327
    my $hold_check = Koha::Holds->find( $hold_expired_on_monday->{reserve_id} );
328
    ok( $hold_check, 'Hold should not be cancelled when script runs on holiday (regardless of when it expired)' );
329
330
    # Reset time
331
    Time::Fake->reset;
332
333
    # Scenario 2: Hold expired on Wednesday (holiday), script runs on Thursday (non-holiday)
334
    # The hold SHOULD be cancelled because grace period has ended (running on business day)
335
336
    my $wednesday = dt_from_string('2024-01-17');    # Wednesday
337
    my $thursday  = dt_from_string('2024-01-18');    # Thursday
338
339
    # Create a hold that expired on Wednesday
340
    my $hold_expired_on_wednesday = $builder->build(
341
        {
342
            source => 'Reserve',
343
            value  => {
344
                branchcode             => $branchcode,
345
                patron_expiration_date => $wednesday->ymd,
346
                expirationdate         => $wednesday->ymd,
347
                cancellationdate       => undef,
348
                priority               => 0,
349
                found                  => 'W',
350
            },
351
        }
352
    );
353
354
    # Make Wednesday a holiday
355
    my $wednesday_holiday = $builder->build(
356
        {
357
            source => 'SpecialHoliday',
358
            value  => {
359
                branchcode  => $branchcode,
360
                day         => $wednesday->day,
361
                month       => $wednesday->month,
362
                year        => $wednesday->year,
363
                title       => 'Wednesday Holiday',
364
                isexception => 0
365
            },
366
        }
367
    );
368
369
    # Clear cache so holiday is recognized
370
    Koha::Caches->get_instance()->flush_all();
371
372
    # Set fake time to Thursday (non-holiday)
373
    Time::Fake->offset( $thursday->epoch );
374
375
    # The hold SHOULD be cancelled because we're running on a business day
376
    # The hold SHOULD be cancelled because it expired in the past (not today)
377
    # Even though it expired on a holiday, we're running on a business day
378
    CancelExpiredReserves();
379
    $hold_check = Koha::Holds->find( $hold_expired_on_wednesday->{reserve_id} );
380
    is(
381
        $hold_check, undef,
382
        'Hold that expired on holiday in the past should be cancelled when script runs on business day'
383
    );
384
385
    # Reset time
386
    Time::Fake->reset;
387
388
    # Scenario 3: Multiple holds expired on different days, script runs on holiday
389
    # Only holds that expired on non-holidays should be cancelled
390
391
    my $friday   = dt_from_string('2024-01-19');    # Friday (non-holiday)
392
    my $saturday = dt_from_string('2024-01-20');    # Saturday (holiday)
393
    my $sunday   = dt_from_string('2024-01-21');    # Sunday (script runs today, also holiday)
394
395
    # Hold that expired on Friday (non-holiday)
396
    my $hold_expired_friday = $builder->build(
397
        {
398
            source => 'Reserve',
399
            value  => {
400
                branchcode             => $branchcode,
401
                patron_expiration_date => $friday->ymd,
402
                expirationdate         => $friday->ymd,
403
                cancellationdate       => undef,
404
                priority               => 0,
405
                found                  => 'W',
406
            },
407
        }
408
    );
409
410
    # Hold that expired on Saturday (holiday)
411
    my $hold_expired_saturday = $builder->build(
412
        {
413
            source => 'Reserve',
414
            value  => {
415
                branchcode             => $branchcode,
416
                patron_expiration_date => $saturday->ymd,
417
                expirationdate         => $saturday->ymd,
418
                cancellationdate       => undef,
419
                priority               => 0,
420
                found                  => 'W',
421
            },
422
        }
423
    );
424
425
    # Make Saturday and Sunday holidays
426
    my $saturday_holiday = $builder->build(
427
        {
428
            source => 'SpecialHoliday',
429
            value  => {
430
                branchcode  => $branchcode,
431
                day         => $saturday->day,
432
                month       => $saturday->month,
433
                year        => $saturday->year,
434
                title       => 'Saturday Holiday',
435
                isexception => 0
436
            },
437
        }
438
    );
439
440
    my $sunday_holiday = $builder->build(
441
        {
442
            source => 'SpecialHoliday',
443
            value  => {
444
                branchcode  => $branchcode,
445
                day         => $sunday->day,
446
                month       => $sunday->month,
447
                year        => $sunday->year,
448
                title       => 'Sunday Holiday',
449
                isexception => 0
450
            },
451
        }
452
    );
453
454
    # Clear cache so holidays are recognized
455
    Koha::Caches->get_instance()->flush_all();
456
457
    # Set fake time to Sunday (holiday)
458
    Time::Fake->offset( $sunday->epoch );
459
460
    CancelExpiredReserves();
461
462
    # Hold that expired on Friday (non-holiday) should NOT be cancelled because script runs on holiday
463
    my $friday_hold_check = Koha::Holds->find( $hold_expired_friday->{reserve_id} );
464
    ok(
465
        $friday_hold_check,
466
        'Hold should not be cancelled when script runs on Sunday (holiday), regardless of when it expired'
467
    );
468
469
    # Hold that expired on Saturday (holiday) should NOT be cancelled
470
    my $saturday_hold_check = Koha::Holds->find( $hold_expired_saturday->{reserve_id} );
471
    ok(
472
        $saturday_hold_check,
473
        'Hold that expired on Saturday (holiday) should not be cancelled when script runs on Sunday (holiday)'
474
    );
475
476
    # Reset time
477
    Time::Fake->reset;
478
479
    # Scenario 4: Demonstrate grace period - hold expired on holiday, script runs on business day
480
    # This shows that holds that expired on holidays DO get cancelled when script runs on business days
481
482
    my $monday_holiday   = dt_from_string('2024-01-22');    # Monday (holiday)
483
    my $tuesday_business = dt_from_string('2024-01-23');    # Tuesday (business day)
484
485
    # Create a hold that expired on Monday (holiday)
486
    my $hold_expired_monday_holiday = $builder->build(
487
        {
488
            source => 'Reserve',
489
            value  => {
490
                branchcode             => $branchcode,
491
                patron_expiration_date => $monday_holiday->ymd,
492
                expirationdate         => $monday_holiday->ymd,
493
                cancellationdate       => undef,
494
                priority               => 0,
495
                found                  => 'W',
496
            },
497
        }
498
    );
499
500
    # Make Monday a holiday
501
    my $monday_holiday_record = $builder->build(
502
        {
503
            source => 'SpecialHoliday',
504
            value  => {
505
                branchcode  => $branchcode,
506
                day         => $monday_holiday->day,
507
                month       => $monday_holiday->month,
508
                year        => $monday_holiday->year,
509
                title       => 'Monday Holiday',
510
                isexception => 0
511
            },
512
        }
513
    );
514
515
    # Clear cache so holiday is recognized
516
    Koha::Caches->get_instance()->flush_all();
517
518
    # Set fake time to Tuesday (business day)
519
    Time::Fake->offset( $tuesday_business->epoch );
520
521
    # The hold SHOULD be cancelled because it expired in the past (not today)
522
    # Even though it expired on a holiday, we're running on a business day
523
    CancelExpiredReserves();
524
    my $monday_hold_check = Koha::Holds->find( $hold_expired_monday_holiday->{reserve_id} );
525
    is(
526
        $monday_hold_check, undef,
527
        'Hold that expired on Monday (holiday) should be cancelled when script runs on Tuesday (business day)'
528
    );
529
530
    # Reset time
531
    Time::Fake->reset;
532
533
    # Scenario 5: CRITICAL BUG - Hold expired on holiday LONG AGO should be cancelled
534
    # This exposes the bug where holds that expired on holidays are never cancelled
535
536
    my $christmas_2023 = dt_from_string('2023-12-25');    # Christmas 2023 (holiday)
537
    my $august_2024    = dt_from_string('2024-08-15');    # August 2024 (business day)
538
539
    # Create a hold that expired on Christmas 2023 (8 months ago!)
540
    my $hold_expired_long_ago = $builder->build(
541
        {
542
            source => 'Reserve',
543
            value  => {
544
                branchcode             => $branchcode,
545
                patron_expiration_date => $christmas_2023->ymd,
546
                expirationdate         => $christmas_2023->ymd,
547
                cancellationdate       => undef,
548
                priority               => 0,
549
                found                  => 'W',
550
            },
551
        }
552
    );
553
554
    # Make Christmas 2023 a holiday
555
    my $christmas_holiday = $builder->build(
556
        {
557
            source => 'SpecialHoliday',
558
            value  => {
559
                branchcode  => $branchcode,
560
                day         => $christmas_2023->day,
561
                month       => $christmas_2023->month,
562
                year        => $christmas_2023->year,
563
                title       => 'Christmas',
564
                isexception => 0
565
            },
566
        }
567
    );
568
569
    # Clear cache so holiday is recognized
570
    Koha::Caches->get_instance()->flush_all();
571
572
    # Set fake time to August 2024 (business day, 8 months later)
573
    Time::Fake->offset( $august_2024->epoch );
574
575
    # This hold SHOULD be cancelled because it expired 8 months ago
576
    # Even though it expired on a holiday, it should not remain active forever
577
    CancelExpiredReserves();
578
    my $old_hold_check = Koha::Holds->find( $hold_expired_long_ago->{reserve_id} );
579
    is(
580
        $old_hold_check, undef,
581
        'Hold that expired on holiday 8 months ago should be cancelled (not remain active forever)'
582
    );
583
584
    # Reset time
585
    Time::Fake->reset;
586
};
587
257
$schema->storage->txn_rollback;
588
$schema->storage->txn_rollback;
258
- 

Return to bug 40636