Bug 18936: (follow-up) fix tests, null vs. empty behavior for limiting rules
[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     my $rule = $builder->build({
504         source => 'Issuingrule',
505         value => {
506             categorycode => '*',
507             itemtype     => $itemtype->{itemtype},
508             branchcode   => '*',
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     # Set an All->All for an itemtype
517     Koha::CirculationRules->set_rules(
518         {
519             branchcode   => '*',
520             categorycode => '*',
521             itemtype     => $itemtype->{itemtype},
522             rules        => {
523                 maxissueqty       => 1,
524                 maxonsiteissueqty => 1,
525             }
526         }
527     );
528
529     # Create an item
530     my $issue_item = $builder->build_sample_item({
531         itype => $itemtype->{itemtype}
532     });
533     my $branch_item = $builder->build_sample_item({
534         itype => $itemtype->{itemtype},
535         homebranch => $branch->{branchcode},
536         holdingbranch => $branch->{branchcode}
537     });
538
539
540     t::lib::Mocks::mock_userenv({ branchcode => $branch->{branchcode} });
541     my $issue = C4::Circulation::AddIssue( $patron, $issue_item->barcode, dt_from_string() );
542     # We checkout one item
543     is_deeply(
544         C4::Circulation::TooMany( $patron, $branch_item ),
545         {
546             reason => 'TOO_MANY_CHECKOUTS',
547             count => 1,
548             max_allowed => 1,
549         },
550         'We are only allowed one, and we have one (itemtype on item)'
551     );
552
553     # Check itemtype on biblio level
554     t::lib::Mocks::mock_preference('item-level_itypes', 0);
555     $issue_item->biblio->biblioitem->itemtype($itemtype->{itemtype})->store;
556     $branch_item->biblio->biblioitem->itemtype($itemtype->{itemtype})->store;
557     # We checkout one item
558     is_deeply(
559         C4::Circulation::TooMany( $patron, $branch_item ),
560         {
561             reason => 'TOO_MANY_CHECKOUTS',
562             count => 1,
563             max_allowed => 1,
564         },
565         'We are only allowed one, and we have one (itemtype on biblioitem)'
566     );
567     t::lib::Mocks::mock_preference('item-level_itypes', 1);
568
569     # Set a branch specific rule
570     Koha::CirculationRules->set_rules(
571         {
572             branchcode   => $branch->{branchcode},
573             categorycode => $category->{categorycode},
574             itemtype     => $itemtype->{itemtype},
575             rules        => {
576                 maxissueqty       => 1,
577                 maxonsiteissueqty => 1,
578             }
579         }
580     );
581
582     is(
583         C4::Circulation::TooMany( $patron, $branch_item ),
584         undef,
585         'We are allowed one from the branch specifically now'
586     );
587
588     # If circcontrol is PatronLibrary we count all the patron's loan, regardless of branch
589     t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary');
590     is_deeply(
591         C4::Circulation::TooMany( $patron, $branch_item ),
592         {
593             reason => 'TOO_MANY_CHECKOUTS',
594             count => 1,
595             max_allowed => 1,
596         },
597         'We are allowed one from the branch specifically, but have one'
598     );
599     t::lib::Mocks::mock_preference('CircControl', 'ItemHomeLibrary');
600
601     $issue = C4::Circulation::AddIssue( $patron, $branch_item->barcode, dt_from_string() );
602     # We issue that one
603     # And make another
604     my $branch_item_2 = $builder->build_sample_item({
605         itype => $itemtype->{itemtype},
606         homebranch => $branch->{branchcode},
607         holdingbranch => $branch->{branchcode}
608     });
609     is_deeply(
610         C4::Circulation::TooMany( $patron, $branch_item_2 ),
611         {
612             reason => 'TOO_MANY_CHECKOUTS',
613             count => 1,
614             max_allowed => 1,
615         },
616         'We are only allowed one from that branch, and have one'
617     );
618
619     # Now we make anothe from a different branch
620     my $item_2 = $builder->build_sample_item({
621         itype => $itemtype->{itemtype},
622     });
623     is_deeply(
624         C4::Circulation::TooMany( $patron, $item_2 ),
625         {
626             reason => 'TOO_MANY_CHECKOUTS',
627             count => 2,
628             max_allowed => 1,
629         },
630         'We are only allowed one for general rule, and have two'
631     );
632     t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary');
633     is_deeply(
634         C4::Circulation::TooMany( $patron, $item_2 ),
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 two'
641     );
642
643     t::lib::Mocks::mock_preference('CircControl', 'PickupLibrary');
644     is_deeply(
645         C4::Circulation::TooMany( $patron, $item_2 ),
646         {
647             reason => 'TOO_MANY_CHECKOUTS',
648             count => 2,
649             max_allowed => 1,
650         },
651         'We are only allowed one for general rule, and have checked out two at this branch'
652     );
653
654     my $branch2   = $builder->build({source => 'Branch',});
655     t::lib::Mocks::mock_userenv({ branchcode => $branch2->{branchcode} });
656     is_deeply(
657         C4::Circulation::TooMany( $patron, $item_2 ),
658         {
659             reason => 'TOO_MANY_CHECKOUTS',
660             count => 2,
661             max_allowed => 1,
662         },
663         'We are only allowed one for general rule, and have two total (no rule for specific branch)'
664     );
665     # Set a branch specific rule for new branch
666     Koha::CirculationRules->set_rules(
667         {
668             branchcode   => $branch2->{branchcode},
669             categorycode => $category->{categorycode},
670             itemtype     => $itemtype->{itemtype},
671             rules        => {
672                 maxissueqty       => 1,
673                 maxonsiteissueqty => 1,
674             }
675         }
676     );
677
678     is(
679         C4::Circulation::TooMany( $patron, $branch_item ),
680         undef,
681         'We are allowed one from the branch specifically now'
682     );
683 };
684
685 subtest 'empty string means unlimited' => sub {
686     plan tests => 2;
687
688     Koha::CirculationRules->set_rules(
689         {
690             branchcode   => '*',
691             categorycode => '*',
692             itemtype     => '*',
693             rules        => {
694                 maxissueqty       => '',
695                 maxonsiteissueqty => '',
696             }
697         },
698     );
699     is(
700         C4::Circulation::TooMany( $patron, $item_object ),
701         undef,
702         'maxissueqty="" should mean unlimited'
703     );
704
705     is(
706         C4::Circulation::TooMany( $patron, $item_object, { onsite_checkout => 1 } ),
707         undef,
708         'maxonsiteissueqty="" should mean unlimited'
709     );
710
711     teardown();
712 };
713
714 $schema->storage->txn_rollback;
715
716 sub teardown {
717     $dbh->do(q|DELETE FROM issues|);
718     $dbh->do(q|DELETE FROM circulation_rules|);
719 }
720