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