Bug 21817: Centralize the mock of userenv from tests
[koha.git] / t / db_dependent / Circulation / TooMany.t
1 #!/usr/bin/perl
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation; either version 3 of the License, or (at your option) any later
8 # version.
9 #
10 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License along
15 # with Koha; if not, see <http://www.gnu.org/licenses>.
16
17 use Modern::Perl;
18 use Test::More tests => 7;
19 use C4::Context;
20
21 use C4::Members;
22 use C4::Items;
23 use C4::Biblio;
24 use C4::Circulation;
25 use C4::Context;
26
27 use Koha::DateUtils qw( dt_from_string );
28 use Koha::Database;
29
30 use t::lib::TestBuilder;
31 use t::lib::Mocks;
32
33 my $schema = Koha::Database->new->schema;
34 $schema->storage->txn_begin;
35
36 our $dbh = C4::Context->dbh;
37
38 $dbh->do(q|DELETE FROM issues|);
39 $dbh->do(q|DELETE FROM items|);
40 $dbh->do(q|DELETE FROM borrowers|);
41 $dbh->do(q|DELETE FROM branches|);
42 $dbh->do(q|DELETE FROM categories|);
43 $dbh->do(q|DELETE FROM accountlines|);
44 $dbh->do(q|DELETE FROM itemtypes|);
45 $dbh->do(q|DELETE FROM branch_item_rules|);
46 $dbh->do(q|DELETE FROM branch_borrower_circ_rules|);
47 $dbh->do(q|DELETE FROM default_branch_circ_rules|);
48 $dbh->do(q|DELETE FROM default_circ_rules|);
49 $dbh->do(q|DELETE FROM default_branch_item_rules|);
50 $dbh->do(q|DELETE FROM issuingrules|);
51
52 my $builder = t::lib::TestBuilder->new();
53 t::lib::Mocks::mock_preference('item-level_itypes', 1); # Assuming the item type is defined at item level
54
55 my $branch = $builder->build({
56     source => 'Branch',
57 });
58
59 my $category = $builder->build({
60     source => 'Category',
61 });
62
63 my $patron = $builder->build({
64     source => 'Borrower',
65     value => {
66         categorycode => $category->{categorycode},
67         branchcode => $branch->{branchcode},
68     },
69 });
70
71 my $biblio = $builder->build({
72     source => 'Biblio',
73     value => {
74         branchcode => $branch->{branchcode},
75     },
76 });
77 my $item = $builder->build({
78     source => 'Item',
79     value => {
80         biblionumber => $biblio->{biblionumber},
81         homebranch => $branch->{branchcode},
82         holdingbranch => $branch->{branchcode},
83     },
84 });
85
86 my $patron_object = Koha::Patrons->find( $patron->{borrowernumber} );
87 t::lib::Mocks::mock_userenv( { patron => $patron_object });
88
89 # TooMany return ($current_loan_count, $max_loans_allowed) or undef
90 # CO = Checkout
91 # OSCO: On-site checkout
92
93 subtest 'no rules exist' => sub {
94     plan tests => 2;
95     is_deeply(
96         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
97         { reason => 'NO_RULE_DEFINED', max_allowed => 0 },
98         'CO should not be allowed, in any cases'
99     );
100     is_deeply(
101         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
102         { reason => 'NO_RULE_DEFINED', max_allowed => 0 },
103         'OSCO should not be allowed, in any cases'
104     );
105 };
106
107 subtest '1 Issuingrule exist 0 0: no issue allowed' => sub {
108     plan tests => 4;
109     my $issuingrule = $builder->build({
110         source => 'Issuingrule',
111         value => {
112             branchcode         => $branch->{branchcode},
113             categorycode       => $category->{categorycode},
114             itemtype           => '*',
115             maxissueqty        => 0,
116             maxonsiteissueqty  => 0,
117         },
118     });
119     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
120     is_deeply(
121         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
122         {
123             reason => 'TOO_MANY_CHECKOUTS',
124             count => 0,
125             max_allowed => 0,
126         },
127         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
128     );
129     is_deeply(
130         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
131         {
132             reason => 'TOO_MANY_ONSITE_CHECKOUTS',
133             count => 0,
134             max_allowed => 0,
135         },
136         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
137     );
138
139     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
140     is_deeply(
141         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
142         {
143             reason => 'TOO_MANY_CHECKOUTS',
144             count => 0,
145             max_allowed => 0,
146         },
147         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
148     );
149     is_deeply(
150         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
151         {
152             reason => 'TOO_MANY_ONSITE_CHECKOUTS',
153             count => 0,
154             max_allowed => 0,
155         },
156         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
157     );
158
159     teardown();
160 };
161
162 subtest '1 Issuingrule exist with onsiteissueqty=unlimited' => sub {
163     plan tests => 4;
164     my $issuingrule = $builder->build({
165         source => 'Issuingrule',
166         value => {
167             branchcode         => $branch->{branchcode},
168             categorycode       => $category->{categorycode},
169             itemtype           => '*',
170             maxissueqty        => 1,
171             maxonsiteissueqty  => undef,
172         },
173     });
174     my $issue = C4::Circulation::AddIssue( $patron, $item->{barcode}, dt_from_string() );
175     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
176     is_deeply(
177         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
178         {
179             reason => 'TOO_MANY_CHECKOUTS',
180             count => 1,
181             max_allowed => 1,
182         },
183         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
184     );
185     is(
186         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
187         undef,
188         'OSCO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
189     );
190
191     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
192     is_deeply(
193         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
194         {
195             reason => 'TOO_MANY_CHECKOUTS',
196             count => 1,
197             max_allowed => 1,
198         },
199         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
200     );
201     is_deeply(
202         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
203         {
204             reason => 'TOO_MANY_CHECKOUTS',
205             count => 1,
206             max_allowed => 1,
207         },
208         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
209     );
210
211     teardown();
212 };
213
214
215 subtest '1 Issuingrule exist 1 1: issue is allowed' => sub {
216     plan tests => 4;
217     my $issuingrule = $builder->build({
218         source => 'Issuingrule',
219         value => {
220             branchcode         => $branch->{branchcode},
221             categorycode       => $category->{categorycode},
222             itemtype           => '*',
223             maxissueqty        => 1,
224             maxonsiteissueqty  => 1,
225         },
226     });
227     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
228     is(
229         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
230         undef,
231         'CO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
232     );
233     is(
234         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
235         undef,
236         'OSCO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
237     );
238
239     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
240     is(
241         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
242         undef,
243         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
244     );
245     is(
246         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
247         undef,
248         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
249     );
250
251     teardown();
252 };
253
254 subtest '1 Issuingrule exist: 1 CO allowed, 1 OSCO allowed. Do a CO' => sub {
255     plan tests => 5;
256     my $issuingrule = $builder->build({
257         source => 'Issuingrule',
258         value => {
259             branchcode         => $branch->{branchcode},
260             categorycode       => $category->{categorycode},
261             itemtype           => '*',
262             maxissueqty        => 1,
263             maxonsiteissueqty  => 1,
264         },
265     });
266
267     my $issue = C4::Circulation::AddIssue( $patron, $item->{barcode}, dt_from_string() );
268     like( $issue->issue_id, qr|^\d+$|, 'The issue should have been inserted' );
269
270     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
271     is_deeply(
272         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
273         {
274             reason => 'TOO_MANY_CHECKOUTS',
275             count => 1,
276             max_allowed => 1,
277         },
278         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
279     );
280     is(
281         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
282         undef,
283         'OSCO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
284     );
285
286     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
287     is_deeply(
288         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
289         {
290             reason => 'TOO_MANY_CHECKOUTS',
291             count => 1,
292             max_allowed => 1,
293         },
294         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
295     );
296     is_deeply(
297         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
298         {
299             reason => 'TOO_MANY_CHECKOUTS',
300             count => 1,
301             max_allowed => 1,
302         },
303         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
304     );
305
306     teardown();
307 };
308
309 subtest '1 Issuingrule exist: 1 CO allowed, 1 OSCO allowed, Do a OSCO' => sub {
310     plan tests => 5;
311     my $issuingrule = $builder->build({
312         source => 'Issuingrule',
313         value => {
314             branchcode         => $branch->{branchcode},
315             categorycode       => $category->{categorycode},
316             itemtype           => '*',
317             maxissueqty        => 1,
318             maxonsiteissueqty  => 1,
319         },
320     });
321
322     my $issue = C4::Circulation::AddIssue( $patron, $item->{barcode}, dt_from_string(), undef, undef, undef, { onsite_checkout => 1 } );
323     like( $issue->issue_id, qr|^\d+$|, 'The issue should have been inserted' );
324
325     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
326     is(
327         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
328         undef,
329         'CO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
330     );
331     is_deeply(
332         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
333         {
334             reason => 'TOO_MANY_ONSITE_CHECKOUTS',
335             count => 1,
336             max_allowed => 1,
337         },
338         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
339     );
340
341     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
342     is_deeply(
343         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
344         {
345             reason => 'TOO_MANY_CHECKOUTS',
346             count => 1,
347             max_allowed => 1,
348         },
349         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
350     );
351     is_deeply(
352         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
353         {
354             reason => 'TOO_MANY_ONSITE_CHECKOUTS',
355             count => 1,
356             max_allowed => 1,
357         },
358         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
359     );
360
361     teardown();
362 };
363
364 subtest '1 BranchBorrowerCircRule exist: 1 CO allowed, 1 OSCO allowed' => sub {
365     # Note: the same test coul be done for
366     # DefaultBorrowerCircRule, DefaultBranchCircRule, DefaultBranchItemRule ans DefaultCircRule.pm
367
368     plan tests => 10;
369     my $issuingrule = $builder->build({
370         source => 'BranchBorrowerCircRule',
371         value => {
372             branchcode         => $branch->{branchcode},
373             categorycode       => $category->{categorycode},
374             maxissueqty        => 1,
375             maxonsiteissueqty  => 1,
376         },
377     });
378
379     my $issue = C4::Circulation::AddIssue( $patron, $item->{barcode}, dt_from_string(), undef, undef, undef );
380     like( $issue->issue_id, qr|^\d+$|, 'The issue should have been inserted' );
381
382     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
383     is_deeply(
384         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
385         {
386             reason => 'TOO_MANY_CHECKOUTS',
387             count => 1,
388             max_allowed => 1,
389         },
390         'CO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
391     );
392     is(
393         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
394         undef,
395         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
396     );
397
398     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
399     is_deeply(
400         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
401         {
402             reason => 'TOO_MANY_CHECKOUTS',
403             count => 1,
404             max_allowed => 1,
405         },
406         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
407     );
408     is_deeply(
409         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
410         {
411             reason => 'TOO_MANY_CHECKOUTS',
412             count => 1,
413             max_allowed => 1,
414         },
415         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
416     );
417
418     teardown();
419
420     $issue = C4::Circulation::AddIssue( $patron, $item->{barcode}, dt_from_string(), undef, undef, undef, { onsite_checkout => 1 } );
421     like( $issue->issue_id, qr|^\d+$|, 'The issue should have been inserted' );
422
423     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
424     is(
425         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
426         undef,
427         'CO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
428     );
429     is_deeply(
430         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
431         {
432             reason => 'TOO_MANY_ONSITE_CHECKOUTS',
433             count => 1,
434             max_allowed => 1,
435         },
436         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
437     );
438
439     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
440     is_deeply(
441         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
442         {
443             reason => 'TOO_MANY_CHECKOUTS',
444             count => 1,
445             max_allowed => 1,
446         },
447         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
448     );
449     is_deeply(
450         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
451         {
452             reason => 'TOO_MANY_ONSITE_CHECKOUTS',
453             count => 1,
454             max_allowed => 1,
455         },
456         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
457     );
458
459     teardown();
460 };
461
462 $schema->storage->txn_rollback;
463
464 sub teardown {
465     $dbh->do(q|DELETE FROM issues|);
466     $dbh->do(q|DELETE FROM issuingrules|);
467 }
468