From 9c4ecbc38fce613575bafc96aeff33a0039bbb1a Mon Sep 17 00:00:00 2001 From: Lucas Gass Date: Mon, 23 Feb 2026 23:16:20 +0000 Subject: [PATCH] Bug 41882: Enforce pickup location rules --- .../en/modules/tools/batch_modify_holds.tt | 62 +++++++++++++++++++ tools/batch_modify_holds.pl | 42 ++++++++++++- 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_modify_holds.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_modify_holds.tt index de32ade2238..7b57c36502c 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_modify_holds.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_modify_holds.tt @@ -106,6 +106,7 @@

Modified holds

+

Holds updated

@@ -154,6 +155,61 @@
+ [% IF failed_holds_obj.size %] +
+

Holds not updated

+ + + + + + + + + + + + + + + + [% FOREACH failure IN failed_holds_obj %] + [% SET hold = failure.hold %] + + + + + + + + + + + + [% END %] + +
TitlePatronPickup locationPriorityExpiration dateStatusSuspendedSuspended untilNotes
+ [% INCLUDE 'biblio-title.inc' biblio=hold.biblio link = 1 %] +
[% failure.reason %]
+
[% INCLUDE 'patron-title.inc' invert_name=1 patron=hold.borrower hide_patron_infos_if_needed=1 link_to="circulation_reserves" %][% hold.branch.branchname | html %][% hold.priority | html %][% hold.expirationdate | $KohaDates %] + [% IF hold.found == 'F' %] + Fulfilled + [% ELSIF hold.cancellationdate %] + Cancelled + [% ELSIF hold.found == 'W' %] + [% IF hold.cancellation_requests.count == 0 %] + Waiting + [% ELSE %] + Cancelled + [% END %] + [% ELSIF hold.found == 'T' %] + In transit + [% ELSE %] + Pending + [% END %] + [% IF hold.suspend %]Yes[% ELSE %]No[% END %][% IF hold.suspend_until %][% hold.suspend_until | $KohaDates %][% ELSE %]-[% END %][% hold.reservenotes | html %]
+
+ [% END %]
Return to batch hold modification @@ -648,6 +704,12 @@ pagingType: "full", autoWidth: false }, table_settings); + [% IF failed_holds_obj.size %] + $("#failed_holds").kohaTable({ + pagingType: "full", + autoWidth: false + }, table_settings); + [% END %] }); diff --git a/tools/batch_modify_holds.pl b/tools/batch_modify_holds.pl index 4ba25f9cf40..f9e70ee8ded 100755 --- a/tools/batch_modify_holds.pl +++ b/tools/batch_modify_holds.pl @@ -28,6 +28,9 @@ use C4::Output qw( output_html_with_http_headers ); use Koha::DateUtils qw( dt_from_string ); use Koha::Holds; +use Koha::Libraries; +use Koha::Policy::Holds; +use C4::Circulation qw( GetBranchItemRule ); my $input = CGI->new; my $op = $input->param('op') // q|cud-form|; @@ -59,7 +62,8 @@ if ( $op eq 'cud-form' ) { my $clear_hold_notes = $input->param('clear_hold_notes'); @hold_ids = $input->multi_param('hold_id'); - my @holds_data = (); + my @holds_data = (); + my @failed_holds = (); my $holds_to_update = Koha::Holds->search( { reserve_id => { -in => \@hold_ids } }, { join => [ "item", "biblio" ] } ); @@ -71,7 +75,39 @@ if ( $op eq 'cud-form' ) { } if ( $new_pickup_loc && ( $hold->branchcode ne $new_pickup_loc ) ) { - $hold->branchcode($new_pickup_loc)->store; + + #check if the new pickup location is allowed + my $pickup_allowed = 1; + my $item = $hold->item; + my $patron = $hold->patron; + my $branch = Koha::Policy::Holds->holds_control_library( $item, $patron ); + my $branchitemrule = GetBranchItemRule( $branch, $item->effective_itemtype ); + my $hold_fulfillment_policy = $branchitemrule->{hold_fulfillment_policy}; + my $item_library = Koha::Libraries->find( { branchcode => $item->homebranch } ); + my $reason; + + if ( $hold_fulfillment_policy eq 'holdgroup' + && !$item_library->validate_hold_sibling( { branchcode => $new_pickup_loc } ) ) + { + $pickup_allowed = 0; + $reason = "pickupNotInHoldGroup"; + } elsif ( ( $hold_fulfillment_policy eq 'homebranch' || $hold_fulfillment_policy eq 'holdingbranch' ) + && $new_pickup_loc ne $item->$hold_fulfillment_policy ) + { + $pickup_allowed = 0; + $reason = $hold_fulfillment_policy eq 'homebranch' ? 'pickupNotHomeLibrary' : 'pickupNotHoldingLibrary'; + + } elsif ( !$item->can_be_transferred( { to => Koha::Libraries->find($new_pickup_loc) } ) ) { + $pickup_allowed = 0; + $reason = 'cannotBeTransferred'; + } + + if ($pickup_allowed) { + $hold->branchcode($new_pickup_loc)->store; + } else { + push @failed_holds, { hold => $hold, reason => $reason }; + next; + } } if ( $new_suspend_status ne "" ) { @@ -104,7 +140,9 @@ if ( $op eq 'cud-form' ) { $template->param( updated_holds => to_json( \@hold_ids ), updated_holds_obj => \@holds_data, + failed_holds_obj => \@failed_holds, total_updated => scalar @holds_data, + total_failed => scalar @failed_holds, view => 'report', ); -- 2.39.5