Bug 23404: add UT to show bug
[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 => 9;
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 use Koha::CirculationRules;
30
31 use t::lib::TestBuilder;
32 use t::lib::Mocks;
33
34 my $schema = Koha::Database->new->schema;
35 $schema->storage->txn_begin;
36
37 our $dbh = C4::Context->dbh;
38
39 $dbh->do(q|DELETE FROM issues|);
40 $dbh->do(q|DELETE FROM items|);
41 $dbh->do(q|DELETE FROM borrowers|);
42 $dbh->do(q|DELETE FROM branches|);
43 $dbh->do(q|DELETE FROM categories|);
44 $dbh->do(q|DELETE FROM accountlines|);
45 $dbh->do(q|DELETE FROM itemtypes|);
46 $dbh->do(q|DELETE FROM issuingrules|);
47 Koha::CirculationRules->search()->delete();
48
49 my $builder = t::lib::TestBuilder->new();
50 t::lib::Mocks::mock_preference('item-level_itypes', 1); # Assuming the item type is defined at item level
51
52 my $branch = $builder->build({
53     source => 'Branch',
54 });
55
56 my $category = $builder->build({
57     source => 'Category',
58 });
59
60 my $patron = $builder->build({
61     source => 'Borrower',
62     value => {
63         categorycode => $category->{categorycode},
64         branchcode => $branch->{branchcode},
65     },
66 });
67
68 my $biblio = $builder->build({
69     source => 'Biblio',
70     value => {
71         branchcode => $branch->{branchcode},
72     },
73 });
74 my $item = $builder->build({
75     source => 'Item',
76     value => {
77         biblionumber => $biblio->{biblionumber},
78         homebranch => $branch->{branchcode},
79         holdingbranch => $branch->{branchcode},
80     },
81 });
82
83 my $patron_object = Koha::Patrons->find( $patron->{borrowernumber} );
84 t::lib::Mocks::mock_userenv( { patron => $patron_object });
85
86 # TooMany return ($current_loan_count, $max_loans_allowed) or undef
87 # CO = Checkout
88 # OSCO: On-site checkout
89
90 subtest 'no rules exist' => sub {
91     plan tests => 2;
92     is_deeply(
93         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
94         { reason => 'NO_RULE_DEFINED', max_allowed => 0 },
95         'CO should not be allowed, in any cases'
96     );
97     is_deeply(
98         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
99         { reason => 'NO_RULE_DEFINED', max_allowed => 0 },
100         'OSCO should not be allowed, in any cases'
101     );
102 };
103
104 subtest '1 Issuingrule exist 0 0: no issue allowed' => sub {
105     plan tests => 4;
106     Koha::CirculationRules->set_rules(
107         {
108             branchcode   => $branch->{branchcode},
109             categorycode => $category->{categorycode},
110             itemtype     => '*',
111             rules        => {
112                 maxissueqty       => 0,
113                 maxonsiteissueqty => 0,
114             }
115         },
116     );
117     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
118     is_deeply(
119         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
120         {
121             reason => 'TOO_MANY_CHECKOUTS',
122             count => 0,
123             max_allowed => 0,
124         },
125         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
126     );
127     is_deeply(
128         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
129         {
130             reason => 'TOO_MANY_ONSITE_CHECKOUTS',
131             count => 0,
132             max_allowed => 0,
133         },
134         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
135     );
136
137     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
138     is_deeply(
139         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
140         {
141             reason => 'TOO_MANY_CHECKOUTS',
142             count => 0,
143             max_allowed => 0,
144         },
145         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
146     );
147     is_deeply(
148         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
149         {
150             reason => 'TOO_MANY_ONSITE_CHECKOUTS',
151             count => 0,
152             max_allowed => 0,
153         },
154         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
155     );
156
157     teardown();
158 };
159
160 subtest '1 Issuingrule exist with onsiteissueqty=unlimited' => sub {
161     plan tests => 4;
162
163     Koha::CirculationRules->set_rules(
164         {
165             branchcode   => $branch->{branchcode},
166             categorycode => $category->{categorycode},
167             itemtype     => '*',
168             rules        => {
169                 maxissueqty       => 1,
170                 maxonsiteissueqty => undef,
171             }
172         },
173     );
174
175     my $issue = C4::Circulation::AddIssue( $patron, $item->{barcode}, dt_from_string() );
176     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
177     is_deeply(
178         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
179         {
180             reason => 'TOO_MANY_CHECKOUTS',
181             count => 1,
182             max_allowed => 1,
183         },
184         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
185     );
186     is(
187         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
188         undef,
189         'OSCO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
190     );
191
192     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
193     is_deeply(
194         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
195         {
196             reason => 'TOO_MANY_CHECKOUTS',
197             count => 1,
198             max_allowed => 1,
199         },
200         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
201     );
202     is_deeply(
203         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
204         {
205             reason => 'TOO_MANY_CHECKOUTS',
206             count => 1,
207             max_allowed => 1,
208         },
209         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
210     );
211
212     teardown();
213 };
214
215
216 subtest '1 Issuingrule exist 1 1: issue is allowed' => sub {
217     plan tests => 4;
218     Koha::CirculationRules->set_rules(
219         {
220             branchcode   => $branch->{branchcode},
221             categorycode => $category->{categorycode},
222             itemtype     => '*',
223             rules        => {
224                 maxissueqty       => 1,
225                 maxonsiteissueqty => 1,
226             }
227         }
228     );
229     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
230     is(
231         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
232         undef,
233         'CO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
234     );
235     is(
236         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
237         undef,
238         'OSCO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
239     );
240
241     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
242     is(
243         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
244         undef,
245         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
246     );
247     is(
248         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
249         undef,
250         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
251     );
252
253     teardown();
254 };
255
256 subtest '1 Issuingrule exist: 1 CO allowed, 1 OSCO allowed. Do a CO' => sub {
257     plan tests => 5;
258     Koha::CirculationRules->set_rules(
259         {
260             branchcode   => $branch->{branchcode},
261             categorycode => $category->{categorycode},
262             itemtype     => '*',
263             rules        => {
264                 maxissueqty       => 1,
265                 maxonsiteissueqty => 1,
266             }
267         }
268     );
269
270     my $issue = C4::Circulation::AddIssue( $patron, $item->{barcode}, dt_from_string() );
271     like( $issue->issue_id, qr|^\d+$|, 'The issue should have been inserted' );
272
273     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
274     is_deeply(
275         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
276         {
277             reason => 'TOO_MANY_CHECKOUTS',
278             count => 1,
279             max_allowed => 1,
280         },
281         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
282     );
283     is(
284         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
285         undef,
286         'OSCO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
287     );
288
289     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
290     is_deeply(
291         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
292         {
293             reason => 'TOO_MANY_CHECKOUTS',
294             count => 1,
295             max_allowed => 1,
296         },
297         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
298     );
299     is_deeply(
300         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
301         {
302             reason => 'TOO_MANY_CHECKOUTS',
303             count => 1,
304             max_allowed => 1,
305         },
306         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
307     );
308
309     teardown();
310 };
311
312 subtest '1 Issuingrule exist: 1 CO allowed, 1 OSCO allowed, Do a OSCO' => sub {
313     plan tests => 5;
314     Koha::CirculationRules->set_rules(
315         {
316             branchcode   => $branch->{branchcode},
317             categorycode => $category->{categorycode},
318             itemtype     => '*',
319             rules        => {
320                 maxissueqty       => 1,
321                 maxonsiteissueqty => 1,
322             }
323         }
324     );
325
326     my $issue = C4::Circulation::AddIssue( $patron, $item->{barcode}, dt_from_string(), undef, undef, undef, { onsite_checkout => 1 } );
327     like( $issue->issue_id, qr|^\d+$|, 'The issue should have been inserted' );
328
329     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
330     is(
331         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
332         undef,
333         'CO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
334     );
335     is_deeply(
336         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
337         {
338             reason => 'TOO_MANY_ONSITE_CHECKOUTS',
339             count => 1,
340             max_allowed => 1,
341         },
342         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
343     );
344
345     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
346     is_deeply(
347         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
348         {
349             reason => 'TOO_MANY_CHECKOUTS',
350             count => 1,
351             max_allowed => 1,
352         },
353         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
354     );
355     is_deeply(
356         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
357         {
358             reason => 'TOO_MANY_ONSITE_CHECKOUTS',
359             count => 1,
360             max_allowed => 1,
361         },
362         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
363     );
364
365     teardown();
366 };
367
368 subtest '1 BranchBorrowerCircRule exist: 1 CO allowed, 1 OSCO allowed' => sub {
369     # Note: the same test coul be done for
370     # DefaultBorrowerCircRule, DefaultBranchCircRule, DefaultBranchItemRule ans DefaultCircRule.pm
371
372     plan tests => 10;
373     Koha::CirculationRules->set_rules(
374         {
375             branchcode   => $branch->{branchcode},
376             categorycode => $category->{categorycode},
377             itemtype     => undef,
378             rules        => {
379                 maxissueqty       => 1,
380                 maxonsiteissueqty => 1,
381             }
382         }
383     );
384
385     my $issue = C4::Circulation::AddIssue( $patron, $item->{barcode}, dt_from_string(), undef, undef, undef );
386     like( $issue->issue_id, qr|^\d+$|, 'The issue should have been inserted' );
387
388     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
389     is_deeply(
390         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
391         {
392             reason => 'TOO_MANY_CHECKOUTS',
393             count => 1,
394             max_allowed => 1,
395         },
396         'CO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
397     );
398     is(
399         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
400         undef,
401         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
402     );
403
404     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
405     is_deeply(
406         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
407         {
408             reason => 'TOO_MANY_CHECKOUTS',
409             count => 1,
410             max_allowed => 1,
411         },
412         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
413     );
414     is_deeply(
415         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
416         {
417             reason => 'TOO_MANY_CHECKOUTS',
418             count => 1,
419             max_allowed => 1,
420         },
421         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
422     );
423
424     teardown();
425
426     $issue = C4::Circulation::AddIssue( $patron, $item->{barcode}, dt_from_string(), undef, undef, undef, { onsite_checkout => 1 } );
427     like( $issue->issue_id, qr|^\d+$|, 'The issue should have been inserted' );
428
429     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
430     is(
431         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
432         undef,
433         'CO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
434     );
435     is_deeply(
436         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
437         {
438             reason => 'TOO_MANY_ONSITE_CHECKOUTS',
439             count => 1,
440             max_allowed => 1,
441         },
442         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
443     );
444
445     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
446     is_deeply(
447         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
448         {
449             reason => 'TOO_MANY_CHECKOUTS',
450             count => 1,
451             max_allowed => 1,
452         },
453         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
454     );
455     is_deeply(
456         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
457         {
458             reason => 'TOO_MANY_ONSITE_CHECKOUTS',
459             count => 1,
460             max_allowed => 1,
461         },
462         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
463     );
464
465     teardown();
466 };
467
468 subtest 'General vs specific rules limit quantity correctly' => sub {
469     plan tests => 10;
470
471     t::lib::Mocks::mock_preference('CircControl', 'ItemHomeLibrary');
472     my $branch   = $builder->build({source => 'Branch',});
473     my $category = $builder->build({source => 'Category',});
474     my $itemtype = $builder->build({
475         source => 'Itemtype',
476         value => {
477             rentalcharge => 0,
478             rentalcharge_daily => 0,
479             rentalcharge_hourly => 0,
480             notforloan => 0,
481         }
482     });
483     my $patron = $builder->build({
484         source => 'Borrower',
485         value => {
486             categorycode => $category->{categorycode},
487             branchcode => $branch->{branchcode},
488         }
489     });
490
491     # Set up an issuing rule
492     my $rule = $builder->build({
493         source => 'Issuingrule',
494         value => {
495             categorycode => '*',
496             itemtype     => $itemtype->{itemtype},
497             branchcode   => '*',
498             issuelength  => 1,
499             firstremind  => 1,        # 1 day of grace
500             finedays     => 2,        # 2 days of fine per day of overdue
501             lengthunit   => 'days',
502         }
503     });
504
505     # Set an All->All for an itemtype
506     Koha::CirculationRules->set_rules(
507         {
508             branchcode   => '*',
509             categorycode => '*',
510             itemtype     => $itemtype->{itemtype},
511             rules        => {
512                 maxissueqty       => 1,
513                 maxonsiteissueqty => 1,
514             }
515         }
516     );
517
518     # Create an item
519     my $issue_item = $builder->build_sample_item({
520         itype => $itemtype->{itemtype}
521     });
522     my $branch_item = $builder->build_sample_item({
523         itype => $itemtype->{itemtype},
524         homebranch => $branch->{branchcode},
525         holdingbranch => $branch->{branchcode}
526     });
527
528
529     t::lib::Mocks::mock_userenv({ branchcode => $branch->{branchcode} });
530     my $issue = C4::Circulation::AddIssue( $patron, $issue_item->barcode, dt_from_string() );
531     # We checkout one item
532     is_deeply(
533         C4::Circulation::TooMany( $patron, $branch_item->biblionumber, $branch_item->unblessed ),
534         {
535             reason => 'TOO_MANY_CHECKOUTS',
536             count => 1,
537             max_allowed => 1,
538         },
539         'We are only allowed one, and we have one (itemtype on item)'
540     );
541
542     # Check itemtype on biblio level
543     t::lib::Mocks::mock_preference('item-level_itypes', 0);
544     $issue_item->biblio->biblioitem->itemtype($itemtype->{itemtype})->store;
545     $branch_item->biblio->biblioitem->itemtype($itemtype->{itemtype})->store;
546     # We checkout one item
547     is_deeply(
548         C4::Circulation::TooMany( $patron, $branch_item->biblionumber, $branch_item->unblessed ),
549         {
550             reason => 'TOO_MANY_CHECKOUTS',
551             count => 1,
552             max_allowed => 1,
553         },
554         'We are only allowed one, and we have one (itemtype on biblioitem)'
555     );
556     t::lib::Mocks::mock_preference('item-level_itypes', 1);
557
558     # Set a branch specific rule
559     Koha::CirculationRules->set_rules(
560         {
561             branchcode   => $branch->{branchcode},
562             categorycode => $category->{categorycode},
563             itemtype     => $itemtype->{itemtype},
564             rules        => {
565                 maxissueqty       => 1,
566                 maxonsiteissueqty => 1,
567             }
568         }
569     );
570
571     is(
572         C4::Circulation::TooMany( $patron, $branch_item->biblionumber, $branch_item->unblessed ),
573         undef,
574         'We are allowed one from the branch specifically now'
575     );
576
577     # If circcontrol is PatronLibrary we count all the patron's loan, regardless of branch
578     t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary');
579     is_deeply(
580         C4::Circulation::TooMany( $patron, $branch_item->biblionumber, $branch_item->unblessed ),
581         {
582             reason => 'TOO_MANY_CHECKOUTS',
583             count => 1,
584             max_allowed => 1,
585         },
586         'We are allowed one from the branch specifically, but have one'
587     );
588     t::lib::Mocks::mock_preference('CircControl', 'ItemHomeLibrary');
589
590     $issue = C4::Circulation::AddIssue( $patron, $branch_item->barcode, dt_from_string() );
591     # We issue that one
592     # And make another
593     my $branch_item_2 = $builder->build_sample_item({
594         itype => $itemtype->{itemtype},
595         homebranch => $branch->{branchcode},
596         holdingbranch => $branch->{branchcode}
597     });
598     is_deeply(
599         C4::Circulation::TooMany( $patron, $branch_item_2->biblionumber, $branch_item_2->unblessed ),
600         {
601             reason => 'TOO_MANY_CHECKOUTS',
602             count => 1,
603             max_allowed => 1,
604         },
605         'We are only allowed one from that branch, and have one'
606     );
607
608     # Now we make anothe from a different branch
609     my $item_2 = $builder->build_sample_item({
610         itype => $itemtype->{itemtype},
611     });
612     is_deeply(
613         C4::Circulation::TooMany( $patron, $item_2->biblionumber, $item_2->unblessed ),
614         {
615             reason => 'TOO_MANY_CHECKOUTS',
616             count => 2,
617             max_allowed => 1,
618         },
619         'We are only allowed one for general rule, and have two'
620     );
621     t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary');
622     is_deeply(
623         C4::Circulation::TooMany( $patron, $item_2->biblionumber, $item_2->unblessed ),
624         {
625             reason => 'TOO_MANY_CHECKOUTS',
626             count => 2,
627             max_allowed => 1,
628         },
629         'We are only allowed one for general rule, and have two'
630     );
631
632     t::lib::Mocks::mock_preference('CircControl', 'PickupLibrary');
633     is_deeply(
634         C4::Circulation::TooMany( $patron, $item_2->biblionumber, $item_2->unblessed ),
635         {
636             reason => 'TOO_MANY_CHECKOUTS',
637             count => 2,
638             max_allowed => 1,
639         },
640         'We are only allowed one for general rule, and have checked out two at this branch'
641     );
642
643     my $branch2   = $builder->build({source => 'Branch',});
644     t::lib::Mocks::mock_userenv({ branchcode => $branch2->{branchcode} });
645     is_deeply(
646         C4::Circulation::TooMany( $patron, $item_2->biblionumber, $item_2->unblessed ),
647         {
648             reason => 'TOO_MANY_CHECKOUTS',
649             count => 2,
650             max_allowed => 1,
651         },
652         'We are only allowed one for general rule, and have two total (no rule for specific branch)'
653     );
654     # Set a branch specific rule for new branch
655     Koha::CirculationRules->set_rules(
656         {
657             branchcode   => $branch2->{branchcode},
658             categorycode => $category->{categorycode},
659             itemtype     => $itemtype->{itemtype},
660             rules        => {
661                 maxissueqty       => 1,
662                 maxonsiteissueqty => 1,
663             }
664         }
665     );
666
667     is(
668         C4::Circulation::TooMany( $patron, $branch_item->biblionumber, $branch_item->unblessed ),
669         undef,
670         'We are allowed one from the branch specifically now'
671     );
672 };
673
674 subtest 'empty string means unlimited' => sub {
675     plan tests => 2;
676
677     Koha::CirculationRules->set_rules(
678         {
679             branchcode   => '*',
680             categorycode => '*',
681             itemtype     => '*',
682             rules        => {
683                 maxissueqty       => '',
684                 maxonsiteissueqty => '',
685             }
686         },
687     );
688     is(
689         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item ),
690         undef,
691         'maxissueqty="" should mean unlimited'
692     );
693
694     is(
695         C4::Circulation::TooMany( $patron, $biblio->{biblionumber}, $item, { onsite_checkout => 1 } ),
696         undef,
697         'maxonsiteissueqty="" should mean unlimited'
698     );
699
700     teardown();
701 };
702
703 $schema->storage->txn_rollback;
704
705 sub teardown {
706     $dbh->do(q|DELETE FROM issues|);
707     $dbh->do(q|DELETE FROM issuingrules|);
708 }
709