Bug 18936: More fixes
[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 Koha::CirculationRules->search()->delete();
47
48 my $builder = t::lib::TestBuilder->new();
49 t::lib::Mocks::mock_preference('item-level_itypes', 1); # Assuming the item type is defined at item level
50
51 my $branch = $builder->build({
52     source => 'Branch',
53 });
54
55 my $category = $builder->build({
56     source => 'Category',
57 });
58
59 my $patron = $builder->build({
60     source => 'Borrower',
61     value => {
62         categorycode => $category->{categorycode},
63         branchcode => $branch->{branchcode},
64     },
65 });
66
67 my $biblio = $builder->build({
68     source => 'Biblio',
69     value => {
70         branchcode => $branch->{branchcode},
71     },
72 });
73 my $item = $builder->build({
74     source => 'Item',
75     value => {
76         biblionumber => $biblio->{biblionumber},
77         homebranch => $branch->{branchcode},
78         holdingbranch => $branch->{branchcode},
79     },
80 });
81
82 my $patron_object = Koha::Patrons->find( $patron->{borrowernumber} );
83 my $item_object = Koha::Items->find( $item->{itemnumber} );
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, $item_object ),
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, $item_object, { 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     => undef,
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, $item_object ),
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, $item_object, { 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, $item_object ),
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, $item_object, { 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     => undef,
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, $item_object ),
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, $item_object, { 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, $item_object ),
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, $item_object, { 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     => undef,
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, $item_object ),
232         undef,
233         'CO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
234     );
235     is(
236         C4::Circulation::TooMany( $patron, $item_object, { 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, $item_object ),
244         undef,
245         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
246     );
247     is(
248         C4::Circulation::TooMany( $patron, $item_object, { 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     => undef,
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, $item_object ),
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, $item_object, { 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, $item_object ),
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, $item_object, { 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     => undef,
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, $item_object ),
332         undef,
333         'CO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
334     );
335     is_deeply(
336         C4::Circulation::TooMany( $patron, $item_object, { 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, $item_object ),
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, $item_object, { 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, $item_object ),
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, $item_object, { 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, $item_object ),
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, $item_object, { 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     Koha::CirculationRules->set_rules(
426         {
427             branchcode   => $branch->{branchcode},
428             categorycode => $category->{categorycode},
429             itemtype     => undef,
430             rules        => {
431                 maxissueqty       => 1,
432                 maxonsiteissueqty => 1,
433             }
434         }
435     );
436
437     $issue = C4::Circulation::AddIssue( $patron, $item->{barcode}, dt_from_string(), undef, undef, undef, { onsite_checkout => 1 } );
438     like( $issue->issue_id, qr|^\d+$|, 'The issue should have been inserted' );
439
440     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 0);
441     is(
442         C4::Circulation::TooMany( $patron, $item_object ),
443         undef,
444         'CO should be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
445     );
446     is_deeply(
447         C4::Circulation::TooMany( $patron, $item_object, { onsite_checkout => 1 } ),
448         {
449             reason => 'TOO_MANY_ONSITE_CHECKOUTS',
450             count => 1,
451             max_allowed => 1,
452         },
453         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 0'
454     );
455
456     t::lib::Mocks::mock_preference('ConsiderOnSiteCheckoutsAsNormalCheckouts', 1);
457     is_deeply(
458         C4::Circulation::TooMany( $patron, $item_object ),
459         {
460             reason => 'TOO_MANY_CHECKOUTS',
461             count => 1,
462             max_allowed => 1,
463         },
464         'CO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
465     );
466     is_deeply(
467         C4::Circulation::TooMany( $patron, $item_object, { onsite_checkout => 1 } ),
468         {
469             reason => 'TOO_MANY_ONSITE_CHECKOUTS',
470             count => 1,
471             max_allowed => 1,
472         },
473         'OSCO should not be allowed if ConsiderOnSiteCheckoutsAsNormalCheckouts == 1'
474     );
475
476     teardown();
477 };
478
479 subtest 'General vs specific rules limit quantity correctly' => sub {
480     plan tests => 10;
481
482     t::lib::Mocks::mock_preference('CircControl', 'ItemHomeLibrary');
483     my $branch   = $builder->build({source => 'Branch',});
484     my $category = $builder->build({source => 'Category',});
485     my $itemtype = $builder->build({
486         source => 'Itemtype',
487         value => {
488             rentalcharge => 0,
489             rentalcharge_daily => 0,
490             rentalcharge_hourly => 0,
491             notforloan => 0,
492         }
493     });
494     my $patron = $builder->build({
495         source => 'Borrower',
496         value => {
497             categorycode => $category->{categorycode},
498             branchcode => $branch->{branchcode},
499         }
500     });
501
502     # Set up an issuing rule
503     Koha::CirculationRules->set_rules(
504         {
505             categorycode => '*',
506             itemtype     => $itemtype->{itemtype},
507             branchcode   => '*',
508             rules        => {
509                 issuelength => 1,
510                 firstremind => 1,        # 1 day of grace
511                 finedays    => 2,        # 2 days of fine per day of overdue
512                 lengthunit  => 'days',
513             }
514         }
515     );
516
517     # Set an All->All for an itemtype
518     Koha::CirculationRules->set_rules(
519         {
520             branchcode   => '*',
521             categorycode => '*',
522             itemtype     => $itemtype->{itemtype},
523             rules        => {
524                 maxissueqty       => 1,
525                 maxonsiteissueqty => 1,
526             }
527         }
528     );
529
530     # Create an item
531     my $issue_item = $builder->build_sample_item({
532         itype => $itemtype->{itemtype}
533     });
534     my $branch_item = $builder->build_sample_item({
535         itype => $itemtype->{itemtype},
536         homebranch => $branch->{branchcode},
537         holdingbranch => $branch->{branchcode}
538     });
539
540
541     t::lib::Mocks::mock_userenv({ branchcode => $branch->{branchcode} });
542     my $issue = C4::Circulation::AddIssue( $patron, $issue_item->barcode, dt_from_string() );
543     # We checkout one item
544     is_deeply(
545         C4::Circulation::TooMany( $patron, $branch_item ),
546         {
547             reason => 'TOO_MANY_CHECKOUTS',
548             count => 1,
549             max_allowed => 1,
550         },
551         'We are only allowed one, and we have one (itemtype on item)'
552     );
553
554     # Check itemtype on biblio level
555     t::lib::Mocks::mock_preference('item-level_itypes', 0);
556     $issue_item->biblio->biblioitem->itemtype($itemtype->{itemtype})->store;
557     $branch_item->biblio->biblioitem->itemtype($itemtype->{itemtype})->store;
558     # We checkout one item
559     is_deeply(
560         C4::Circulation::TooMany( $patron, $branch_item ),
561         {
562             reason => 'TOO_MANY_CHECKOUTS',
563             count => 1,
564             max_allowed => 1,
565         },
566         'We are only allowed one, and we have one (itemtype on biblioitem)'
567     );
568     t::lib::Mocks::mock_preference('item-level_itypes', 1);
569
570     # Set a branch specific rule
571     Koha::CirculationRules->set_rules(
572         {
573             branchcode   => $branch->{branchcode},
574             categorycode => $category->{categorycode},
575             itemtype     => $itemtype->{itemtype},
576             rules        => {
577                 maxissueqty       => 1,
578                 maxonsiteissueqty => 1,
579             }
580         }
581     );
582
583     is(
584         C4::Circulation::TooMany( $patron, $branch_item ),
585         undef,
586         'We are allowed one from the branch specifically now'
587     );
588
589     # If circcontrol is PatronLibrary we count all the patron's loan, regardless of branch
590     t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary');
591     is_deeply(
592         C4::Circulation::TooMany( $patron, $branch_item ),
593         {
594             reason => 'TOO_MANY_CHECKOUTS',
595             count => 1,
596             max_allowed => 1,
597         },
598         'We are allowed one from the branch specifically, but have one'
599     );
600     t::lib::Mocks::mock_preference('CircControl', 'ItemHomeLibrary');
601
602     $issue = C4::Circulation::AddIssue( $patron, $branch_item->barcode, dt_from_string() );
603     # We issue that one
604     # And make another
605     my $branch_item_2 = $builder->build_sample_item({
606         itype => $itemtype->{itemtype},
607         homebranch => $branch->{branchcode},
608         holdingbranch => $branch->{branchcode}
609     });
610     is_deeply(
611         C4::Circulation::TooMany( $patron, $branch_item_2 ),
612         {
613             reason => 'TOO_MANY_CHECKOUTS',
614             count => 1,
615             max_allowed => 1,
616         },
617         'We are only allowed one from that branch, and have one'
618     );
619
620     # Now we make anothe from a different branch
621     my $item_2 = $builder->build_sample_item({
622         itype => $itemtype->{itemtype},
623     });
624     is_deeply(
625         C4::Circulation::TooMany( $patron, $item_2 ),
626         {
627             reason => 'TOO_MANY_CHECKOUTS',
628             count => 2,
629             max_allowed => 1,
630         },
631         'We are only allowed one for general rule, and have two'
632     );
633     t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary');
634     is_deeply(
635         C4::Circulation::TooMany( $patron, $item_2 ),
636         {
637             reason => 'TOO_MANY_CHECKOUTS',
638             count => 2,
639             max_allowed => 1,
640         },
641         'We are only allowed one for general rule, and have two'
642     );
643
644     t::lib::Mocks::mock_preference('CircControl', 'PickupLibrary');
645     is_deeply(
646         C4::Circulation::TooMany( $patron, $item_2 ),
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 checked out two at this branch'
653     );
654
655     my $branch2   = $builder->build({source => 'Branch',});
656     t::lib::Mocks::mock_userenv({ branchcode => $branch2->{branchcode} });
657     is_deeply(
658         C4::Circulation::TooMany( $patron, $item_2 ),
659         {
660             reason => 'TOO_MANY_CHECKOUTS',
661             count => 2,
662             max_allowed => 1,
663         },
664         'We are only allowed one for general rule, and have two total (no rule for specific branch)'
665     );
666     # Set a branch specific rule for new branch
667     Koha::CirculationRules->set_rules(
668         {
669             branchcode   => $branch2->{branchcode},
670             categorycode => $category->{categorycode},
671             itemtype     => $itemtype->{itemtype},
672             rules        => {
673                 maxissueqty       => 1,
674                 maxonsiteissueqty => 1,
675             }
676         }
677     );
678
679     is(
680         C4::Circulation::TooMany( $patron, $branch_item ),
681         undef,
682         'We are allowed one from the branch specifically now'
683     );
684 };
685
686 subtest 'empty string means unlimited' => sub {
687     plan tests => 2;
688
689     Koha::CirculationRules->set_rules(
690         {
691             branchcode   => '*',
692             categorycode => '*',
693             itemtype     => '*',
694             rules        => {
695                 maxissueqty       => '',
696                 maxonsiteissueqty => '',
697             }
698         },
699     );
700     is(
701         C4::Circulation::TooMany( $patron, $item_object ),
702         undef,
703         'maxissueqty="" should mean unlimited'
704     );
705
706     is(
707         C4::Circulation::TooMany( $patron, $item_object, { onsite_checkout => 1 } ),
708         undef,
709         'maxonsiteissueqty="" should mean unlimited'
710     );
711
712     teardown();
713 };
714
715 $schema->storage->txn_rollback;
716
717 sub teardown {
718     $dbh->do(q|DELETE FROM issues|);
719     $dbh->do(q|DELETE FROM circulation_rules|);
720 }
721