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