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