From 5c833281ed064d1fa70a0c373920d224fc399740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Cohen=20Arazi?= Date: Tue, 5 May 2026 10:12:51 -0300 Subject: [PATCH] Bug 42385: Add Koha::Policy::Circulation Adds Koha::Policy::Circulation->circ_control_library($item, $patron) class method that resolves which library controls circulation rules, based on the CircControl and HomeOrHoldingBranch system preferences. This follows the same pattern as Koha::Policy::Holds->holds_control_library. An optional pickup_library_id parameter allows callers in API/daemon contexts to pass the library explicitly instead of relying on userenv. Test plan: 1. Apply patch 2. Run: $ ktd --shell k$ prove t/db_dependent/Koha/Policy/Circulation.t => SUCCESS: Tests pass! 3. Sign off :-D Signed-off-by: Alex Carver [Acerock7] Signed-off-by: Martin Renvoize Signed-off-by: Pedro Amorim --- Koha/Policy/Circulation.pm | 79 ++++++++++++++ t/db_dependent/Koha/Policy/Circulation.t | 127 +++++++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 Koha/Policy/Circulation.pm create mode 100755 t/db_dependent/Koha/Policy/Circulation.t diff --git a/Koha/Policy/Circulation.pm b/Koha/Policy/Circulation.pm new file mode 100644 index 0000000000..3ee32d1f29 --- /dev/null +++ b/Koha/Policy/Circulation.pm @@ -0,0 +1,79 @@ +package Koha::Policy::Circulation; + +# Copyright 2026 Koha Development Team +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use C4::Context; + +=head1 NAME + +Koha::Policy::Circulation - module to deal with circulation policy + +=head1 API + +=head2 Class methods + +=head3 circ_control_library + + my $branchcode = Koha::Policy::Circulation->circ_control_library( $item, $patron ); + + # With explicit pickup library (avoids userenv dependency) + my $branchcode = Koha::Policy::Circulation->circ_control_library( + $item, $patron, { pickup_library_id => $library_id } + ); + +Given L and L objects, returns the branchcode of +the library that controls circulation rules. Relies on the B +and B system preferences. + +When C is C, the method uses the +C parameter if provided, otherwise falls back to +Cuserenv-E{branch}>. + +=cut + +sub circ_control_library { + my ( $class, $item, $patron, $params ) = @_; + + my $circcontrol = C4::Context->preference('CircControl'); + + if ( $circcontrol eq 'PickupLibrary' ) { + return $params->{pickup_library_id} + if $params->{pickup_library_id}; + my $userenv = C4::Context->userenv; + return $userenv->{branch} if $userenv && $userenv->{branch}; + } + + if ( $circcontrol eq 'PatronLibrary' ) { + return $patron->branchcode; + } + + # ItemHomeLibrary (default) + my $branchfield = C4::Context->preference('HomeOrHoldingBranch') || 'homebranch'; + my $branch = $item->$branchfield; + + # Fall back to homebranch if holdingbranch is not set + if ( !defined($branch) && $branchfield eq 'holdingbranch' ) { + $branch = $item->homebranch; + } + + return $branch; +} + +1; diff --git a/t/db_dependent/Koha/Policy/Circulation.t b/t/db_dependent/Koha/Policy/Circulation.t new file mode 100755 index 0000000000..80ddda6b5c --- /dev/null +++ b/t/db_dependent/Koha/Policy/Circulation.t @@ -0,0 +1,127 @@ +#!/usr/bin/env perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use Test::NoWarnings; +use Test::More tests => 5; +use Test::MockModule; + +use t::lib::Mocks; +use t::lib::TestBuilder; + +use Koha::Database; + +BEGIN { use_ok('Koha::Policy::Circulation'); } + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new; + +subtest 'circ_control_library() - PickupLibrary' => sub { + + plan tests => 2; + + $schema->storage->txn_begin; + + t::lib::Mocks::mock_preference( 'CircControl', 'PickupLibrary' ); + + my $library = $builder->build_object( { class => 'Koha::Libraries' } ); + my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); + my $item = $builder->build_sample_item(); + + t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode } ); + + is( + Koha::Policy::Circulation->circ_control_library( $item, $patron ), + $library->branchcode, + 'Uses userenv branch when CircControl is PickupLibrary' + ); + + my $other_library = $builder->build_object( { class => 'Koha::Libraries' } ); + is( + Koha::Policy::Circulation->circ_control_library( + $item, $patron, { pickup_library_id => $other_library->branchcode } + ), + $other_library->branchcode, + 'Uses explicit pickup_library_id when provided' + ); + + $schema->storage->txn_rollback; +}; + +subtest 'circ_control_library() - PatronLibrary' => sub { + + plan tests => 1; + + $schema->storage->txn_begin; + + t::lib::Mocks::mock_preference( 'CircControl', 'PatronLibrary' ); + + my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); + my $item = $builder->build_sample_item(); + + is( + Koha::Policy::Circulation->circ_control_library( $item, $patron ), + $patron->branchcode, + 'Uses patron branchcode when CircControl is PatronLibrary' + ); + + $schema->storage->txn_rollback; +}; + +subtest 'circ_control_library() - ItemHomeLibrary' => sub { + + plan tests => 3; + + $schema->storage->txn_begin; + + t::lib::Mocks::mock_preference( 'CircControl', 'ItemHomeLibrary' ); + + my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); + + # homebranch + t::lib::Mocks::mock_preference( 'HomeOrHoldingBranch', 'homebranch' ); + my $item = $builder->build_sample_item(); + + is( + Koha::Policy::Circulation->circ_control_library( $item, $patron ), + $item->homebranch, + 'Uses item homebranch when HomeOrHoldingBranch is homebranch' + ); + + # holdingbranch + t::lib::Mocks::mock_preference( 'HomeOrHoldingBranch', 'holdingbranch' ); + my $holding_library = $builder->build_object( { class => 'Koha::Libraries' } ); + $item->holdingbranch( $holding_library->branchcode )->store; + + is( + Koha::Policy::Circulation->circ_control_library( $item, $patron ), + $holding_library->branchcode, + 'Uses item holdingbranch when HomeOrHoldingBranch is holdingbranch' + ); + + # holdingbranch fallback to homebranch when null + $item->holdingbranch(undef)->store; + + is( + Koha::Policy::Circulation->circ_control_library( $item, $patron ), + $item->homebranch, + 'Falls back to homebranch when holdingbranch is null' + ); + + $schema->storage->txn_rollback; +};