From cfbe93c08bfd33a8c79a85db6abcd0de8fd7f869 Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Fri, 29 Aug 2025 13:55:40 +0100 Subject: [PATCH] Bug 27834: Use new CircControlCheckoutLimitScope system preference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch updates the TooMany function to use the new CircControlCheckoutLimitScope system preference, providing explicit control over how checkout limits (maxissueqty and patron_maxissueqty) are calculated, independent of the CircControl preference. Previously, the TooMany function conflated two separate concerns: 1. CircControl - which circulation rules to apply 2. Checkout counting scope - which checkouts count toward limits This caused the checkout counting behavior to change based on CircControl, even when libraries wanted consistent limit enforcement regardless of which circulation rules applied. The new CircControlCheckoutLimitScope preference offers three options: * 'all' (default) - Count all patron checkouts across all libraries * 'item' - Count only checkouts of items from the same library as the item being checked out (follows HomeOrHoldingBranch preference) * 'checkout' - Count only checkouts made at the same library as the current checkout Implementation note: When scope is 'item', the system counts checkouts of items from the same library as the item being checked out (using the item's homebranch or holdingbranch). This applies regardless of whether the matching circulation rule is branch-specific or general (branch=*), ensuring consistent behavior across all rule types. This gives libraries explicit, flexible control over checkout limit scope while maintaining CircControl's intended purpose of rule selection. Test plan: 1. Apply patches and update database: perl installer/data/mysql/updatedatabase.pl 2. Verify new preference exists: Administration > System preferences > Circulation Search for "CircControlCheckoutLimitScope" Default value should be 'all' 3. Set up test environment: - Create two branches (Branch1, Branch2) - Create a patron category and item type - Create a patron in Branch1 - Create items from both branches - Set circulation rules for both branches: * Branch1: maxissueqty = 1 for itemtype/category * Branch2: maxissueqty = 1 for itemtype/category 4. Test scope = 'checkout': a. Set CircControlCheckoutLimitScope = 'checkout' b. Log in as staff from Branch1 c. Check out item from Branch1 to patron → succeeds d. Try to check out another item from Branch1 → blocked (limit: 1) e. Log in as staff from Branch2 f. Check out item from Branch2 to same patron → succeeds g. Expected: Checkouts at different libraries don't count together 5. Test scope = 'all': a. Set CircControlCheckoutLimitScope = 'all' b. Return all items c. Log in as staff from Branch1 d. Check out item from Branch1 to patron → succeeds e. Log in as staff from Branch2 f. Try to check out item from Branch2 to same patron → blocked g. Expected: All patron checkouts count toward limit regardless of location 6. Test scope = 'item': a. Set CircControlCheckoutLimitScope = 'item' b. Set HomeOrHoldingBranch = 'homebranch' c. Return all items d. Check out item from Branch1 to patron → succeeds e. Try to check out another item from Branch1 → blocked (limit: 1) f. Check out item from Branch2 to same patron → succeeds g. Expected: Only items from same home library count together h. Note: When checking out an item, only checkouts of items from the same library as that item count toward the limit 7. Test scope applies to all rule types: a. With scope='item', test with a general rule (branch=*) b. Check out items from different branches c. Expected: Scope still applies - only items from the same branch as the item being checked out are counted 8. Test independence from CircControl: a. Test each scope with CircControl = 'PickupLibrary' b. Test each scope with CircControl = 'ItemHomeLibrary' c. Test each scope with CircControl = 'PatronLibrary' d. Expected: CircControlCheckoutLimitScope behavior is consistent regardless of CircControl setting 9. Run tests: prove t/db_dependent/Circulation/TooMany.t 10. Test with patron_maxissueqty: - Set up patron-level limits instead of item-type limits - Verify all three scopes work correctly - Test with both rule types active 11. Verify existing functionality: - Test on-site checkouts still work - Test item type hierarchies still work - Test unlimited ("") limits still work - prove t/db_dependent/Circulation/ Sponsored-by: University of the Arts London --- C4/Circulation.pm | 51 ++- .../admin/preferences/circulation.pref | 8 + t/db_dependent/Circulation/TooMany.t | 362 +++++++++++++++++- 3 files changed, 397 insertions(+), 24 deletions(-) diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 5aa42a4d25f..471da9c851c 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -482,17 +482,23 @@ sub TooMany { if ( defined($maxissueqty_rule) and $maxissueqty_rule->rule_value ne "" ) { my $checkouts; - if ( $maxissueqty_rule->branchcode ) { - if ( C4::Context->preference('CircControl') eq 'PickupLibrary' ) { - $checkouts = $patron->checkouts->search( { 'me.branchcode' => $maxissueqty_rule->branchcode } ); - } elsif ( C4::Context->preference('CircControl') eq 'PatronLibrary' ) { - $checkouts = $patron->checkouts; # if branch is the patron's home branch, then count all loans by patron - } else { - my $branch_type = C4::Context->preference('HomeOrHoldingBranch') || 'homebranch'; - $checkouts = $patron->checkouts->search( { "item.$branch_type" => $maxissueqty_rule->branchcode } ); - } + my $checkout_limit_scope = C4::Context->preference('CircControlCheckoutLimitScope') || 'all'; + if ( $checkout_limit_scope eq 'checkout' + and ( C4::Context->userenv and C4::Context->userenv->{'branch'} ) ) + { + + # Count only checkouts made at this pickup library + my $pickup_branch = C4::Context->userenv->{'branch'}; + $checkouts = $patron->checkouts->search( { 'me.branchcode' => $pickup_branch } ); + } elsif ( $checkout_limit_scope eq 'item' ) { + + # Count only checkouts of items from this items library (follows HomeOrHoldingBranch) + my $branch_type = C4::Context->preference('HomeOrHoldingBranch') || 'homebranch'; + $checkouts = $patron->checkouts->search( { "item.$branch_type" => $item->$branch_type } ); } else { - $checkouts = $patron->checkouts; # if rule is not branch specific then count all loans by patron + + # Default 'all': count all patron checkouts across all libraries + $checkouts = $patron->checkouts; # if rule is not branch specific then count all loans by patron } $checkouts = $checkouts->search( undef, { prefetch => 'item' } ); @@ -590,17 +596,24 @@ sub TooMany { and $branch_borrower_circ_rule->{patron_maxissueqty} ne '' ) { my $checkouts; - if ( C4::Context->preference('CircControl') eq 'PickupLibrary' ) { - $checkouts = $patron->checkouts->search( { 'me.branchcode' => $branch } ); - } elsif ( C4::Context->preference('CircControl') eq 'PatronLibrary' ) { - $checkouts = $patron->checkouts; # if branch is the patron's home branch, then count all loans by patron - } else { + my $checkout_limit_scope = C4::Context->preference('CircControlCheckoutLimitScope') || 'all'; + if ( $checkout_limit_scope eq 'checkout' + and ( C4::Context->userenv and C4::Context->userenv->{'branch'} ) ) + { + # Count only checkouts made at this pickup library + my $pickup_branch = C4::Context->userenv->{'branch'}; + $checkouts = $patron->checkouts->search( { 'me.branchcode' => $pickup_branch } ); + } elsif ( $checkout_limit_scope eq 'item' ) { + + # Count only checkouts of items from this library (follows HomeOrHoldingBranch) my $branch_type = C4::Context->preference('HomeOrHoldingBranch') || 'homebranch'; - $checkouts = $patron->checkouts->search( - { "item.$branch_type" => $branch }, - { prefetch => 'item' } - ); + $checkouts = $patron->checkouts->search( { "item.$branch_type" => $item->$branch_type } ); + } else { + + # Default 'all': count all patron checkouts across all libraries + $checkouts = $patron->checkouts; } + $checkouts = $checkouts->search( undef, { prefetch => 'item' } ); my $checkout_count = $checkouts->count; my $onsite_checkout_count = $checkouts->search( { onsite_checkout => 1 } )->count; 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 82070552d6c..580e5a8a7d5 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 @@ -312,6 +312,14 @@ Circulation: homebranch: the item's home library (homebranch) holdingbranch: the item's holding library (holdingbranch) - . + - + - Calculate total issues for maxIssue limits using + - pref: CircControlCheckoutLimitScope + type: choice + choices: + all: all patron checkouts across all libraries. + item: only checkouts of items from the same library as the item being checked out (follows HomeOrHoldingBranch preference). + checkout: only checkouts made at the same library as the current checkout. - - Allow items to be checked in - pref: AllowReturnToBranch diff --git a/t/db_dependent/Circulation/TooMany.t b/t/db_dependent/Circulation/TooMany.t index e2105c921f2..5b5a2f95038 100755 --- a/t/db_dependent/Circulation/TooMany.t +++ b/t/db_dependent/Circulation/TooMany.t @@ -16,7 +16,7 @@ use Modern::Perl; use Test::NoWarnings; -use Test::More tests => 12; +use Test::More tests => 15; use C4::Context; use C4::Members; @@ -562,7 +562,8 @@ subtest '1 BranchBorrowerCircRule exist: 1 CO allowed, 1 OSCO allowed' => sub { subtest 'General vs specific rules limit quantity correctly' => sub { plan tests => 18; - t::lib::Mocks::mock_preference( 'CircControl', 'ItemHomeLibrary' ); + t::lib::Mocks::mock_preference( 'CircControl', 'ItemHomeLibrary' ); + t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'item' ); my $branch = $builder->build( { source => 'Branch', } ); my $category = $builder->build( { source => 'Category', } ); my $itemtype = $builder->build( @@ -695,7 +696,8 @@ subtest 'General vs specific rules limit quantity correctly' => sub { ); # If circcontrol is PatronLibrary we count all the patron's loan, regardless of branch - t::lib::Mocks::mock_preference( 'CircControl', 'PatronLibrary' ); + t::lib::Mocks::mock_preference( 'CircControl', 'PatronLibrary' ); + t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'all' ); $data = C4::Circulation::TooMany( $patron, $branch_item ); $rule = delete $data->{circulation_rule}; is( ref $rule, 'Koha::CirculationRule', 'Circulation rule was returned' ); @@ -708,7 +710,8 @@ subtest 'General vs specific rules limit quantity correctly' => sub { }, 'We are allowed one from the branch specifically, but have one' ); - t::lib::Mocks::mock_preference( 'CircControl', 'ItemHomeLibrary' ); + t::lib::Mocks::mock_preference( 'CircControl', 'ItemHomeLibrary' ); + t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'item' ); $issue = C4::Circulation::AddIssue( $patron, $branch_item->barcode, dt_from_string() ); @@ -735,6 +738,8 @@ subtest 'General vs specific rules limit quantity correctly' => sub { ); # Now we make another from a different branch + # Switch to 'all' scope to test general rules counting all checkouts + t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'all' ); my $item_2 = $builder->build_sample_item( { itype => $itemtype->{itemtype}, @@ -794,7 +799,9 @@ subtest 'General vs specific rules limit quantity correctly' => sub { 'We are only allowed one for general rule, and have two total (no rule for specific branch)' ); - # Set a branch specific rule for new branch + # Set a branch specific rule for new branch and use 'checkout' scope + # With PickupLibrary CircControl, we want to count checkouts made at this branch + t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'checkout' ); Koha::CirculationRules->set_rules( { branchcode => $branch2->{branchcode}, @@ -1225,6 +1232,351 @@ subtest 'HomeOrHoldingBranch is used' => sub { teardown(); }; +subtest 'CircControlCheckoutLimitScope system preference controls checkout counting' => sub { + plan tests => 12; + + t::lib::Mocks::mock_preference( 'item-level_itypes', 1 ); + + my $branch1 = $builder->build( { source => 'Branch' } ); + my $branch2 = $builder->build( { source => 'Branch' } ); + my $category = $builder->build( { source => 'Category' } ); + my $itemtype = $builder->build( { source => 'Itemtype', value => { notforloan => 0 } } ); + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { + categorycode => $category->{categorycode}, + branchcode => $branch1->{branchcode}, + } + } + ); + + # Create items from different branches + my $item_branch1 = $builder->build_sample_item( + { + homebranch => $branch1->{branchcode}, + holdingbranch => $branch1->{branchcode}, + itype => $itemtype->{itemtype} + } + ); + + my $item_branch2 = $builder->build_sample_item( + { + homebranch => $branch2->{branchcode}, + holdingbranch => $branch2->{branchcode}, + itype => $itemtype->{itemtype} + } + ); + + # Set circulation rules for both branches + Koha::CirculationRules->set_rules( + { + branchcode => $branch1->{branchcode}, + categorycode => $category->{categorycode}, + itemtype => $itemtype->{itemtype}, + rules => { + maxissueqty => 1, + } + } + ); + + Koha::CirculationRules->set_rules( + { + branchcode => $branch2->{branchcode}, + categorycode => $category->{categorycode}, + itemtype => $itemtype->{itemtype}, + rules => { + maxissueqty => 1, + } + } + ); + + # Issue an item from branch1 + t::lib::Mocks::mock_userenv( { branchcode => $branch1->{branchcode} } ); + my $issue = C4::Circulation::AddIssue( $patron, $item_branch1->barcode, dt_from_string() ); + ok( $issue, 'Item from branch1 issued successfully' ); + + # Test with CircControlCheckoutLimitScope = 'checkout' + t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'checkout' ); + + # From branch1 - should count checkout made at branch1 + my $data = C4::Circulation::TooMany( $patron, $item_branch1 ); + ok( $data, 'TooMany should return limit exceeded when checking from same pickup library' ); + is( $data->{count}, 1, 'Should count checkout made at this pickup library' ); + is( $data->{reason}, 'TOO_MANY_CHECKOUTS', 'Correct reason returned' ); + + # From branch2 - should not count checkout made at branch1 + t::lib::Mocks::mock_userenv( { branchcode => $branch2->{branchcode} } ); + $data = C4::Circulation::TooMany( $patron, $item_branch2 ); + ok( !$data, 'TooMany should allow checkout when no checkouts from this pickup library' ); + + # Test with CircControlCheckoutLimitScope = 'all' + t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'all' ); + + # Should count all patron checkouts regardless of pickup library + $data = C4::Circulation::TooMany( $patron, $item_branch2 ); + ok( $data, 'TooMany should return limit exceeded when counting all checkouts' ); + is( $data->{count}, 1, 'Should count all patron checkouts regardless of branch' ); + + # Test with CircControlCheckoutLimitScope = 'all' from branch1 + t::lib::Mocks::mock_userenv( { branchcode => $branch1->{branchcode} } ); + $data = C4::Circulation::TooMany( $patron, $item_branch2 ); + ok( $data, 'TooMany should return limit exceeded' ); + is( $data->{count}, 1, 'Should count all patron checkouts' ); + + # Test with CircControlCheckoutLimitScope = 'item' + t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'item' ); + + # Item from branch2 - should not count checkout of item from branch1 + $data = C4::Circulation::TooMany( $patron, $item_branch2 ); + ok( !$data, 'TooMany should allow checkout when no items from this home library checked out' ); + + # Item from branch1 - should count checkout of item from branch1 + my $item_branch1_2 = $builder->build_sample_item( + { + homebranch => $branch1->{branchcode}, + holdingbranch => $branch1->{branchcode}, + itype => $itemtype->{itemtype} + } + ); + + $data = C4::Circulation::TooMany( $patron, $item_branch1_2 ); + ok( $data, 'TooMany should return limit exceeded when item from same home library already checked out' ); + is( $data->{count}, 1, 'Should count checkout of item from same home library' ); + + teardown(); +}; + +subtest 'CircControlCheckoutLimitScope with HomeOrHoldingBranch preference' => sub { + plan tests => 8; + + t::lib::Mocks::mock_preference( 'item-level_itypes', 1 ); + t::lib::Mocks::mock_preference( 'CircControl', 'ItemHomeLibrary' ); + + my $branch1 = $builder->build( { source => 'Branch' } ); + my $branch2 = $builder->build( { source => 'Branch' } ); + my $category = $builder->build( { source => 'Category' } ); + my $itemtype = $builder->build( { source => 'Itemtype', value => { notforloan => 0 } } ); + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { + categorycode => $category->{categorycode}, + branchcode => $branch1->{branchcode}, + } + } + ); + + # Create an item with different home and holding branches + my $item_home1_holding2 = $builder->build_sample_item( + { + homebranch => $branch1->{branchcode}, + holdingbranch => $branch2->{branchcode}, + itype => $itemtype->{itemtype} + } + ); + + my $item_home2_holding1 = $builder->build_sample_item( + { + homebranch => $branch2->{branchcode}, + holdingbranch => $branch1->{branchcode}, + itype => $itemtype->{itemtype} + } + ); + + # Set circulation rules for both branches + Koha::CirculationRules->set_rules( + { + branchcode => $branch1->{branchcode}, + categorycode => $category->{categorycode}, + itemtype => $itemtype->{itemtype}, + rules => { + maxissueqty => 1, + } + } + ); + + Koha::CirculationRules->set_rules( + { + branchcode => $branch2->{branchcode}, + categorycode => $category->{categorycode}, + itemtype => $itemtype->{itemtype}, + rules => { + maxissueqty => 1, + } + } + ); + + # Test with scope='item' and HomeOrHoldingBranch='homebranch' + t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'item' ); + t::lib::Mocks::mock_preference( 'HomeOrHoldingBranch', 'homebranch' ); + t::lib::Mocks::mock_userenv( { branchcode => $branch1->{branchcode} } ); + + # Check out item with homebranch=branch1 + my $issue = C4::Circulation::AddIssue( $patron, $item_home1_holding2->barcode, dt_from_string() ); + ok( $issue, 'Item with homebranch=branch1 issued successfully' ); + + # Reload item to get updated holdingbranch (AddIssue may change it) + $item_home1_holding2->discard_changes; + + # Try to check out another item with homebranch=branch1 + my $item_home1_2 = $builder->build_sample_item( + { + homebranch => $branch1->{branchcode}, + holdingbranch => $branch1->{branchcode}, + itype => $itemtype->{itemtype} + } + ); + + my $data = C4::Circulation::TooMany( $patron, $item_home1_2 ); + ok( $data, 'TooMany should block checkout of another item from same homebranch' ); + is( $data->{count}, 1, 'Should count 1 checkout with same homebranch' ); + + # Try to check out item with different homebranch (branch2) + $data = C4::Circulation::TooMany( $patron, $item_home2_holding1 ); + ok( !$data, 'TooMany should allow checkout of item from different homebranch' ); + + # Now test with HomeOrHoldingBranch='holdingbranch' + t::lib::Mocks::mock_preference( 'HomeOrHoldingBranch', 'holdingbranch' ); + + # After checkout, the item's holdingbranch may have been updated to the checkout library + # Get the current holdingbranch from the checked-out item + my $checked_out_holdingbranch = $item_home1_holding2->holdingbranch; + + # Try to check out another item with the same holdingbranch + my $item_holding_same = $builder->build_sample_item( + { + homebranch => $branch2->{branchcode}, + holdingbranch => $checked_out_holdingbranch, + itype => $itemtype->{itemtype} + } + ); + + $data = C4::Circulation::TooMany( $patron, $item_holding_same ); + ok( $data, 'TooMany should block checkout of another item from same holdingbranch' ); + is( $data->{count}, 1, 'Should count 1 checkout with same holdingbranch' ); + + # Try to check out item with different holdingbranch + my $different_holdingbranch = + $checked_out_holdingbranch eq $branch1->{branchcode} ? $branch2->{branchcode} : $branch1->{branchcode}; + my $item_holding_different = $builder->build_sample_item( + { + homebranch => $branch1->{branchcode}, + holdingbranch => $different_holdingbranch, + itype => $itemtype->{itemtype} + } + ); + + $data = C4::Circulation::TooMany( $patron, $item_holding_different ); + ok( !$data, 'TooMany should allow checkout of item from different holdingbranch' ); + + # Verify that we're actually using holdingbranch not homebranch + # item_home1_2 has homebranch=branch1, verify its holdingbranch determines the result + $data = C4::Circulation::TooMany( $patron, $item_home1_2 ); + if ( $item_home1_2->holdingbranch eq $checked_out_holdingbranch ) { + ok( $data, 'Item with same holdingbranch should be blocked' ); + } else { + ok( !$data, 'Item with different holdingbranch should be allowed' ); + } + + teardown(); +}; + +subtest 'CircControlCheckoutLimitScope with patron_maxissueqty' => sub { + plan tests => 9; + + t::lib::Mocks::mock_preference( 'item-level_itypes', 1 ); + + my $branch1 = $builder->build( { source => 'Branch' } ); + my $branch2 = $builder->build( { source => 'Branch' } ); + my $category = $builder->build( { source => 'Category' } ); + my $itemtype = $builder->build( { source => 'Itemtype', value => { notforloan => 0 } } ); + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { + categorycode => $category->{categorycode}, + branchcode => $branch1->{branchcode}, + } + } + ); + + # Create items from different branches + my $item_branch1 = $builder->build_sample_item( + { + homebranch => $branch1->{branchcode}, + holdingbranch => $branch1->{branchcode}, + itype => $itemtype->{itemtype} + } + ); + + my $item_branch2 = $builder->build_sample_item( + { + homebranch => $branch2->{branchcode}, + holdingbranch => $branch2->{branchcode}, + itype => $itemtype->{itemtype} + } + ); + + # Set patron_maxissueqty rules for both branches + Koha::CirculationRules->set_rules( + { + branchcode => $branch1->{branchcode}, + categorycode => $category->{categorycode}, + rules => { + patron_maxissueqty => 1, + } + } + ); + + Koha::CirculationRules->set_rules( + { + branchcode => $branch2->{branchcode}, + categorycode => $category->{categorycode}, + rules => { + patron_maxissueqty => 1, + } + } + ); + + # Issue an item from branch1 + t::lib::Mocks::mock_userenv( { branchcode => $branch1->{branchcode} } ); + my $issue = C4::Circulation::AddIssue( $patron, $item_branch1->barcode, dt_from_string() ); + ok( $issue, 'Item from branch1 issued successfully' ); + + # Test with scope='all' - should count all checkouts + t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'all' ); + my $data = C4::Circulation::TooMany( $patron, $item_branch1 ); + ok( $data, 'patron_maxissueqty with scope=all should count all checkouts' ); + is( $data->{count}, 1, 'Should count 1 checkout total' ); + + # Test with scope='item' - should count items from same branch + t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'item' ); + $data = C4::Circulation::TooMany( $patron, $item_branch1 ); + ok( $data, 'patron_maxissueqty with scope=item should count items from same branch' ); + is( $data->{count}, 1, 'Should count 1 checkout from branch1' ); + + # Item from different branch should be allowed with scope='item' + $data = C4::Circulation::TooMany( $patron, $item_branch2 ); + ok( !$data, 'patron_maxissueqty with scope=item should allow item from different branch' ); + + # Test with scope='checkout' - should count checkouts made at same location + t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'checkout' ); + $data = C4::Circulation::TooMany( $patron, $item_branch1 ); + ok( $data, 'patron_maxissueqty with scope=checkout should count checkouts made here' ); + is( $data->{count}, 1, 'Should count 1 checkout made at branch1' ); + + # From different branch should be allowed with scope='checkout' + t::lib::Mocks::mock_userenv( { branchcode => $branch2->{branchcode} } ); + $data = C4::Circulation::TooMany( $patron, $item_branch2 ); + ok( !$data, 'patron_maxissueqty with scope=checkout should allow checkout from different branch' ); + + teardown(); +}; + $schema->storage->txn_rollback; sub teardown { -- 2.51.0