3 # Copyright 2020 Koha Development team
5 # This file is part of Koha
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22 use Test::More tests => 4;
25 use Koha::CirculationRules;
29 use t::lib::TestBuilder;
32 my $schema = Koha::Database->new->schema;
33 my $builder = t::lib::TestBuilder->new;
35 subtest 'set_rule + get_effective_rule' => sub {
38 $schema->storage->txn_begin;
40 my $categorycode = $builder->build_object( { class => 'Koha::Patron::Categories' } )->categorycode;
41 my $itemtype = $builder->build_object( { class => 'Koha::ItemTypes' } )->itemtype;
42 my $branchcode = $builder->build_object( { class => 'Koha::Libraries' } )->branchcode;
43 my $branchcode_2 = $builder->build_object( { class => 'Koha::Libraries' } )->branchcode;
44 my $rule_name = 'maxissueqty';
45 my $default_rule_value = 1;
48 Koha::CirculationRules->delete;
50 throws_ok { Koha::CirculationRules->get_effective_rule }
51 'Koha::Exceptions::MissingParameter',
52 "Exception should be raised if get_effective_rule is called without rule_name parameter";
54 $rule = Koha::CirculationRules->get_effective_rule(
56 branchcode => $branchcode,
57 categorycode => $categorycode,
58 itemtype => $itemtype,
59 rule_name => $rule_name,
62 is( $rule, undef, 'Undef should be returned if no rule exist' );
64 Koha::CirculationRules->set_rule(
69 rule_name => $rule_name,
70 rule_value => $default_rule_value,
74 $rule = Koha::CirculationRules->get_effective_rule(
77 categorycode => undef,
79 rule_name => $rule_name,
82 is( $rule->rule_value, $default_rule_value, 'undef means default' );
83 $rule = Koha::CirculationRules->get_effective_rule(
88 rule_name => $rule_name,
92 is( $rule->rule_value, $default_rule_value, '* means default' );
94 $rule = Koha::CirculationRules->get_effective_rule(
96 branchcode => $branchcode_2,
99 rule_name => $rule_name,
102 is( $rule->rule_value, 1,
103 'Default rule is returned if there is no rule for this branchcode' );
105 subtest 'test rule matching with different combinations of rule scopes' => sub {
106 my ( $tests, $order ) = prepare_tests_for_rule_scope_combinations(
108 branchcode => $branchcode,
109 categorycode => $categorycode,
110 itemtype => $itemtype,
115 plan tests => 2**scalar @$order;
117 foreach my $test (@$tests) {
118 my $rule_params = {%$test};
119 $rule_params->{rule_name} = $rule_name;
120 my $rule_value = $rule_params->{rule_value} = int( rand(10) );
122 Koha::CirculationRules->set_rule($rule_params);
124 my $rule = Koha::CirculationRules->get_effective_rule(
126 branchcode => $branchcode,
127 categorycode => $categorycode,
128 itemtype => $itemtype,
129 rule_name => $rule_name,
133 my $scope_output = '';
134 foreach my $key ( values @$order ) {
135 $scope_output .= " $key" if $test->{$key} ne '*';
138 is( $rule->rule_value, $rule_value,
140 . ( $scope_output ? $scope_output : ' nothing' ) );
144 my $our_branch_rules = Koha::CirculationRules->search({branchcode => $branchcode});
145 is( $our_branch_rules->count, 4, "We added 8 rules");
146 $our_branch_rules->delete;
147 is( $our_branch_rules->count, 0, "We deleted 8 rules");
149 $schema->storage->txn_rollback;
152 subtest 'get_onshelfholds_policy() tests' => sub {
156 $schema->storage->txn_begin;
158 my $item = $builder->build_sample_item();
160 my $circ_rules = Koha::CirculationRules->new;
162 $circ_rules->search({ rule_name => 'onshelfholds' })->delete;
164 $circ_rules->set_rule(
169 rule_name => 'onshelfholds',
174 is( $circ_rules->get_onshelfholds_policy({ item => $item }), 1, 'If rule_value is set on a matching rule, return it' );
175 # Delete the rule (i.e. get_effective_rule returns undef)
177 is( $circ_rules->get_onshelfholds_policy({ item => $item }), 0, 'If no matching rule, fallback to 0' );
179 $schema->storage->txn_rollback;
182 subtest 'get_effective_daysmode' => sub {
185 $schema->storage->txn_begin;
187 my $item_1 = $builder->build_sample_item();
188 my $item_2 = $builder->build_sample_item();
191 Koha::CirculationRules->search( { rule_name => 'daysmode' } )->delete;
193 # Default value 'Datedue' at pref level
194 t::lib::Mocks::mock_preference( 'useDaysMode', 'Datedue' );
197 Koha::CirculationRules->get_effective_daysmode(
199 categorycode => undef,
200 itemtype => $item_1->effective_itemtype,
205 'daysmode default to pref value if the rule does not exist'
208 Koha::CirculationRules->set_rule(
213 rule_name => 'daysmode',
214 rule_value => 'Calendar',
217 Koha::CirculationRules->set_rule(
221 itemtype => $item_1->effective_itemtype,
222 rule_name => 'daysmode',
223 rule_value => 'Days',
228 Koha::CirculationRules->get_effective_daysmode(
230 categorycode => undef,
231 itemtype => $item_1->effective_itemtype,
236 "daysmode for item_1 is the specific rule"
239 Koha::CirculationRules->get_effective_daysmode(
241 categorycode => undef,
242 itemtype => $item_2->effective_itemtype,
247 "daysmode for item_2 is the one defined for the default circ rule"
250 Koha::CirculationRules->set_rule(
254 itemtype => $item_2->effective_itemtype,
255 rule_name => 'daysmode',
261 Koha::CirculationRules->get_effective_daysmode(
263 categorycode => undef,
264 itemtype => $item_2->effective_itemtype,
269 'daysmode default to pref value if the rule exists but set to""'
272 $schema->storage->txn_rollback;
275 subtest 'get_lostreturn_policy() tests' => sub {
278 $schema->storage->txn_begin;
280 $schema->resultset('CirculationRule')->search()->delete;
282 my $default_rule = $builder->build(
284 source => 'CirculationRule',
287 categorycode => undef,
289 rule_name => 'refund',
294 my $branchcode = $builder->build( { source => 'Branch' } )->{branchcode};
295 my $specific_rule_false = $builder->build(
297 source => 'CirculationRule',
299 branchcode => $branchcode,
300 categorycode => undef,
302 rule_name => 'refund',
307 my $branchcode2 = $builder->build( { source => 'Branch' } )->{branchcode};
308 my $specific_rule_true = $builder->build(
310 source => 'CirculationRule',
312 branchcode => $branchcode2,
313 categorycode => undef,
315 rule_name => 'refund',
320 # Make sure we have an unused branchcode
321 my $branchcode3 = $builder->build( { source => 'Branch' } )->{branchcode};
322 my $specific_rule_dummy = $builder->build(
324 source => 'CirculationRule',
326 branchcode => $branchcode3,
327 categorycode => undef,
329 rule_name => 'refund',
333 my $branch_without_rule = $specific_rule_dummy->{ branchcode };
334 Koha::CirculationRules
337 branchcode => $branch_without_rule,
338 categorycode => undef,
340 rule_name => 'refund'
346 my $item = $builder->build_sample_item(
348 homebranch => $specific_rule_false->{branchcode},
349 holdingbranch => $specific_rule_true->{branchcode}
353 return_branch => $specific_rule_true->{ branchcode },
358 t::lib::Mocks::mock_preference( 'RefundLostOnReturnControl', 'CheckinLibrary' );
359 is( Koha::CirculationRules->get_lostreturn_policy( $params ),
360 1,'Specific rule for checkin branch is applied (true)');
362 t::lib::Mocks::mock_preference( 'RefundLostOnReturnControl', 'ItemHomeBranch' );
363 is( Koha::CirculationRules->get_lostreturn_policy( $params ),
364 0,'Specific rule for home branch is applied (false)');
366 t::lib::Mocks::mock_preference( 'RefundLostOnReturnControl', 'ItemHoldingBranch' );
367 is( Koha::CirculationRules->get_lostreturn_policy( $params ),
368 1,'Specific rule for holding branch is applied (true)');
371 t::lib::Mocks::mock_preference( 'RefundLostOnReturnControl', 'CheckinLibrary' );
372 $params->{return_branch} = $branch_without_rule;
373 is( Koha::CirculationRules->get_lostreturn_policy( $params ),
374 1,'No rule for branch, global rule applied (true)');
376 # Change the default value just to try
377 Koha::CirculationRules->search({ branchcode => undef, rule_name => 'refund' })->next->rule_value(0)->store;
378 is( Koha::CirculationRules->get_lostreturn_policy( $params ),
379 0,'No rule for branch, global rule applied (false)');
381 # No default rule defined check
382 Koha::CirculationRules
386 categorycode => undef,
388 rule_name => 'refund'
393 is( Koha::CirculationRules->get_lostreturn_policy( $params ),
394 1,'No rule for branch, no default rule, fallback default (true)');
396 # Fallback to ItemHoldBranch if CheckinLibrary is undefined
397 $params->{return_branch} = undef;
398 is( Koha::CirculationRules->get_lostreturn_policy( $params ),
399 0,'return_branch undefined, fallback to ItemHomeBranch rule (false)');
401 $schema->storage->txn_rollback;
404 sub prepare_tests_for_rule_scope_combinations {
405 my ( $scope, $rule_name ) = @_;
407 # Here we create a combinations of 1s and 0s the following way
418 # (the number of columns equals to the amount of rule scopes)
419 # The ... symbolizes possible future scopes.
421 # - 0 equals to circulation rule scope with any value (aka. *)
422 # - 1 equals to circulation rule scope exact value, e.g.
423 # "CPL" (for branchcode).
425 # The order is the same as the weight of scopes when sorting circulation
426 # rules. So the first column of numbers is the scope with most weight.
427 # This is defined by C<$order> which will be assigned next.
429 # We must maintain the order in order to keep the test valid. This should be
430 # equal to Koha/CirculationRules.pm "order_by" of C<get_effective_rule> sub.
431 # Let's explicitly define the order and fail test if we are missing a scope:
432 my $order = [ 'branchcode', 'categorycode', 'itemtype' ];
433 is( join(", ", sort keys %$scope),
434 join(", ", sort @$order), 'Missing a scope!' ) if keys %$scope ne scalar @$order;
437 foreach my $value ( glob( "{0,1}" x keys %$scope || 1 ) ) {
438 my $test = { %$scope };
439 for ( my $i=0; $i < keys %$scope; $i++ ) {
440 $test->{$order->[$i]} = '*' unless substr( $value, $i, 1 );
445 return \@tests, $order;