From 4b23e5c5d1802593e4b68e1fc872ea329aecb11f Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Thu, 16 Apr 2026 09:54:18 +0100 Subject: [PATCH] Bug 41410: (follow-up) Decouple exclusivity from LocalHoldsPriority MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LocalHoldsExclusivity and LocalHoldsPriority are adjacent but distinct features: priority alters *which patron's hold* gets filled first, while exclusivity restricts *which items* may fill a hold during a time window. Each should be usable independently of the other. Changes: - Rename LocalHoldsPriorityExclusivityPeriod to LocalHoldsExclusivityPeriod - Add LocalHoldsExclusivityPatronControl (PickupLibrary/HomeLibrary) - Add LocalHoldsExclusivityItemControl (homebranch/holdingbranch) - HoldsQueue: add _flag_local_holdgroup_matches() that flags local_holdgroup_match on allocated items using hold-group matching and the new exclusivity control prefs, regardless of LocalHoldsPriority - Reserves: CheckReserves exclusivity check now computes its own "is this item local" using the new prefs and hold-group siblings, independent of the LocalHoldsPriority matching variables - Atomicupdate handles both fresh install and rename-from-old-pref paths - Pref UI note clarifies independence from LocalHoldsPriority Test plan: 1. Apply patch, run updatedatabase.pl 2. Verify three new sysprefs exist in Administration > System preferences > Circulation: LocalHoldsExclusivityPeriod, LocalHoldsExclusivityPatronControl, LocalHoldsExclusivityItemControl 3. Set LocalHoldsPriority to "Don't give" (None) 4. Create a library hold group with at least two libraries (e.g. Lib-A, Lib-B) 5. Create a third library (Lib-C) NOT in the hold group 6. Create an item at Lib-B 7. Place a hold for a patron at Lib-A (pickup at Lib-A) 8. Set LocalHoldsExclusivityPeriod to 7, PatronControl to HomeLibrary, ItemControl to homebranch 9. Run the holds queue builder (misc/cronjobs/holds/build_holds_queue.pl) 10. Verify hold_fill_targets.local_holdgroup_match = 1 for that hold (item at Lib-B is in the same hold group as patron at Lib-A) 11. Check in the item at Lib-C — it should NOT fill the hold (exclusivity blocks the non-local item within the 7-day window) 12. Check in the item at Lib-B — it SHOULD fill the hold (local match) 13. Reset: delete the hold, create a new one with a patron at Lib-C (pickup at Lib-C). Run the queue again. 14. Verify hold_fill_targets.local_holdgroup_match = 0 for this hold 15. The item at Lib-B should now fill this hold at Lib-C checkin (non-local patron, no exclusivity protection) 16. Set LocalHoldsExclusivityPeriod to 0 — verify non-local items can fill holds normally regardless of hold_fill_targets flag 17. prove t/db_dependent/Holds/LocalHoldsPriority.t — passes 18. prove t/db_dependent/HoldsQueue.t — passes 19. prove t/db_dependent/Reserves.t — passes Signed-off-by: Trevor Diamond Signed-off-by: Andrew Fuerste Henry Signed-off-by: Pedro Amorim --- C4/HoldsQueue.pm | 60 +++++++++- C4/Reserves.pm | 55 ++++++--- .../data/mysql/atomicupdate/bug_41410.pl | 58 ++++++++-- installer/data/mysql/mandatory/sysprefs.sql | 4 +- .../admin/preferences/circulation.pref | 16 ++- t/db_dependent/Holds/LocalHoldsPriority.t | 42 ++++--- t/db_dependent/HoldsQueue.t | 107 +++++++++++++++++- 7 files changed, 291 insertions(+), 51 deletions(-) diff --git a/C4/HoldsQueue.pm b/C4/HoldsQueue.pm index 8a23493142..7cf6575af8 100644 --- a/C4/HoldsQueue.pm +++ b/C4/HoldsQueue.pm @@ -911,6 +911,7 @@ sub MapItemsToHoldRequests { $item_map{ $allocation->[0] } = $allocation->[1]; $num_items_remaining--; } + _flag_local_holdgroup_matches( \%item_map, \%items_by_itemnumber ); return \%item_map; } @@ -922,7 +923,10 @@ sub MapItemsToHoldRequests { push @{ $items_by_branch{ $item->{holdingbranch} } }, $item unless exists $allocated_items{ $item->{itemnumber} }; } - return \%item_map unless keys %items_by_branch; + unless ( keys %items_by_branch ) { + _flag_local_holdgroup_matches( \%item_map, \%items_by_itemnumber ); + return \%item_map; + } # now handle the title-level requests $num_items_remaining = scalar(@$available_items) - scalar( keys %allocated_items ); @@ -1034,9 +1038,63 @@ sub MapItemsToHoldRequests { $num_items_remaining--; } } + _flag_local_holdgroup_matches( \%item_map, \%items_by_itemnumber ); return \%item_map; } +=head2 _flag_local_holdgroup_matches + + _flag_local_holdgroup_matches( $item_map, $items_by_itemnumber ); + +For each mapped item that does not already have local_holdgroup_match set, +check whether the item and the patron belong to the same library hold group +using the LocalHoldsExclusivity* system preferences. This runs independently +of LocalHoldsPriority so that the exclusivity feature can operate on its own. + +=cut + +sub _flag_local_holdgroup_matches { + my ( $item_map, $items_by_itemnumber ) = @_; + + my $excl_period = C4::Context->preference('LocalHoldsExclusivityPeriod'); + return unless $excl_period; + + my $patron_control = C4::Context->preference('LocalHoldsExclusivityPatronControl') || 'PickupLibrary'; + my $item_control = C4::Context->preference('LocalHoldsExclusivityItemControl') || 'holdingbranch'; + + foreach my $itemnumber ( keys %$item_map ) { + my $mapped = $item_map->{$itemnumber}; + next if $mapped->{local_holdgroup_match}; + + my $item_branchcode = + $items_by_itemnumber->{$itemnumber} + ? $items_by_itemnumber->{$itemnumber}->{$item_control} + : undef; + + unless ($item_branchcode) { + my $item_obj = Koha::Items->find($itemnumber); + $item_branchcode = $item_obj->$item_control if $item_obj; + } + next unless $item_branchcode; + + my $patron_branchcode = + ( $patron_control eq 'PickupLibrary' ) ? $mapped->{pickup_branch} + : ( $patron_control eq 'HomeLibrary' ) ? Koha::Patrons->find( $mapped->{borrowernumber} )->branchcode + : undef; + next unless $patron_branchcode; + + if ( $item_branchcode eq $patron_branchcode ) { + $mapped->{local_holdgroup_match} = 1; + next; + } + + my $item_library = Koha::Libraries->find( { branchcode => $item_branchcode } ); + if ( $item_library && $item_library->validate_hold_sibling( { branchcode => $patron_branchcode } ) ) { + $mapped->{local_holdgroup_match} = 1; + } + } +} + =head2 _can_item_fill_request my $bool = _can_item_fill_request( $item, $request, $libraries ); diff --git a/C4/Reserves.pm b/C4/Reserves.pm index 1abef7ac71..4f4fdb9275 100644 --- a/C4/Reserves.pm +++ b/C4/Reserves.pm @@ -895,27 +895,50 @@ sub CheckReserves { } } - # Local holds priority exclusivity period: when the queue has + # Local holds exclusivity period: when the queue has # targeted a local-group item for this hold and we're still # within the exclusivity window, skip non-local items so the # targeted local item can fulfil it. If the queue has not yet # run for this reserve (no hold_fill_targets row), we fall # through and let the hold be filled normally. - my $excl_period = C4::Context->preference('LocalHoldsPriorityExclusivityPeriod'); - if ( $excl_period - && !$local_hold_match - && !$local_hold_group_match - && $res->{reserve_id} ) - { - my $reservedate_dt = eval { dt_from_string( $res->{reservedate} ) }; - if ($reservedate_dt) { - my $age_days = dt_from_string->delta_days($reservedate_dt)->in_units('days'); - if ( $age_days < $excl_period ) { - my $target = - Koha::Database->new->schema->resultset('HoldFillTarget') - ->search( { reserve_id => $res->{reserve_id} }, { rows => 1 } ) - ->next; - next if $target && $target->local_holdgroup_match; + # This is independent of LocalHoldsPriority. + my $excl_period = C4::Context->preference('LocalHoldsExclusivityPeriod'); + if ( $excl_period && $res->{reserve_id} ) { + my $excl_item_control = + C4::Context->preference('LocalHoldsExclusivityItemControl') || 'holdingbranch'; + my $excl_patron_control = + C4::Context->preference('LocalHoldsExclusivityPatronControl') || 'PickupLibrary'; + + my $excl_item_branchcode = $item->$excl_item_control; + $patron //= Koha::Patrons->find( $res->{borrowernumber} ); + my $excl_patron_branchcode = + ( $excl_patron_control eq 'PickupLibrary' ) ? $res->{branchcode} + : ( $excl_patron_control eq 'HomeLibrary' ) ? $patron->branchcode + : undef; + + my $is_local_for_exclusivity = 0; + if ( $excl_item_branchcode && $excl_patron_branchcode ) { + if ( $excl_item_branchcode eq $excl_patron_branchcode ) { + $is_local_for_exclusivity = 1; + } else { + my $excl_library = Koha::Libraries->find( { branchcode => $excl_item_branchcode } ); + $is_local_for_exclusivity = 1 + if $excl_library + && $excl_library->validate_hold_sibling( { branchcode => $excl_patron_branchcode } ); + } + } + + if ( !$is_local_for_exclusivity ) { + my $reservedate_dt = eval { dt_from_string( $res->{reservedate} ) }; + if ($reservedate_dt) { + my $age_days = dt_from_string->delta_days($reservedate_dt)->in_units('days'); + if ( $age_days < $excl_period ) { + my $target = + Koha::Database->new->schema->resultset('HoldFillTarget') + ->search( { reserve_id => $res->{reserve_id} }, { rows => 1 } ) + ->next; + next if $target && $target->local_holdgroup_match; + } } } } diff --git a/installer/data/mysql/atomicupdate/bug_41410.pl b/installer/data/mysql/atomicupdate/bug_41410.pl index adf4893f20..195f60cfba 100755 --- a/installer/data/mysql/atomicupdate/bug_41410.pl +++ b/installer/data/mysql/atomicupdate/bug_41410.pl @@ -3,7 +3,7 @@ use Koha::Installer::Output qw(say_success say_info say_warning); return { bug_number => "41410", - description => "Add local holds priority exclusivity period", + description => "Add local holds exclusivity period", up => sub { my ($args) = @_; my ( $dbh, $out ) = @$args{qw(dbh out)}; @@ -36,23 +36,61 @@ return { say_success( $out, "Added column 'tmp_holdsqueue.local_holdgroup_match'" ); } + # Rename old pref if it exists (upgrade path) + my ($old_exists) = $dbh->selectrow_array( + q{SELECT COUNT(*) FROM systempreferences WHERE variable = 'LocalHoldsPriorityExclusivityPeriod'}); + if ($old_exists) { + $dbh->do( + q{UPDATE systempreferences SET variable = 'LocalHoldsExclusivityPeriod', + explanation = 'For this many days after a hold is placed, only items flagged as a local hold group match by the holds queue may fill the hold. Set to 0 to disable.' + WHERE variable = 'LocalHoldsPriorityExclusivityPeriod'} + ); + say_success( + $out, + "Renamed system preference 'LocalHoldsPriorityExclusivityPeriod' to 'LocalHoldsExclusivityPeriod'" + ); + } else { + $dbh->do( + q{ + INSERT IGNORE INTO systempreferences (variable, value, options, explanation, type) + VALUES ( + 'LocalHoldsExclusivityPeriod', + '0', + NULL, + 'For this many days after a hold is placed, only items flagged as a local hold group match by the holds queue may fill the hold. Set to 0 to disable.', + 'Integer' + ) + } + ); + say_success( $out, "Added new system preference 'LocalHoldsExclusivityPeriod'" ); + } + $dbh->do( q{ INSERT IGNORE INTO systempreferences (variable, value, options, explanation, type) VALUES ( - 'LocalHoldsPriorityExclusivityPeriod', - '0', - NULL, - 'When LocalHoldsPriority is set, for this many days after a hold is placed, only items flagged as a local hold group match by the holds queue may fill the hold. Set to 0 to disable.', - 'Integer' + 'LocalHoldsExclusivityPatronControl', + 'PickupLibrary', + 'PickupLibrary|HomeLibrary', + 'Determine local hold group match for exclusivity by comparing the hold pickup library or the patron home library.', + 'Choice' ) } ); - say_success( $out, "Added new system preference 'LocalHoldsPriorityExclusivityPeriod'" ); + say_success( $out, "Added new system preference 'LocalHoldsExclusivityPatronControl'" ); - say_info( - $out, - "The LocalHoldsPriorityExclusivityPeriod feature only takes effect when LocalHoldsPriority is set to something other than 'None'." + $dbh->do( + q{ + INSERT IGNORE INTO systempreferences (variable, value, options, explanation, type) + VALUES ( + 'LocalHoldsExclusivityItemControl', + 'holdingbranch', + 'homebranch|holdingbranch', + 'Determine local hold group match for exclusivity by comparing the item home library or holding library.', + 'Choice' + ) + } ); + say_success( $out, "Added new system preference 'LocalHoldsExclusivityItemControl'" ); }, }; diff --git a/installer/data/mysql/mandatory/sysprefs.sql b/installer/data/mysql/mandatory/sysprefs.sql index 0e546d9434..00e5754310 100644 --- a/installer/data/mysql/mandatory/sysprefs.sql +++ b/installer/data/mysql/mandatory/sysprefs.sql @@ -420,8 +420,10 @@ INSERT INTO systempreferences ( `variable`, `value` ) VALUES ('LoadCheckoutsTableDelay','0'), ('LoadSearchHistoryToTheFirstLoggedUser', '1'), ('LocalCoverImages','0'), +('LocalHoldsExclusivityItemControl', 'holdingbranch'), +('LocalHoldsExclusivityPatronControl', 'PickupLibrary'), +('LocalHoldsExclusivityPeriod', '0'), ('LocalHoldsPriority', 'None'), -('LocalHoldsPriorityExclusivityPeriod', '0'), ('LocalHoldsPriorityItemControl', 'holdingbranch'), ('LocalHoldsPriorityPatronControl', 'PickupLibrary'), ('LockExpiredDelay',''), diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref index a27cb4b6b8..c0484d7b55 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref @@ -1105,10 +1105,20 @@ Circulation: -
Note: If a group option is selected, LocalHoldsPriorityPatronControl and LocalHoldsPriorityItemControl will use the library hold group of the selected libraries. - - For - - pref: LocalHoldsPriorityExclusivityPeriod + - pref: LocalHoldsExclusivityPeriod class: integer - - days after a hold is placed, only items flagged by the holds queue as a local hold group match may be used to fill the hold. Set to 0 to disable. - -
Note: Only takes effect when LocalHoldsPriority is set to something other than 'Don't give'. + - days after a hold is placed, only items whose + - pref: LocalHoldsExclusivityItemControl + choices: + homebranch: "home library" + holdingbranch: "holding library" + - is in the same library hold group as the patron's + - pref: LocalHoldsExclusivityPatronControl + choices: + PickupLibrary: "pickup library" + HomeLibrary: "home library" + - may be used to fill the hold. Set to 0 to disable. + -
Note: This feature is independent of LocalHoldsPriority. It restricts which items can fill a hold rather than altering the priority order. - - pref: OPACHoldsIfAvailableAtPickup choices: diff --git a/t/db_dependent/Holds/LocalHoldsPriority.t b/t/db_dependent/Holds/LocalHoldsPriority.t index c3e19485e4..2de5a9d9d4 100755 --- a/t/db_dependent/Holds/LocalHoldsPriority.t +++ b/t/db_dependent/Holds/LocalHoldsPriority.t @@ -392,16 +392,19 @@ subtest "exclude from local holds" => sub { $schema->storage->txn_rollback; }; -subtest 'LocalHoldsPriorityExclusivityPeriod' => sub { - plan tests => 4; +subtest 'LocalHoldsExclusivityPeriod' => sub { + plan tests => 5; $schema->storage->txn_begin; my $dbh = C4::Context->dbh; - t::lib::Mocks::mock_preference( 'LocalHoldsPriority', 'GiveLibraryGroup' ); - t::lib::Mocks::mock_preference( 'LocalHoldsPriorityPatronControl', 'HomeLibrary' ); - t::lib::Mocks::mock_preference( 'LocalHoldsPriorityItemControl', 'homebranch' ); + # Exclusivity is independent of LocalHoldsPriority — disable it to prove that. + t::lib::Mocks::mock_preference( 'LocalHoldsPriority', 'None' ); + + # Configure exclusivity control prefs + t::lib::Mocks::mock_preference( 'LocalHoldsExclusivityPatronControl', 'HomeLibrary' ); + t::lib::Mocks::mock_preference( 'LocalHoldsExclusivityItemControl', 'homebranch' ); my $group = Koha::Library::Group->new( { title => "Excl period group", ft_local_hold_group => 1 } )->store(); @@ -419,18 +422,16 @@ subtest 'LocalHoldsPriorityExclusivityPeriod' => sub { my $biblio = $builder->build_sample_biblio(); my $local_item = $builder->build_sample_item( { - biblionumber => $biblio->biblionumber, - homebranch => $other_lib_in->branchcode, - holdingbranch => $other_lib_in->branchcode, - exclude_from_local_holds_priority => 0, + biblionumber => $biblio->biblionumber, + homebranch => $other_lib_in->branchcode, + holdingbranch => $other_lib_in->branchcode, } ); my $non_local_item = $builder->build_sample_item( { - biblionumber => $biblio->biblionumber, - homebranch => $non_local_lib->branchcode, - holdingbranch => $non_local_lib->branchcode, - exclude_from_local_holds_priority => 0, + biblionumber => $biblio->biblionumber, + homebranch => $non_local_lib->branchcode, + holdingbranch => $non_local_lib->branchcode, } ); @@ -469,7 +470,7 @@ subtest 'LocalHoldsPriorityExclusivityPeriod' => sub { ); # 1. Pref disabled (0): the non-local item may still fill the hold. - t::lib::Mocks::mock_preference( 'LocalHoldsPriorityExclusivityPeriod', 0 ); + t::lib::Mocks::mock_preference( 'LocalHoldsExclusivityPeriod', 0 ); my ( undef, $reserve ) = CheckReserves($non_local_item); is( $reserve->{reserve_id}, $reserve_id, @@ -477,14 +478,21 @@ subtest 'LocalHoldsPriorityExclusivityPeriod' => sub { ); # 2. Pref active, hold is fresh: non-local item must be skipped. - t::lib::Mocks::mock_preference( 'LocalHoldsPriorityExclusivityPeriod', 7 ); + t::lib::Mocks::mock_preference( 'LocalHoldsExclusivityPeriod', 7 ); ( undef, $reserve ) = CheckReserves($non_local_item); ok( !$reserve, "Non-local item is blocked by exclusivity period when queue targeted a local match" ); - # 3. Push reservedate back past the exclusivity window: should fill again. + # 3. The local-group item itself should still fill the hold during the window. + ( undef, $reserve ) = CheckReserves($local_item); + is( + $reserve->{reserve_id}, $reserve_id, + "Local-group item fills the hold even during the exclusivity window" + ); + + # 4. Push reservedate back past the exclusivity window: should fill again. $dbh->do( q{UPDATE reserves SET reservedate = DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY) WHERE reserve_id = ?}, undef, $reserve_id @@ -495,7 +503,7 @@ subtest 'LocalHoldsPriorityExclusivityPeriod' => sub { "Non-local item fills the hold once the exclusivity period has expired" ); - # 4. Remove the hold_fill_targets row: with no queue target we must not block. + # 5. Remove the hold_fill_targets row: with no queue target we must not block. $dbh->do( q{UPDATE reserves SET reservedate = CURRENT_DATE WHERE reserve_id = ?}, undef, $reserve_id ); $dbh->do( q{DELETE FROM hold_fill_targets WHERE reserve_id = ?}, undef, $reserve_id ); ( undef, $reserve ) = CheckReserves($non_local_item); diff --git a/t/db_dependent/HoldsQueue.t b/t/db_dependent/HoldsQueue.t index 316bedfcf0..c651cba3cf 100755 --- a/t/db_dependent/HoldsQueue.t +++ b/t/db_dependent/HoldsQueue.t @@ -9,7 +9,7 @@ use Modern::Perl; use Test::NoWarnings; -use Test::More tests => 67; +use Test::More tests => 68; use Data::Dumper; use C4::Calendar; @@ -471,7 +471,7 @@ is( ); is( $holds_queue->[0]->{local_holdgroup_match}, 1, - "tmp_holdsqueue.local_holdgroup_match is set when LocalHoldsPriority finds a local match" + "tmp_holdsqueue.local_holdgroup_match is set for a local match" ); my $hft_row = $dbh->selectrow_hashref( "SELECT local_holdgroup_match FROM hold_fill_targets WHERE borrowernumber = ?", @@ -479,7 +479,7 @@ my $hft_row = $dbh->selectrow_hashref( ); is( $hft_row->{local_holdgroup_match}, 1, - "hold_fill_targets.local_holdgroup_match is set when LocalHoldsPriority finds a local match" + "hold_fill_targets.local_holdgroup_match is set for a local match" ); ### Test branch transfer limits ### @@ -2546,3 +2546,104 @@ subtest "Test unallocated option" => sub { "Previously allocated hold not updated when unallocated passed and others are allocated" ); }; + +subtest "local_holdgroup_match is set independently of LocalHoldsPriority" => sub { + plan tests => 4; + + $schema->storage->txn_begin; + + # Disable LocalHoldsPriority to prove independence + t::lib::Mocks::mock_preference( 'LocalHoldsPriority', 'None' ); + + # Enable exclusivity so _flag_local_holdgroup_matches runs + t::lib::Mocks::mock_preference( 'LocalHoldsExclusivityPeriod', 7 ); + t::lib::Mocks::mock_preference( 'LocalHoldsExclusivityPatronControl', 'HomeLibrary' ); + t::lib::Mocks::mock_preference( 'LocalHoldsExclusivityItemControl', 'homebranch' ); + + my $group = $builder->build_object( { class => 'Koha::Library::Groups', value => { ft_local_hold_group => 1 } } ); + + my $lib_a = $builder->build_object( { class => 'Koha::Libraries' } ); + my $lib_b = $builder->build_object( { class => 'Koha::Libraries' } ); + my $lib_c = $builder->build_object( { class => 'Koha::Libraries' } ); # not in group + + $builder->build_object( + { class => 'Koha::Library::Groups', value => { parent_id => $group->id, branchcode => $lib_a->branchcode } } ); + $builder->build_object( + { class => 'Koha::Library::Groups', value => { parent_id => $group->id, branchcode => $lib_b->branchcode } } ); + + Koha::CirculationRules->set_rule( + { + rule_name => 'holdallowed', + rule_value => 'from_any_library', + branchcode => undef, + itemtype => undef, + } + ); + + # Use a single item at lib_b so the allocation is deterministic. + # lib_b is in the hold group with lib_a but not with lib_c. + my $biblio = $builder->build_sample_biblio(); + my $item = $builder->build_sample_item( + { + biblionumber => $biblio->biblionumber, + homebranch => $lib_b->branchcode, + holdingbranch => $lib_b->branchcode, + } + ); + + my $patron_local = + $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $lib_a->branchcode } } ); + my $patron_non_local = + $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $lib_c->branchcode } } ); + + # Hold by the local patron (lib_a, in same group as item at lib_b) + my $reserve_id_local = AddReserve( + { + branchcode => $lib_a->branchcode, + borrowernumber => $patron_local->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + } + ); + + C4::HoldsQueue::CreateQueue(); + + my $hft_local = + $schema->resultset('HoldFillTarget')->search( { reserve_id => $reserve_id_local }, { rows => 1 } )->next; + ok( $hft_local, "Hold fill target created for the local patron's hold" ); + +SKIP: { + skip "No hold_fill_target row to inspect", 1 unless $hft_local; + is( + $hft_local->local_holdgroup_match, 1, + "local_holdgroup_match is set even though LocalHoldsPriority is 'None'" + ); + } + + # Now test a non-local patron (lib_c, NOT in hold group with item at lib_b) + $schema->resultset('Reserve')->search( {} )->delete; + my $reserve_id_non_local = AddReserve( + { + branchcode => $lib_c->branchcode, + borrowernumber => $patron_non_local->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + } + ); + + C4::HoldsQueue::CreateQueue(); + + my $hft_non_local = + $schema->resultset('HoldFillTarget')->search( { reserve_id => $reserve_id_non_local }, { rows => 1 } )->next; + ok( $hft_non_local, "Hold fill target created for the non-local patron's hold" ); + +SKIP: { + skip "No hold_fill_target row to inspect", 1 unless $hft_non_local; + is( + $hft_non_local->local_holdgroup_match, 0, + "local_holdgroup_match is NOT set for a non-local match" + ); + } + + $schema->storage->txn_rollback; +};