Lines 983-992
sub CheckReserves {
Link Here
|
983 |
|
983 |
|
984 |
=head2 CancelExpiredReserves |
984 |
=head2 CancelExpiredReserves |
985 |
|
985 |
|
986 |
CancelExpiredReserves(); |
986 |
CancelExpiredReserves(); |
|
|
987 |
CancelExpiredReserves($cancellation_reason); |
987 |
|
988 |
|
988 |
Cancels all reserves with an expiration date from before today. |
989 |
Cancels all reserves with an expiration date from before today. |
989 |
|
990 |
|
|
|
991 |
When the ExpireReservesOnHolidays system preference is disabled (0), implements |
992 |
sophisticated holiday logic to handle various edge cases: |
993 |
|
994 |
=head3 Use Cases and Logic |
995 |
|
996 |
=over 4 |
997 |
|
998 |
=item B<Case 1: Hold expires today on a holiday> |
999 |
|
1000 |
If a hold expires today and today is a holiday, the hold is NOT cancelled. |
1001 |
This respects the library's policy of not processing cancellations on holidays. |
1002 |
|
1003 |
Example: Hold expires on Christmas Day, script runs on Christmas Day => Hold preserved |
1004 |
|
1005 |
=item B<Case 2: Hold expired in the past, script runs on business day> |
1006 |
|
1007 |
If a hold expired in the past (regardless of whether the expiration date was a holiday), |
1008 |
and today is a business day, the hold IS cancelled. |
1009 |
|
1010 |
Example: Hold expired on Christmas 2023, script runs in August 2024 => Hold cancelled |
1011 |
|
1012 |
=item B<Case 3: Hold expired in the past, script runs on holiday, no business days missed> |
1013 |
|
1014 |
If a hold expired recently and today is a holiday, but no business days have passed |
1015 |
since expiration, the hold is NOT cancelled. |
1016 |
|
1017 |
Example: Hold expires Friday, script runs Saturday (holiday) => Hold preserved |
1018 |
|
1019 |
=item B<Case 4: Hold expired in the past, script runs on holiday, business days were missed> |
1020 |
|
1021 |
If a hold expired in the past and today is a holiday, but business days have passed |
1022 |
since expiration (due to server downtime, cron failures, etc.), the hold IS cancelled |
1023 |
retrospectively to prevent holds from remaining active indefinitely. |
1024 |
|
1025 |
Example: Hold expires Monday, server down Tue-Wed (business days), script runs Thursday (holiday) => Hold cancelled |
1026 |
|
1027 |
=item B<Case 5: ExpireReservesOnHolidays enabled> |
1028 |
|
1029 |
When ExpireReservesOnHolidays is enabled (1), all expired holds are cancelled |
1030 |
regardless of holidays, maintaining the original simple behavior. |
1031 |
|
1032 |
=back |
1033 |
|
1034 |
=head3 Parameters |
1035 |
|
1036 |
=over 4 |
1037 |
|
1038 |
=item $cancellation_reason (optional) |
1039 |
|
1040 |
Optional cancellation reason to be recorded in the hold's cancellation_reason field. |
1041 |
|
1042 |
=back |
1043 |
|
1044 |
This logic ensures that: |
1045 |
- Library holiday policies are respected |
1046 |
- Holds don't remain active indefinitely due to technical issues |
1047 |
- The system is robust against server downtime and cron failures |
1048 |
- Behavior is predictable and well-defined for all scenarios |
1049 |
|
990 |
=cut |
1050 |
=cut |
991 |
|
1051 |
|
992 |
sub CancelExpiredReserves { |
1052 |
sub CancelExpiredReserves { |
Lines 1011-1017
sub CancelExpiredReserves {
Link Here
|
1011 |
while ( my $hold = $holds->next ) { |
1071 |
while ( my $hold = $holds->next ) { |
1012 |
my $calendar = Koha::Calendar->new( branchcode => $hold->branchcode ); |
1072 |
my $calendar = Koha::Calendar->new( branchcode => $hold->branchcode ); |
1013 |
|
1073 |
|
1014 |
next if !$cancel_on_holidays && $calendar->is_holiday($today); |
1074 |
# Get the actual expiration date for this hold |
|
|
1075 |
my $expiration_date = $hold->expirationdate || $hold->patron_expiration_date; |
1076 |
|
1077 |
# When ExpireReservesOnHolidays is disabled, implement proper holiday logic: |
1078 |
if ( !$cancel_on_holidays && $expiration_date ) { |
1079 |
my $expiration_dt = dt_from_string($expiration_date); |
1080 |
|
1081 |
# Check if hold expired today |
1082 |
my $expired_today = $expiration_dt->ymd eq $today->ymd; |
1083 |
|
1084 |
if ($expired_today) { |
1085 |
|
1086 |
# Rule 1: Don't cancel holds that expired TODAY if today is a holiday |
1087 |
next if $calendar->is_holiday($today); |
1088 |
} else { |
1089 |
|
1090 |
# Rule 2: For holds that expired in the past, check for missed business days |
1091 |
if ( $calendar->is_holiday($today) ) { |
1092 |
|
1093 |
# If today is a holiday, check if business days were missed since expiration |
1094 |
my $days_since_expiration = $today->delta_days($expiration_dt)->in_units('days'); |
1095 |
|
1096 |
if ( $days_since_expiration > 0 ) { |
1097 |
|
1098 |
# Use Calendar's has_business_days_between for cleaner logic |
1099 |
my $business_days_missed = $calendar->has_business_days_between( $expiration_dt, $today ); |
1100 |
|
1101 |
# Rule 3: If business days were missed, cancel retrospectively even on holidays |
1102 |
if ( !$business_days_missed ) { |
1103 |
|
1104 |
# No business days were missed, skip cancellation |
1105 |
next; |
1106 |
} |
1107 |
|
1108 |
# If business days were missed, continue with cancellation |
1109 |
} else { |
1110 |
|
1111 |
# This shouldn't happen (expired_today would be true), but skip to be safe |
1112 |
next; |
1113 |
} |
1114 |
} |
1115 |
|
1116 |
# If today is a business day, cancel expired holds from the past |
1117 |
} |
1118 |
} |
1015 |
|
1119 |
|
1016 |
my $cancel_params = {}; |
1120 |
my $cancel_params = {}; |
1017 |
$cancel_params->{cancellation_reason} = $cancellation_reason if defined($cancellation_reason); |
1121 |
$cancel_params->{cancellation_reason} = $cancellation_reason if defined($cancellation_reason); |
1018 |
- |
|
|