Bug 17680: C4::Circulation - Remove GetItemIssue, simple calls
[koha.git] / t / db_dependent / Circulation.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
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18 use Modern::Perl;
19
20 use Test::More tests => 95;
21
22 use DateTime;
23
24 use t::lib::Mocks;
25 use t::lib::TestBuilder;
26
27 use C4::Circulation;
28 use C4::Biblio;
29 use C4::Items;
30 use C4::Log;
31 use C4::Members;
32 use C4::Reserves;
33 use C4::Overdues qw(UpdateFine CalcFine);
34 use Koha::DateUtils;
35 use Koha::Database;
36 use Koha::IssuingRules;
37 use Koha::Checkouts;
38 use Koha::Subscriptions;
39
40 my $schema = Koha::Database->schema;
41 $schema->storage->txn_begin;
42 my $builder = t::lib::TestBuilder->new;
43 my $dbh = C4::Context->dbh;
44
45 # Start transaction
46 $dbh->{RaiseError} = 1;
47
48 # Start with a clean slate
49 $dbh->do('DELETE FROM issues');
50
51 my $library = $builder->build({
52     source => 'Branch',
53 });
54 my $library2 = $builder->build({
55     source => 'Branch',
56 });
57 my $itemtype = $builder->build(
58     {   source => 'Itemtype',
59         value  => { notforloan => undef, rentalcharge => 0 }
60     }
61 )->{itemtype};
62 my $patron_category = $builder->build({ source => 'Category', value => { enrolmentfee => 0 } });
63
64 my $CircControl = C4::Context->preference('CircControl');
65 my $HomeOrHoldingBranch = C4::Context->preference('HomeOrHoldingBranch');
66
67 my $item = {
68     homebranch => $library2->{branchcode},
69     holdingbranch => $library2->{branchcode}
70 };
71
72 my $borrower = {
73     branchcode => $library2->{branchcode}
74 };
75
76 # No userenv, PickupLibrary
77 t::lib::Mocks::mock_preference('IndependentBranches', '0');
78 t::lib::Mocks::mock_preference('CircControl', 'PickupLibrary');
79 is(
80     C4::Context->preference('CircControl'),
81     'PickupLibrary',
82     'CircControl changed to PickupLibrary'
83 );
84 is(
85     C4::Circulation::_GetCircControlBranch($item, $borrower),
86     $item->{$HomeOrHoldingBranch},
87     '_GetCircControlBranch returned item branch (no userenv defined)'
88 );
89
90 # No userenv, PatronLibrary
91 t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary');
92 is(
93     C4::Context->preference('CircControl'),
94     'PatronLibrary',
95     'CircControl changed to PatronLibrary'
96 );
97 is(
98     C4::Circulation::_GetCircControlBranch($item, $borrower),
99     $borrower->{branchcode},
100     '_GetCircControlBranch returned borrower branch'
101 );
102
103 # No userenv, ItemHomeLibrary
104 t::lib::Mocks::mock_preference('CircControl', 'ItemHomeLibrary');
105 is(
106     C4::Context->preference('CircControl'),
107     'ItemHomeLibrary',
108     'CircControl changed to ItemHomeLibrary'
109 );
110 is(
111     $item->{$HomeOrHoldingBranch},
112     C4::Circulation::_GetCircControlBranch($item, $borrower),
113     '_GetCircControlBranch returned item branch'
114 );
115
116 # Now, set a userenv
117 C4::Context->_new_userenv('xxx');
118 C4::Context->set_userenv(0,0,0,'firstname','surname', $library2->{branchcode}, 'Midway Public Library', '', '', '');
119 is(C4::Context->userenv->{branch}, $library2->{branchcode}, 'userenv set');
120
121 # Userenv set, PickupLibrary
122 t::lib::Mocks::mock_preference('CircControl', 'PickupLibrary');
123 is(
124     C4::Context->preference('CircControl'),
125     'PickupLibrary',
126     'CircControl changed to PickupLibrary'
127 );
128 is(
129     C4::Circulation::_GetCircControlBranch($item, $borrower),
130     $library2->{branchcode},
131     '_GetCircControlBranch returned current branch'
132 );
133
134 # Userenv set, PatronLibrary
135 t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary');
136 is(
137     C4::Context->preference('CircControl'),
138     'PatronLibrary',
139     'CircControl changed to PatronLibrary'
140 );
141 is(
142     C4::Circulation::_GetCircControlBranch($item, $borrower),
143     $borrower->{branchcode},
144     '_GetCircControlBranch returned borrower branch'
145 );
146
147 # Userenv set, ItemHomeLibrary
148 t::lib::Mocks::mock_preference('CircControl', 'ItemHomeLibrary');
149 is(
150     C4::Context->preference('CircControl'),
151     'ItemHomeLibrary',
152     'CircControl changed to ItemHomeLibrary'
153 );
154 is(
155     C4::Circulation::_GetCircControlBranch($item, $borrower),
156     $item->{$HomeOrHoldingBranch},
157     '_GetCircControlBranch returned item branch'
158 );
159
160 # Reset initial configuration
161 t::lib::Mocks::mock_preference('CircControl', $CircControl);
162 is(
163     C4::Context->preference('CircControl'),
164     $CircControl,
165     'CircControl reset to its initial value'
166 );
167
168 # Set a simple circ policy
169 $dbh->do('DELETE FROM issuingrules');
170 $dbh->do(
171     q{INSERT INTO issuingrules (categorycode, branchcode, itemtype, reservesallowed,
172                                 maxissueqty, issuelength, lengthunit,
173                                 renewalsallowed, renewalperiod,
174                                 norenewalbefore, auto_renew,
175                                 fine, chargeperiod)
176       VALUES (?, ?, ?, ?,
177               ?, ?, ?,
178               ?, ?,
179               ?, ?,
180               ?, ?
181              )
182     },
183     {},
184     '*', '*', '*', 25,
185     20, 14, 'days',
186     1, 7,
187     undef, 0,
188     .10, 1
189 );
190
191 # Test C4::Circulation::ProcessOfflinePayment
192 my $sth = C4::Context->dbh->prepare("SELECT COUNT(*) FROM accountlines WHERE amount = '-123.45' AND accounttype = 'Pay'");
193 $sth->execute();
194 my ( $original_count ) = $sth->fetchrow_array();
195
196 C4::Context->dbh->do("INSERT INTO borrowers ( cardnumber, surname, firstname, categorycode, branchcode ) VALUES ( '99999999999', 'Hall', 'Kyle', ?, ? )", undef, $patron_category->{categorycode}, $library2->{branchcode} );
197
198 C4::Circulation::ProcessOfflinePayment({ cardnumber => '99999999999', amount => '123.45' });
199
200 $sth->execute();
201 my ( $new_count ) = $sth->fetchrow_array();
202
203 ok( $new_count == $original_count  + 1, 'ProcessOfflinePayment makes payment correctly' );
204
205 C4::Context->dbh->do("DELETE FROM accountlines WHERE borrowernumber IN ( SELECT borrowernumber FROM borrowers WHERE cardnumber = '99999999999' )");
206 C4::Context->dbh->do("DELETE FROM borrowers WHERE cardnumber = '99999999999'");
207 C4::Context->dbh->do("DELETE FROM accountlines");
208 {
209 # CanBookBeRenewed tests
210
211     # Generate test biblio
212     my $biblio = MARC::Record->new();
213     my $title = 'Silence in the library';
214     $biblio->append_fields(
215         MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'),
216         MARC::Field->new('245', ' ', ' ', a => $title),
217     );
218
219     my ($biblionumber, $biblioitemnumber) = AddBiblio($biblio, '');
220
221     my $barcode = 'R00000342';
222     my $branch = $library2->{branchcode};
223
224     my ( $item_bibnum, $item_bibitemnum, $itemnumber ) = AddItem(
225         {
226             homebranch       => $branch,
227             holdingbranch    => $branch,
228             barcode          => $barcode,
229             replacementprice => 12.00,
230             itype            => $itemtype
231         },
232         $biblionumber
233     );
234
235     my $barcode2 = 'R00000343';
236     my ( $item_bibnum2, $item_bibitemnum2, $itemnumber2 ) = AddItem(
237         {
238             homebranch       => $branch,
239             holdingbranch    => $branch,
240             barcode          => $barcode2,
241             replacementprice => 23.00,
242             itype            => $itemtype
243         },
244         $biblionumber
245     );
246
247     my $barcode3 = 'R00000346';
248     my ( $item_bibnum3, $item_bibitemnum3, $itemnumber3 ) = AddItem(
249         {
250             homebranch       => $branch,
251             holdingbranch    => $branch,
252             barcode          => $barcode3,
253             replacementprice => 23.00,
254             itype            => $itemtype
255         },
256         $biblionumber
257     );
258
259
260
261
262     # Create borrowers
263     my %renewing_borrower_data = (
264         firstname =>  'John',
265         surname => 'Renewal',
266         categorycode => $patron_category->{categorycode},
267         branchcode => $branch,
268     );
269
270     my %reserving_borrower_data = (
271         firstname =>  'Katrin',
272         surname => 'Reservation',
273         categorycode => $patron_category->{categorycode},
274         branchcode => $branch,
275     );
276
277     my %hold_waiting_borrower_data = (
278         firstname =>  'Kyle',
279         surname => 'Reservation',
280         categorycode => $patron_category->{categorycode},
281         branchcode => $branch,
282     );
283
284     my %restricted_borrower_data = (
285         firstname =>  'Alice',
286         surname => 'Reservation',
287         categorycode => $patron_category->{categorycode},
288         debarred => '3228-01-01',
289         branchcode => $branch,
290     );
291
292     my $renewing_borrowernumber = AddMember(%renewing_borrower_data);
293     my $reserving_borrowernumber = AddMember(%reserving_borrower_data);
294     my $hold_waiting_borrowernumber = AddMember(%hold_waiting_borrower_data);
295     my $restricted_borrowernumber = AddMember(%restricted_borrower_data);
296
297     my $renewing_borrower = GetMember( borrowernumber => $renewing_borrowernumber );
298     my $restricted_borrower = GetMember( borrowernumber => $restricted_borrowernumber );
299
300     my $bibitems       = '';
301     my $priority       = '1';
302     my $resdate        = undef;
303     my $expdate        = undef;
304     my $notes          = '';
305     my $checkitem      = undef;
306     my $found          = undef;
307
308     my $issue = AddIssue( $renewing_borrower, $barcode);
309     my $datedue = dt_from_string( $issue->date_due() );
310     is (defined $issue->date_due(), 1, "Item 1 checked out, due date: " . $issue->date_due() );
311
312     my $issue2 = AddIssue( $renewing_borrower, $barcode2);
313     $datedue = dt_from_string( $issue->date_due() );
314     is (defined $issue2, 1, "Item 2 checked out, due date: " . $issue2->date_due());
315
316
317     my $borrowing_borrowernumber = Koha::Checkouts->find( { itemnumber => $itemnumber } )->borrowernumber;
318     is ($borrowing_borrowernumber, $renewing_borrowernumber, "Item checked out to $renewing_borrower->{firstname} $renewing_borrower->{surname}");
319
320     my ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber, 1);
321     is( $renewokay, 1, 'Can renew, no holds for this title or item');
322
323
324     # Biblio-level hold, renewal test
325     AddReserve(
326         $branch, $reserving_borrowernumber, $biblionumber,
327         $bibitems,  $priority, $resdate, $expdate, $notes,
328         $title, $checkitem, $found
329     );
330
331     # Testing of feature to allow the renewal of reserved items if other items on the record can fill all needed holds
332     C4::Context->dbh->do("UPDATE issuingrules SET onshelfholds = 1");
333     t::lib::Mocks::mock_preference('AllowRenewalIfOtherItemsAvailable', 1 );
334     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber);
335     is( $renewokay, 1, 'Bug 11634 - Allow renewal of item with unfilled holds if other available items can fill those holds');
336     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber2);
337     is( $renewokay, 1, 'Bug 11634 - Allow renewal of item with unfilled holds if other available items can fill those holds');
338
339     # Now let's add an item level hold, we should no longer be able to renew the item
340     my $hold = Koha::Database->new()->schema()->resultset('Reserve')->create(
341         {
342             borrowernumber => $hold_waiting_borrowernumber,
343             biblionumber   => $biblionumber,
344             itemnumber     => $itemnumber,
345             branchcode     => $branch,
346             priority       => 3,
347         }
348     );
349     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber);
350     is( $renewokay, 0, 'Bug 13919 - Renewal possible with item level hold on item');
351     $hold->delete();
352
353     # Now let's add a waiting hold on the 3rd item, it's no longer available tp check out by just anyone, so we should no longer
354     # be able to renew these items
355     $hold = Koha::Database->new()->schema()->resultset('Reserve')->create(
356         {
357             borrowernumber => $hold_waiting_borrowernumber,
358             biblionumber   => $biblionumber,
359             itemnumber     => $itemnumber3,
360             branchcode     => $branch,
361             priority       => 0,
362             found          => 'W'
363         }
364     );
365     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber);
366     is( $renewokay, 0, 'Bug 11634 - Allow renewal of item with unfilled holds if other available items can fill those holds');
367     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber2);
368     is( $renewokay, 0, 'Bug 11634 - Allow renewal of item with unfilled holds if other available items can fill those holds');
369     t::lib::Mocks::mock_preference('AllowRenewalIfOtherItemsAvailable', 0 );
370
371     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber);
372     is( $renewokay, 0, '(Bug 10663) Cannot renew, reserved');
373     is( $error, 'on_reserve', '(Bug 10663) Cannot renew, reserved (returned error is on_reserve)');
374
375     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber2);
376     is( $renewokay, 0, '(Bug 10663) Cannot renew, reserved');
377     is( $error, 'on_reserve', '(Bug 10663) Cannot renew, reserved (returned error is on_reserve)');
378
379     my $reserveid = C4::Reserves::GetReserveId({ biblionumber => $biblionumber, borrowernumber => $reserving_borrowernumber});
380     my $reserving_borrower = GetMember( borrowernumber => $reserving_borrowernumber );
381     AddIssue($reserving_borrower, $barcode3);
382     my $reserve = $dbh->selectrow_hashref(
383         'SELECT * FROM old_reserves WHERE reserve_id = ?',
384         { Slice => {} },
385         $reserveid
386     );
387     is($reserve->{found}, 'F', 'hold marked completed when checking out item that fills it');
388
389     # Item-level hold, renewal test
390     AddReserve(
391         $branch, $reserving_borrowernumber, $biblionumber,
392         $bibitems,  $priority, $resdate, $expdate, $notes,
393         $title, $itemnumber, $found
394     );
395
396     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber, 1);
397     is( $renewokay, 0, '(Bug 10663) Cannot renew, item reserved');
398     is( $error, 'on_reserve', '(Bug 10663) Cannot renew, item reserved (returned error is on_reserve)');
399
400     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber2, 1);
401     is( $renewokay, 1, 'Can renew item 2, item-level hold is on item 1');
402
403     # Items can't fill hold for reasons
404     ModItem({ notforloan => 1 }, $biblionumber, $itemnumber);
405     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber, 1);
406     is( $renewokay, 1, 'Can renew, item is marked not for loan, hold does not block');
407     ModItem({ notforloan => 0, itype => $itemtype }, $biblionumber, $itemnumber,1);
408
409     # FIXME: Add more for itemtype not for loan etc.
410
411     # Restricted users cannot renew when RestrictionBlockRenewing is enabled
412     my $barcode5 = 'R00000347';
413     my ( $item_bibnum5, $item_bibitemnum5, $itemnumber5 ) = AddItem(
414         {
415             homebranch       => $branch,
416             holdingbranch    => $branch,
417             barcode          => $barcode5,
418             replacementprice => 23.00,
419             itype            => $itemtype
420         },
421         $biblionumber
422     );
423     my $datedue5 = AddIssue($restricted_borrower, $barcode5);
424     is (defined $datedue5, 1, "Item with date due checked out, due date: $datedue5");
425
426     t::lib::Mocks::mock_preference('RestrictionBlockRenewing','1');
427     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber2);
428     is( $renewokay, 1, '(Bug 8236), Can renew, user is not restricted');
429     ( $renewokay, $error ) = CanBookBeRenewed($restricted_borrowernumber, $itemnumber5);
430     is( $renewokay, 0, '(Bug 8236), Cannot renew, user is restricted');
431
432     # Users cannot renew an overdue item
433     my $barcode6 = 'R00000348';
434     my ( $item_bibnum6, $item_bibitemnum6, $itemnumber6 ) = AddItem(
435         {
436             homebranch       => $branch,
437             holdingbranch    => $branch,
438             barcode          => $barcode6,
439             replacementprice => 23.00,
440             itype            => $itemtype
441         },
442         $biblionumber
443     );
444
445     my $barcode7 = 'R00000349';
446     my ( $item_bibnum7, $item_bibitemnum7, $itemnumber7 ) = AddItem(
447         {
448             homebranch       => $branch,
449             holdingbranch    => $branch,
450             barcode          => $barcode7,
451             replacementprice => 23.00,
452             itype            => $itemtype
453         },
454         $biblionumber
455     );
456     my $datedue6 = AddIssue( $renewing_borrower, $barcode6);
457     is (defined $datedue6, 1, "Item 2 checked out, due date: $datedue6");
458
459     my $now = dt_from_string();
460     my $five_weeks = DateTime::Duration->new(weeks => 5);
461     my $five_weeks_ago = $now - $five_weeks;
462
463     my $passeddatedue1 = AddIssue($renewing_borrower, $barcode7, $five_weeks_ago);
464     is (defined $passeddatedue1, 1, "Item with passed date due checked out, due date: " . $passeddatedue1->date_due);
465
466     my ( $fine ) = CalcFine( GetItem(undef, $barcode7), $renewing_borrower->{categorycode}, $branch, $five_weeks_ago, $now );
467     C4::Overdues::UpdateFine(
468         {
469             issue_id       => $passeddatedue1->id(),
470             itemnumber     => $itemnumber7,
471             borrowernumber => $renewing_borrower->{borrowernumber},
472             amount         => $fine,
473             type           => 'FU',
474             due            => Koha::DateUtils::output_pref($five_weeks_ago)
475         }
476     );
477     t::lib::Mocks::mock_preference('RenewalLog', 0);
478     my $date = output_pref( { dt => dt_from_string(), datenonly => 1, dateformat => 'iso' } );
479     my $old_log_size =  scalar(@{GetLogs( $date, $date, undef,["CIRCULATION"], ["RENEWAL"]) } );
480     AddRenewal( $renewing_borrower->{borrowernumber}, $itemnumber7, $branch );
481     my $new_log_size =  scalar(@{GetLogs( $date, $date, undef,["CIRCULATION"], ["RENEWAL"]) } );
482     is ($new_log_size, $old_log_size, 'renew log not added because of the syspref RenewalLog');
483
484     t::lib::Mocks::mock_preference('RenewalLog', 1);
485     $date = output_pref( { dt => dt_from_string(), datenonly => 1, dateformat => 'iso' } );
486     $old_log_size =  scalar(@{GetLogs( $date, $date, undef,["CIRCULATION"], ["RENEWAL"]) } );
487     AddRenewal( $renewing_borrower->{borrowernumber}, $itemnumber7, $branch );
488     $new_log_size =  scalar(@{GetLogs( $date, $date, undef,["CIRCULATION"], ["RENEWAL"]) } );
489     is ($new_log_size, $old_log_size + 1, 'renew log successfully added');
490
491
492     $fine = $schema->resultset('Accountline')->single( { borrowernumber => $renewing_borrower->{borrowernumber}, itemnumber => $itemnumber7 } );
493     is( $fine->accounttype, 'F', 'Fine on renewed item is closed out properly' );
494     $fine->delete();
495
496     t::lib::Mocks::mock_preference('OverduesBlockRenewing','blockitem');
497     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber6);
498     is( $renewokay, 1, '(Bug 8236), Can renew, this item is not overdue');
499     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber7);
500     is( $renewokay, 0, '(Bug 8236), Cannot renew, this item is overdue');
501
502
503     $reserveid = C4::Reserves::GetReserveId({ biblionumber => $biblionumber, itemnumber => $itemnumber, borrowernumber => $reserving_borrowernumber});
504     CancelReserve({ reserve_id => $reserveid });
505
506     # Bug 14101
507     # Test automatic renewal before value for "norenewalbefore" in policy is set
508     # In this case automatic renewal is not permitted prior to due date
509     my $barcode4 = '11235813';
510     my ( $item_bibnum4, $item_bibitemnum4, $itemnumber4 ) = AddItem(
511         {
512             homebranch       => $branch,
513             holdingbranch    => $branch,
514             barcode          => $barcode4,
515             replacementprice => 16.00,
516             itype            => $itemtype
517         },
518         $biblionumber
519     );
520
521     $issue = AddIssue( $renewing_borrower, $barcode4, undef, undef, undef, undef, { auto_renew => 1 } );
522     ( $renewokay, $error ) =
523       CanBookBeRenewed( $renewing_borrowernumber, $itemnumber4 );
524     is( $renewokay, 0, 'Bug 14101: Cannot renew, renewal is automatic and premature' );
525     is( $error, 'auto_too_soon',
526         'Bug 14101: Cannot renew, renewal is automatic and premature, "No renewal before" = undef (returned code is auto_too_soon)' );
527
528     # Bug 7413
529     # Test premature manual renewal
530     $dbh->do('UPDATE issuingrules SET norenewalbefore = 7');
531
532     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber);
533     is( $renewokay, 0, 'Bug 7413: Cannot renew, renewal is premature');
534     is( $error, 'too_soon', 'Bug 7413: Cannot renew, renewal is premature (returned code is too_soon)');
535
536     # Bug 14395
537     # Test 'exact time' setting for syspref NoRenewalBeforePrecision
538     t::lib::Mocks::mock_preference( 'NoRenewalBeforePrecision', 'exact_time' );
539     is(
540         GetSoonestRenewDate( $renewing_borrowernumber, $itemnumber ),
541         $datedue->clone->add( days => -7 ),
542         'Bug 14395: Renewals permitted 7 days before due date, as expected'
543     );
544
545     # Bug 14395
546     # Test 'date' setting for syspref NoRenewalBeforePrecision
547     t::lib::Mocks::mock_preference( 'NoRenewalBeforePrecision', 'date' );
548     is(
549         GetSoonestRenewDate( $renewing_borrowernumber, $itemnumber ),
550         $datedue->clone->add( days => -7 )->truncate( to => 'day' ),
551         'Bug 14395: Renewals permitted 7 days before due date, as expected'
552     );
553
554     # Bug 14101
555     # Test premature automatic renewal
556     ( $renewokay, $error ) =
557       CanBookBeRenewed( $renewing_borrowernumber, $itemnumber4 );
558     is( $renewokay, 0, 'Bug 14101: Cannot renew, renewal is automatic and premature' );
559     is( $error, 'auto_too_soon',
560         'Bug 14101: Cannot renew, renewal is automatic and premature (returned code is auto_too_soon)'
561     );
562
563     # Change policy so that loans can only be renewed exactly on due date (0 days prior to due date)
564     # and test automatic renewal again
565     $dbh->do('UPDATE issuingrules SET norenewalbefore = 0');
566     ( $renewokay, $error ) =
567       CanBookBeRenewed( $renewing_borrowernumber, $itemnumber4 );
568     is( $renewokay, 0, 'Bug 14101: Cannot renew, renewal is automatic and premature' );
569     is( $error, 'auto_too_soon',
570         'Bug 14101: Cannot renew, renewal is automatic and premature, "No renewal before" = 0 (returned code is auto_too_soon)'
571     );
572
573     # Change policy so that loans can be renewed 99 days prior to the due date
574     # and test automatic renewal again
575     $dbh->do('UPDATE issuingrules SET norenewalbefore = 99');
576     ( $renewokay, $error ) =
577       CanBookBeRenewed( $renewing_borrowernumber, $itemnumber4 );
578     is( $renewokay, 0, 'Bug 14101: Cannot renew, renewal is automatic' );
579     is( $error, 'auto_renew',
580         'Bug 14101: Cannot renew, renewal is automatic (returned code is auto_renew)'
581     );
582
583     subtest "too_late_renewal / no_auto_renewal_after" => sub {
584         plan tests => 14;
585         my $item_to_auto_renew = $builder->build(
586             {   source => 'Item',
587                 value  => {
588                     biblionumber  => $biblionumber,
589                     homebranch    => $branch,
590                     holdingbranch => $branch,
591                 }
592             }
593         );
594
595         my $ten_days_before = dt_from_string->add( days => -10 );
596         my $ten_days_ahead  = dt_from_string->add( days => 10 );
597         AddIssue( $renewing_borrower, $item_to_auto_renew->{barcode}, $ten_days_ahead, undef, $ten_days_before, undef, { auto_renew => 1 } );
598
599         $dbh->do('UPDATE issuingrules SET norenewalbefore = 7, no_auto_renewal_after = 9');
600         ( $renewokay, $error ) =
601           CanBookBeRenewed( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
602         is( $renewokay, 0, 'Do not renew, renewal is automatic' );
603         is( $error, 'auto_too_late', 'Cannot renew, too late(returned code is auto_too_late)' );
604
605         $dbh->do('UPDATE issuingrules SET norenewalbefore = 7, no_auto_renewal_after = 10');
606         ( $renewokay, $error ) =
607           CanBookBeRenewed( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
608         is( $renewokay, 0, 'Do not renew, renewal is automatic' );
609         is( $error, 'auto_too_late', 'Cannot auto renew, too late - no_auto_renewal_after is inclusive(returned code is auto_too_late)' );
610
611         $dbh->do('UPDATE issuingrules SET norenewalbefore = 7, no_auto_renewal_after = 11');
612         ( $renewokay, $error ) =
613           CanBookBeRenewed( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
614         is( $renewokay, 0, 'Do not renew, renewal is automatic' );
615         is( $error, 'auto_too_soon', 'Cannot auto renew, too soon - no_auto_renewal_after is defined(returned code is auto_too_soon)' );
616
617         $dbh->do('UPDATE issuingrules SET norenewalbefore = 10, no_auto_renewal_after = 11');
618         ( $renewokay, $error ) =
619           CanBookBeRenewed( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
620         is( $renewokay, 0,            'Do not renew, renewal is automatic' );
621         is( $error,     'auto_renew', 'Cannot renew, renew is automatic' );
622
623         $dbh->do('UPDATE issuingrules SET norenewalbefore = 7, no_auto_renewal_after = NULL, no_auto_renewal_after_hard_limit = ?', undef, dt_from_string->add( days => -1 ) );
624         ( $renewokay, $error ) =
625           CanBookBeRenewed( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
626         is( $renewokay, 0, 'Do not renew, renewal is automatic' );
627         is( $error, 'auto_too_late', 'Cannot renew, too late(returned code is auto_too_late)' );
628
629         $dbh->do('UPDATE issuingrules SET norenewalbefore = 7, no_auto_renewal_after = 15, no_auto_renewal_after_hard_limit = ?', undef, dt_from_string->add( days => -1 ) );
630         ( $renewokay, $error ) =
631           CanBookBeRenewed( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
632         is( $renewokay, 0, 'Do not renew, renewal is automatic' );
633         is( $error, 'auto_too_late', 'Cannot renew, too late(returned code is auto_too_late)' );
634
635         $dbh->do('UPDATE issuingrules SET norenewalbefore = 10, no_auto_renewal_after = NULL, no_auto_renewal_after_hard_limit = ?', undef, dt_from_string->add( days => 1 ) );
636         ( $renewokay, $error ) =
637           CanBookBeRenewed( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
638         is( $renewokay, 0, 'Do not renew, renewal is automatic' );
639         is( $error, 'auto_renew', 'Cannot renew, renew is automatic' );
640     };
641
642     subtest "auto_too_much_oweing | OPACFineNoRenewalsBlockAutoRenew" => sub {
643         plan tests => 6;
644         my $item_to_auto_renew = $builder->build({
645             source => 'Item',
646             value => {
647                 biblionumber => $biblionumber,
648                 homebranch       => $branch,
649                 holdingbranch    => $branch,
650             }
651         });
652
653         my $ten_days_before = dt_from_string->add( days => -10 );
654         my $ten_days_ahead = dt_from_string->add( days => 10 );
655         AddIssue( $renewing_borrower, $item_to_auto_renew->{barcode}, $ten_days_ahead, undef, $ten_days_before, undef, { auto_renew => 1 } );
656
657         $dbh->do('UPDATE issuingrules SET norenewalbefore = 10, no_auto_renewal_after = 11');
658         C4::Context->set_preference('OPACFineNoRenewalsBlockAutoRenew','1');
659         C4::Context->set_preference('OPACFineNoRenewals','10');
660         my $fines_amount = 5;
661         C4::Accounts::manualinvoice( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber}, "Some fines", 'F', $fines_amount );
662         ( $renewokay, $error ) =
663           CanBookBeRenewed( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
664         is( $renewokay, 0, 'Do not renew, renewal is automatic' );
665         is( $error, 'auto_renew', 'Can auto renew, OPACFineNoRenewals=10, patron has 5' );
666
667         C4::Accounts::manualinvoice( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber}, "Some fines", 'F', $fines_amount );
668         ( $renewokay, $error ) =
669           CanBookBeRenewed( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
670         is( $renewokay, 0, 'Do not renew, renewal is automatic' );
671         is( $error, 'auto_renew', 'Can auto renew, OPACFineNoRenewals=10, patron has 10' );
672
673         C4::Accounts::manualinvoice( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber}, "Some fines", 'F', $fines_amount );
674         ( $renewokay, $error ) =
675           CanBookBeRenewed( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
676         is( $renewokay, 0, 'Do not renew, renewal is automatic' );
677         is( $error, 'auto_too_much_oweing', 'Cannot auto renew, OPACFineNoRenewals=10, patron has 15' );
678
679         $dbh->do('DELETE FROM accountlines WHERE borrowernumber=?', undef, $renewing_borrowernumber);
680     };
681
682     subtest "GetLatestAutoRenewDate" => sub {
683         plan tests => 5;
684         my $item_to_auto_renew = $builder->build(
685             {   source => 'Item',
686                 value  => {
687                     biblionumber  => $biblionumber,
688                     homebranch    => $branch,
689                     holdingbranch => $branch,
690                 }
691             }
692         );
693
694         my $ten_days_before = dt_from_string->add( days => -10 );
695         my $ten_days_ahead  = dt_from_string->add( days => 10 );
696         AddIssue( $renewing_borrower, $item_to_auto_renew->{barcode}, $ten_days_ahead, undef, $ten_days_before, undef, { auto_renew => 1 } );
697         $dbh->do('UPDATE issuingrules SET norenewalbefore = 7, no_auto_renewal_after = "", no_auto_renewal_after_hard_limit = NULL');
698         my $latest_auto_renew_date = GetLatestAutoRenewDate( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
699         is( $latest_auto_renew_date, undef, 'GetLatestAutoRenewDate should return undef if no_auto_renewal_after or no_auto_renewal_after_hard_limit are not defined' );
700         my $five_days_before = dt_from_string->add( days => -5 );
701         $dbh->do('UPDATE issuingrules SET norenewalbefore = 10, no_auto_renewal_after = 5, no_auto_renewal_after_hard_limit = NULL');
702         $latest_auto_renew_date = GetLatestAutoRenewDate( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
703         is( $latest_auto_renew_date->truncate( to => 'minute' ),
704             $five_days_before->truncate( to => 'minute' ),
705             'GetLatestAutoRenewDate should return -5 days if no_auto_renewal_after = 5 and date_due is 10 days before'
706         );
707         my $five_days_ahead = dt_from_string->add( days => 5 );
708         $dbh->do('UPDATE issuingrules SET norenewalbefore = 10, no_auto_renewal_after = 15, no_auto_renewal_after_hard_limit = NULL');
709         $latest_auto_renew_date = GetLatestAutoRenewDate( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
710         is( $latest_auto_renew_date->truncate( to => 'minute' ),
711             $five_days_ahead->truncate( to => 'minute' ),
712             'GetLatestAutoRenewDate should return +5 days if no_auto_renewal_after = 15 and date_due is 10 days before'
713         );
714         my $two_days_ahead = dt_from_string->add( days => 2 );
715         $dbh->do('UPDATE issuingrules SET norenewalbefore = 10, no_auto_renewal_after = "", no_auto_renewal_after_hard_limit = ?', undef, dt_from_string->add( days => 2 ) );
716         $latest_auto_renew_date = GetLatestAutoRenewDate( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
717         is( $latest_auto_renew_date->truncate( to => 'day' ),
718             $two_days_ahead->truncate( to => 'day' ),
719             'GetLatestAutoRenewDate should return +2 days if no_auto_renewal_after_hard_limit is defined and not no_auto_renewal_after'
720         );
721         $dbh->do('UPDATE issuingrules SET norenewalbefore = 10, no_auto_renewal_after = 15, no_auto_renewal_after_hard_limit = ?', undef, dt_from_string->add( days => 2 ) );
722         $latest_auto_renew_date = GetLatestAutoRenewDate( $renewing_borrowernumber, $item_to_auto_renew->{itemnumber} );
723         is( $latest_auto_renew_date->truncate( to => 'day' ),
724             $two_days_ahead->truncate( to => 'day' ),
725             'GetLatestAutoRenewDate should return +2 days if no_auto_renewal_after_hard_limit is < no_auto_renewal_after'
726         );
727
728     };
729
730     # Too many renewals
731
732     # set policy to forbid renewals
733     $dbh->do('UPDATE issuingrules SET norenewalbefore = NULL, renewalsallowed = 0');
734
735     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber);
736     is( $renewokay, 0, 'Cannot renew, 0 renewals allowed');
737     is( $error, 'too_many', 'Cannot renew, 0 renewals allowed (returned code is too_many)');
738
739     # Test WhenLostForgiveFine and WhenLostChargeReplacementFee
740     t::lib::Mocks::mock_preference('WhenLostForgiveFine','1');
741     t::lib::Mocks::mock_preference('WhenLostChargeReplacementFee','1');
742
743     C4::Overdues::UpdateFine(
744         {
745             issue_id       => $issue->id(),
746             itemnumber     => $itemnumber,
747             borrowernumber => $renewing_borrower->{borrowernumber},
748             amount         => 15.00,
749             type           => q{},
750             due            => Koha::DateUtils::output_pref($datedue)
751         }
752     );
753
754     LostItem( $itemnumber, 1 );
755
756     my $item = Koha::Database->new()->schema()->resultset('Item')->find($itemnumber);
757     ok( !$item->onloan(), "Lost item marked as returned has false onloan value" );
758
759     my $total_due = $dbh->selectrow_array(
760         'SELECT SUM( amountoutstanding ) FROM accountlines WHERE borrowernumber = ?',
761         undef, $renewing_borrower->{borrowernumber}
762     );
763
764     ok( $total_due == 12, 'Borrower only charged replacement fee with both WhenLostForgiveFine and WhenLostChargeReplacementFee enabled' );
765
766     C4::Context->dbh->do("DELETE FROM accountlines");
767
768     t::lib::Mocks::mock_preference('WhenLostForgiveFine','0');
769     t::lib::Mocks::mock_preference('WhenLostChargeReplacementFee','0');
770
771     C4::Overdues::UpdateFine(
772         {
773             issue_id       => $issue2->id(),
774             itemnumber     => $itemnumber2,
775             borrowernumber => $renewing_borrower->{borrowernumber},
776             amount         => 15.00,
777             type           => q{},
778             due            => Koha::DateUtils::output_pref($datedue)
779         }
780     );
781
782     LostItem( $itemnumber2, 0 );
783
784     my $item2 = Koha::Database->new()->schema()->resultset('Item')->find($itemnumber2);
785     ok( $item2->onloan(), "Lost item *not* marked as returned has true onloan value" );
786
787     $total_due = $dbh->selectrow_array(
788         'SELECT SUM( amountoutstanding ) FROM accountlines WHERE borrowernumber = ?',
789         undef, $renewing_borrower->{borrowernumber}
790     );
791
792     ok( $total_due == 15, 'Borrower only charged fine with both WhenLostForgiveFine and WhenLostChargeReplacementFee disabled' );
793
794     my $future = dt_from_string();
795     $future->add( days => 7 );
796     my $units = C4::Overdues::get_chargeable_units('days', $future, $now, $library2->{branchcode});
797     ok( $units == 0, '_get_chargeable_units returns 0 for items not past due date (Bug 12596)' );
798
799     # Users cannot renew any item if there is an overdue item
800     t::lib::Mocks::mock_preference('OverduesBlockRenewing','block');
801     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber6);
802     is( $renewokay, 0, '(Bug 8236), Cannot renew, one of the items is overdue');
803     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber7);
804     is( $renewokay, 0, '(Bug 8236), Cannot renew, one of the items is overdue');
805
806   }
807
808 {
809     # GetUpcomingDueIssues tests
810     my $barcode  = 'R00000342';
811     my $barcode2 = 'R00000343';
812     my $barcode3 = 'R00000344';
813     my $branch   = $library2->{branchcode};
814
815     #Create another record
816     my $biblio2 = MARC::Record->new();
817     my $title2 = 'Something is worng here';
818     $biblio2->append_fields(
819         MARC::Field->new('100', ' ', ' ', a => 'Anonymous'),
820         MARC::Field->new('245', ' ', ' ', a => $title2),
821     );
822     my ($biblionumber2, $biblioitemnumber2) = AddBiblio($biblio2, '');
823
824     #Create third item
825     AddItem(
826         {
827             homebranch       => $branch,
828             holdingbranch    => $branch,
829             barcode          => $barcode3,
830             itype            => $itemtype
831         },
832         $biblionumber2
833     );
834
835     # Create a borrower
836     my %a_borrower_data = (
837         firstname =>  'Fridolyn',
838         surname => 'SOMERS',
839         categorycode => $patron_category->{categorycode},
840         branchcode => $branch,
841     );
842
843     my $a_borrower_borrowernumber = AddMember(%a_borrower_data);
844     my $a_borrower = GetMember( borrowernumber => $a_borrower_borrowernumber );
845
846     my $yesterday = DateTime->today(time_zone => C4::Context->tz())->add( days => -1 );
847     my $two_days_ahead = DateTime->today(time_zone => C4::Context->tz())->add( days => 2 );
848     my $today = DateTime->today(time_zone => C4::Context->tz());
849
850     my $issue = AddIssue( $a_borrower, $barcode, $yesterday );
851     my $datedue = dt_from_string( $issue->date_due() );
852     my $issue2 = AddIssue( $a_borrower, $barcode2, $two_days_ahead );
853     my $datedue2 = dt_from_string( $issue->date_due() );
854
855     my $upcoming_dues;
856
857     # GetUpcomingDueIssues tests
858     for my $i(0..1) {
859         $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => $i } );
860         is ( scalar( @$upcoming_dues ), 0, "No items due in less than one day ($i days in advance)" );
861     }
862
863     #days_in_advance needs to be inclusive, so 1 matches items due tomorrow, 0 items due today etc.
864     $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 2 } );
865     is ( scalar ( @$upcoming_dues), 1, "Only one item due in 2 days or less" );
866
867     for my $i(3..5) {
868         $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => $i } );
869         is ( scalar( @$upcoming_dues ), 1,
870             "Bug 9362: Only one item due in more than 2 days ($i days in advance)" );
871     }
872
873     # Bug 11218 - Due notices not generated - GetUpcomingDueIssues needs to select due today items as well
874
875     my $issue3 = AddIssue( $a_borrower, $barcode3, $today );
876
877     $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => -1 } );
878     is ( scalar ( @$upcoming_dues), 0, "Overdues can not be selected" );
879
880     $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 0 } );
881     is ( scalar ( @$upcoming_dues), 1, "1 item is due today" );
882
883     $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 1 } );
884     is ( scalar ( @$upcoming_dues), 1, "1 item is due today, none tomorrow" );
885
886     $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 2 }  );
887     is ( scalar ( @$upcoming_dues), 2, "2 items are due withing 2 days" );
888
889     $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 3 } );
890     is ( scalar ( @$upcoming_dues), 2, "2 items are due withing 2 days" );
891
892     $upcoming_dues = C4::Circulation::GetUpcomingDueIssues();
893     is ( scalar ( @$upcoming_dues), 2, "days_in_advance is 7 in GetUpcomingDueIssues if not provided" );
894
895 }
896
897 {
898     my $barcode  = '1234567890';
899     my $branch   = $library2->{branchcode};
900
901     my $biblio = MARC::Record->new();
902     my ($biblionumber, $biblioitemnumber) = AddBiblio($biblio, '');
903
904     #Create third item
905     my ( undef, undef, $itemnumber ) = AddItem(
906         {
907             homebranch       => $branch,
908             holdingbranch    => $branch,
909             barcode          => $barcode,
910             itype            => $itemtype
911         },
912         $biblionumber
913     );
914
915     # Create a borrower
916     my %a_borrower_data = (
917         firstname =>  'Kyle',
918         surname => 'Hall',
919         categorycode => $patron_category->{categorycode},
920         branchcode => $branch,
921     );
922
923     my $borrowernumber = AddMember(%a_borrower_data);
924
925     my $issue = AddIssue( GetMember( borrowernumber => $borrowernumber ), $barcode );
926     UpdateFine(
927         {
928             issue_id       => $issue->id(),
929             itemnumber     => $itemnumber,
930             borrowernumber => $borrowernumber,
931             amount         => 0,
932             type           => q{}
933         }
934     );
935
936     my $hr = $dbh->selectrow_hashref(q{SELECT COUNT(*) AS count FROM accountlines WHERE borrowernumber = ? AND itemnumber = ?}, undef, $borrowernumber, $itemnumber );
937     my $count = $hr->{count};
938
939     is ( $count, 0, "Calling UpdateFine on non-existant fine with an amount of 0 does not result in an empty fine" );
940 }
941
942 {
943     $dbh->do('DELETE FROM issues');
944     $dbh->do('DELETE FROM items');
945     $dbh->do('DELETE FROM issuingrules');
946     $dbh->do(
947         q{
948         INSERT INTO issuingrules ( categorycode, branchcode, itemtype, reservesallowed, maxissueqty, issuelength, lengthunit, renewalsallowed, renewalperiod,
949                     norenewalbefore, auto_renew, fine, chargeperiod ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
950         },
951         {},
952         '*', '*', '*', 25,
953         20,  14,  'days',
954         1,   7,
955         undef,  0,
956         .10, 1
957     );
958     my $biblio = MARC::Record->new();
959     my ( $biblionumber, $biblioitemnumber ) = AddBiblio( $biblio, '' );
960
961     my $barcode1 = '1234';
962     my ( undef, undef, $itemnumber1 ) = AddItem(
963         {
964             homebranch    => $library2->{branchcode},
965             holdingbranch => $library2->{branchcode},
966             barcode       => $barcode1,
967             itype         => $itemtype
968         },
969         $biblionumber
970     );
971     my $barcode2 = '4321';
972     my ( undef, undef, $itemnumber2 ) = AddItem(
973         {
974             homebranch    => $library2->{branchcode},
975             holdingbranch => $library2->{branchcode},
976             barcode       => $barcode2,
977             itype         => $itemtype
978         },
979         $biblionumber
980     );
981
982     my $borrowernumber1 = AddMember(
983         firstname    => 'Kyle',
984         surname      => 'Hall',
985         categorycode => $patron_category->{categorycode},
986         branchcode   => $library2->{branchcode},
987     );
988     my $borrowernumber2 = AddMember(
989         firstname    => 'Chelsea',
990         surname      => 'Hall',
991         categorycode => $patron_category->{categorycode},
992         branchcode   => $library2->{branchcode},
993     );
994
995     my $borrower1 = GetMember( borrowernumber => $borrowernumber1 );
996     my $borrower2 = GetMember( borrowernumber => $borrowernumber2 );
997
998     my $issue = AddIssue( $borrower1, $barcode1 );
999
1000     my ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $itemnumber1 );
1001     is( $renewokay, 1, 'Bug 14337 - Verify the borrower can renew with no hold on the record' );
1002
1003     AddReserve(
1004         $library2->{branchcode}, $borrowernumber2, $biblionumber,
1005         '',  1, undef, undef, '',
1006         undef, undef, undef
1007     );
1008
1009     C4::Context->dbh->do("UPDATE issuingrules SET onshelfholds = 0");
1010     t::lib::Mocks::mock_preference( 'AllowRenewalIfOtherItemsAvailable', 0 );
1011     ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $itemnumber1 );
1012     is( $renewokay, 0, 'Bug 14337 - Verify the borrower cannot renew with a hold on the record if AllowRenewalIfOtherItemsAvailable and onshelfholds are disabled' );
1013
1014     C4::Context->dbh->do("UPDATE issuingrules SET onshelfholds = 0");
1015     t::lib::Mocks::mock_preference( 'AllowRenewalIfOtherItemsAvailable', 1 );
1016     ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $itemnumber1 );
1017     is( $renewokay, 0, 'Bug 14337 - Verify the borrower cannot renew with a hold on the record if AllowRenewalIfOtherItemsAvailable is enabled and onshelfholds is disabled' );
1018
1019     C4::Context->dbh->do("UPDATE issuingrules SET onshelfholds = 1");
1020     t::lib::Mocks::mock_preference( 'AllowRenewalIfOtherItemsAvailable', 0 );
1021     ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $itemnumber1 );
1022     is( $renewokay, 0, 'Bug 14337 - Verify the borrower cannot renew with a hold on the record if AllowRenewalIfOtherItemsAvailable is disabled and onshelfhold is enabled' );
1023
1024     C4::Context->dbh->do("UPDATE issuingrules SET onshelfholds = 1");
1025     t::lib::Mocks::mock_preference( 'AllowRenewalIfOtherItemsAvailable', 1 );
1026     ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $itemnumber1 );
1027     is( $renewokay, 1, 'Bug 14337 - Verify the borrower can renew with a hold on the record if AllowRenewalIfOtherItemsAvailable and onshelfhold are enabled' );
1028
1029     # Setting item not checked out to be not for loan but holdable
1030     ModItem({ notforloan => -1 }, $biblionumber, $itemnumber2);
1031
1032     ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $itemnumber1 );
1033     is( $renewokay, 0, 'Bug 14337 - Verify the borrower can not renew with a hold on the record if AllowRenewalIfOtherItemsAvailable is enabled but the only available item is notforloan' );
1034 }
1035
1036 {
1037     # Don't allow renewing onsite checkout
1038     my $barcode  = 'R00000XXX';
1039     my $branch   = $library->{branchcode};
1040
1041     #Create another record
1042     my $biblio = MARC::Record->new();
1043     $biblio->append_fields(
1044         MARC::Field->new('100', ' ', ' ', a => 'Anonymous'),
1045         MARC::Field->new('245', ' ', ' ', a => 'A title'),
1046     );
1047     my ($biblionumber, $biblioitemnumber) = AddBiblio($biblio, '');
1048
1049     my (undef, undef, $itemnumber) = AddItem(
1050         {
1051             homebranch       => $branch,
1052             holdingbranch    => $branch,
1053             barcode          => $barcode,
1054             itype            => $itemtype
1055         },
1056         $biblionumber
1057     );
1058
1059     my $borrowernumber = AddMember(
1060         firstname =>  'fn',
1061         surname => 'dn',
1062         categorycode => $patron_category->{categorycode},
1063         branchcode => $branch,
1064     );
1065
1066     my $borrower = GetMember( borrowernumber => $borrowernumber );
1067     my $issue = AddIssue( $borrower, $barcode, undef, undef, undef, undef, { onsite_checkout => 1 } );
1068     my ( $renewed, $error ) = CanBookBeRenewed( $borrowernumber, $itemnumber );
1069     is( $renewed, 0, 'CanBookBeRenewed should not allow to renew on-site checkout' );
1070     is( $error, 'onsite_checkout', 'A correct error code should be returned by CanBookBeRenewed for on-site checkout' );
1071 }
1072
1073 {
1074     my $library = $builder->build({ source => 'Branch' });
1075
1076     my $biblio = MARC::Record->new();
1077     my ($biblionumber, $biblioitemnumber) = AddBiblio($biblio, '');
1078
1079     my $barcode = 'just a barcode';
1080     my ( undef, undef, $itemnumber ) = AddItem(
1081         {
1082             homebranch       => $library->{branchcode},
1083             holdingbranch    => $library->{branchcode},
1084             barcode          => $barcode,
1085             itype            => $itemtype
1086         },
1087         $biblionumber,
1088     );
1089
1090     my $patron = $builder->build({ source => 'Borrower', value => { branchcode => $library->{branchcode} } } );
1091
1092     my $issue = AddIssue( GetMember( borrowernumber => $patron->{borrowernumber} ), $barcode );
1093     UpdateFine(
1094         {
1095             issue_id       => $issue->id(),
1096             itemnumber     => $itemnumber,
1097             borrowernumber => $patron->{borrowernumber},
1098             amount         => 1,
1099             type           => q{}
1100         }
1101     );
1102     UpdateFine(
1103         {
1104             issue_id       => $issue->id(),
1105             itemnumber     => $itemnumber,
1106             borrowernumber => $patron->{borrowernumber},
1107             amount         => 2,
1108             type           => q{}
1109         }
1110     );
1111     is( Koha::Account::Lines->search({ issue_id => $issue->id })->count, 1, 'UpdateFine should not create a new accountline when updating an existing fine');
1112 }
1113
1114 subtest 'CanBookBeIssued & AllowReturnToBranch' => sub {
1115     plan tests => 26;
1116
1117     my $homebranch    = $builder->build( { source => 'Branch' } );
1118     my $holdingbranch = $builder->build( { source => 'Branch' } );
1119     my $otherbranch   = $builder->build( { source => 'Branch' } );
1120     my $patron_1      = $builder->build( { source => 'Borrower' } );
1121     my $patron_2      = $builder->build( { source => 'Borrower' } );
1122
1123     my $biblioitem = $builder->build( { source => 'Biblioitem' } );
1124     my $item = $builder->build(
1125         {   source => 'Item',
1126             value  => {
1127                 homebranch    => $homebranch->{branchcode},
1128                 holdingbranch => $holdingbranch->{branchcode},
1129                 notforloan    => 0,
1130                 itemlost      => 0,
1131                 withdrawn     => 0,
1132                 restricted    => 0,
1133                 biblionumber  => $biblioitem->{biblionumber}
1134             }
1135         }
1136     );
1137
1138     set_userenv($holdingbranch);
1139
1140     my $issue = AddIssue( $patron_1, $item->{barcode} );
1141     is( ref($issue), 'Koha::Schema::Result::Issue' );    # FIXME Should be Koha::Checkout
1142
1143     my ( $error, $question, $alerts );
1144
1145     # AllowReturnToBranch == anywhere
1146     t::lib::Mocks::mock_preference( 'AllowReturnToBranch', 'anywhere' );
1147     ## Can be issued from homebranch
1148     set_userenv($homebranch);
1149     ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
1150     is( keys(%$error), 0, 'There should not be any errors (impossible)' );
1151     is( keys(%$alerts), 0, 'There should not be any alerts' );
1152     is( exists $question->{ISSUED_TO_ANOTHER}, 1 );
1153     ## Can be issued from holdingbranch
1154     set_userenv($holdingbranch);
1155     ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
1156     is( keys(%$error), 0, 'There should not be any errors (impossible)' );
1157     is( keys(%$alerts), 0, 'There should not be any alerts' );
1158     is( exists $question->{ISSUED_TO_ANOTHER}, 1 );
1159     ## Can be issued from another branch
1160     ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
1161     is( keys(%$error), 0, 'There should not be any errors (impossible)' );
1162     is( keys(%$alerts), 0, 'There should not be any alerts' );
1163     is( exists $question->{ISSUED_TO_ANOTHER}, 1 );
1164
1165     # AllowReturnToBranch == holdingbranch
1166     t::lib::Mocks::mock_preference( 'AllowReturnToBranch', 'holdingbranch' );
1167     ## Cannot be issued from homebranch
1168     set_userenv($homebranch);
1169     ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
1170     is( keys(%$question) + keys(%$alerts),  0 );
1171     is( exists $error->{RETURN_IMPOSSIBLE}, 1 );
1172     is( $error->{branch_to_return},         $holdingbranch->{branchcode} );
1173     ## Can be issued from holdinbranch
1174     set_userenv($holdingbranch);
1175     ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
1176     is( keys(%$error) + keys(%$alerts),        0 );
1177     is( exists $question->{ISSUED_TO_ANOTHER}, 1 );
1178     ## Cannot be issued from another branch
1179     set_userenv($otherbranch);
1180     ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
1181     is( keys(%$question) + keys(%$alerts),  0 );
1182     is( exists $error->{RETURN_IMPOSSIBLE}, 1 );
1183     is( $error->{branch_to_return},         $holdingbranch->{branchcode} );
1184
1185     # AllowReturnToBranch == homebranch
1186     t::lib::Mocks::mock_preference( 'AllowReturnToBranch', 'homebranch' );
1187     ## Can be issued from holdinbranch
1188     set_userenv($homebranch);
1189     ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
1190     is( keys(%$error) + keys(%$alerts),        0 );
1191     is( exists $question->{ISSUED_TO_ANOTHER}, 1 );
1192     ## Cannot be issued from holdinbranch
1193     set_userenv($holdingbranch);
1194     ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
1195     is( keys(%$question) + keys(%$alerts),  0 );
1196     is( exists $error->{RETURN_IMPOSSIBLE}, 1 );
1197     is( $error->{branch_to_return},         $homebranch->{branchcode} );
1198     ## Cannot be issued from holdinbranch
1199     set_userenv($otherbranch);
1200     ( $error, $question, $alerts ) = CanBookBeIssued( $patron_2, $item->{barcode} );
1201     is( keys(%$question) + keys(%$alerts),  0 );
1202     is( exists $error->{RETURN_IMPOSSIBLE}, 1 );
1203     is( $error->{branch_to_return},         $homebranch->{branchcode} );
1204
1205     # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1206 };
1207
1208 subtest 'AddIssue & AllowReturnToBranch' => sub {
1209     plan tests => 9;
1210
1211     my $homebranch    = $builder->build( { source => 'Branch' } );
1212     my $holdingbranch = $builder->build( { source => 'Branch' } );
1213     my $otherbranch   = $builder->build( { source => 'Branch' } );
1214     my $patron_1      = $builder->build( { source => 'Borrower' } );
1215     my $patron_2      = $builder->build( { source => 'Borrower' } );
1216
1217     my $biblioitem = $builder->build( { source => 'Biblioitem' } );
1218     my $item = $builder->build(
1219         {   source => 'Item',
1220             value  => {
1221                 homebranch    => $homebranch->{branchcode},
1222                 holdingbranch => $holdingbranch->{branchcode},
1223                 notforloan    => 0,
1224                 itemlost      => 0,
1225                 withdrawn     => 0,
1226                 biblionumber  => $biblioitem->{biblionumber}
1227             }
1228         }
1229     );
1230
1231     set_userenv($holdingbranch);
1232
1233     my $ref_issue = 'Koha::Schema::Result::Issue'; # FIXME Should be Koha::Checkout
1234     my $issue = AddIssue( $patron_1, $item->{barcode} );
1235
1236     my ( $error, $question, $alerts );
1237
1238     # AllowReturnToBranch == homebranch
1239     t::lib::Mocks::mock_preference( 'AllowReturnToBranch', 'anywhere' );
1240     ## Can be issued from homebranch
1241     set_userenv($homebranch);
1242     is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), $ref_issue );
1243     set_userenv($holdingbranch); AddIssue( $patron_1, $item->{barcode} ); # Reinsert the original issue
1244     ## Can be issued from holdinbranch
1245     set_userenv($holdingbranch);
1246     is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), $ref_issue );
1247     set_userenv($holdingbranch); AddIssue( $patron_1, $item->{barcode} ); # Reinsert the original issue
1248     ## Can be issued from another branch
1249     set_userenv($otherbranch);
1250     is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), $ref_issue );
1251     set_userenv($holdingbranch); AddIssue( $patron_1, $item->{barcode} ); # Reinsert the original issue
1252
1253     # AllowReturnToBranch == holdinbranch
1254     t::lib::Mocks::mock_preference( 'AllowReturnToBranch', 'holdingbranch' );
1255     ## Cannot be issued from homebranch
1256     set_userenv($homebranch);
1257     is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), '' );
1258     ## Can be issued from holdingbranch
1259     set_userenv($holdingbranch);
1260     is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), $ref_issue );
1261     set_userenv($holdingbranch); AddIssue( $patron_1, $item->{barcode} ); # Reinsert the original issue
1262     ## Cannot be issued from another branch
1263     set_userenv($otherbranch);
1264     is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), '' );
1265
1266     # AllowReturnToBranch == homebranch
1267     t::lib::Mocks::mock_preference( 'AllowReturnToBranch', 'homebranch' );
1268     ## Can be issued from homebranch
1269     set_userenv($homebranch);
1270     is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), $ref_issue );
1271     set_userenv($holdingbranch); AddIssue( $patron_1, $item->{barcode} ); # Reinsert the original issue
1272     ## Cannot be issued from holdinbranch
1273     set_userenv($holdingbranch);
1274     is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), '' );
1275     ## Cannot be issued from another branch
1276     set_userenv($otherbranch);
1277     is ( ref( AddIssue( $patron_2, $item->{barcode} ) ), '' );
1278     # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1279 };
1280
1281 subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1282     plan tests => 8;
1283
1284     my $library = $builder->build( { source => 'Branch' } );
1285     my $patron  = $builder->build( { source => 'Borrower' } );
1286
1287     my $biblioitem_1 = $builder->build( { source => 'Biblioitem' } );
1288     my $item_1 = $builder->build(
1289         {   source => 'Item',
1290             value  => {
1291                 homebranch    => $library->{branchcode},
1292                 holdingbranch => $library->{branchcode},
1293                 notforloan    => 0,
1294                 itemlost      => 0,
1295                 withdrawn     => 0,
1296                 biblionumber  => $biblioitem_1->{biblionumber}
1297             }
1298         }
1299     );
1300     my $biblioitem_2 = $builder->build( { source => 'Biblioitem' } );
1301     my $item_2 = $builder->build(
1302         {   source => 'Item',
1303             value  => {
1304                 homebranch    => $library->{branchcode},
1305                 holdingbranch => $library->{branchcode},
1306                 notforloan    => 0,
1307                 itemlost      => 0,
1308                 withdrawn     => 0,
1309                 biblionumber  => $biblioitem_2->{biblionumber}
1310             }
1311         }
1312     );
1313
1314     my ( $error, $question, $alerts );
1315
1316     # Patron cannot issue item_1, they have overdues
1317     my $yesterday = DateTime->today( time_zone => C4::Context->tz() )->add( days => -1 );
1318     my $issue = AddIssue( $patron, $item_1->{barcode}, $yesterday );    # Add an overdue
1319
1320     t::lib::Mocks::mock_preference( 'OverduesBlockCirc', 'confirmation' );
1321     ( $error, $question, $alerts ) = CanBookBeIssued( $patron, $item_2->{barcode} );
1322     is( keys(%$error) + keys(%$alerts),  0 );
1323     is( $question->{USERBLOCKEDOVERDUE}, 1 );
1324
1325     t::lib::Mocks::mock_preference( 'OverduesBlockCirc', 'block' );
1326     ( $error, $question, $alerts ) = CanBookBeIssued( $patron, $item_2->{barcode} );
1327     is( keys(%$question) + keys(%$alerts), 0 );
1328     is( $error->{USERBLOCKEDOVERDUE},      1 );
1329
1330     # Patron cannot issue item_1, they are debarred
1331     my $tomorrow = DateTime->today( time_zone => C4::Context->tz() )->add( days => 1 );
1332     Koha::Patron::Debarments::AddDebarment( { borrowernumber => $patron->{borrowernumber}, expiration => $tomorrow } );
1333     ( $error, $question, $alerts ) = CanBookBeIssued( $patron, $item_2->{barcode} );
1334     is( keys(%$question) + keys(%$alerts), 0 );
1335     is( $error->{USERBLOCKEDWITHENDDATE}, output_pref( { dt => $tomorrow, dateformat => 'sql', dateonly => 1 } ) );
1336
1337     Koha::Patron::Debarments::AddDebarment( { borrowernumber => $patron->{borrowernumber} } );
1338     ( $error, $question, $alerts ) = CanBookBeIssued( $patron, $item_2->{barcode} );
1339     is( keys(%$question) + keys(%$alerts), 0 );
1340     is( $error->{USERBLOCKEDNOENDDATE},    '9999-12-31' );
1341 };
1342
1343 subtest 'MultipleReserves' => sub {
1344     plan tests => 3;
1345
1346     my $biblio = MARC::Record->new();
1347     my $title = 'Silence in the library';
1348     $biblio->append_fields(
1349         MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'),
1350         MARC::Field->new('245', ' ', ' ', a => $title),
1351     );
1352
1353     my ($biblionumber, $biblioitemnumber) = AddBiblio($biblio, '');
1354
1355     my $branch = $library2->{branchcode};
1356
1357     my $barcode1 = 'R00110001';
1358     my ( $item_bibnum1, $item_bibitemnum1, $itemnumber1 ) = AddItem(
1359         {
1360             homebranch       => $branch,
1361             holdingbranch    => $branch,
1362             barcode          => $barcode1,
1363             replacementprice => 12.00,
1364             itype            => $itemtype
1365         },
1366         $biblionumber
1367     );
1368
1369     my $barcode2 = 'R00110002';
1370     my ( $item_bibnum2, $item_bibitemnum2, $itemnumber2 ) = AddItem(
1371         {
1372             homebranch       => $branch,
1373             holdingbranch    => $branch,
1374             barcode          => $barcode2,
1375             replacementprice => 12.00,
1376             itype            => $itemtype
1377         },
1378         $biblionumber
1379     );
1380
1381     my $bibitems       = '';
1382     my $priority       = '1';
1383     my $resdate        = undef;
1384     my $expdate        = undef;
1385     my $notes          = '';
1386     my $checkitem      = undef;
1387     my $found          = undef;
1388
1389     my %renewing_borrower_data = (
1390         firstname =>  'John',
1391         surname => 'Renewal',
1392         categorycode => $patron_category->{categorycode},
1393         branchcode => $branch,
1394     );
1395     my $renewing_borrowernumber = AddMember(%renewing_borrower_data);
1396     my $renewing_borrower = GetMember( borrowernumber => $renewing_borrowernumber );
1397     my $issue = AddIssue( $renewing_borrower, $barcode1);
1398     my $datedue = dt_from_string( $issue->date_due() );
1399     is (defined $issue->date_due(), 1, "item 1 checked out");
1400     my $borrowing_borrowernumber = Koha::Checkouts->find({ itemnumber => $itemnumber1 })->borrowernumber;
1401
1402     my %reserving_borrower_data1 = (
1403         firstname =>  'Katrin',
1404         surname => 'Reservation',
1405         categorycode => $patron_category->{categorycode},
1406         branchcode => $branch,
1407     );
1408     my $reserving_borrowernumber1 = AddMember(%reserving_borrower_data1);
1409     AddReserve(
1410         $branch, $reserving_borrowernumber1, $biblionumber,
1411         $bibitems,  $priority, $resdate, $expdate, $notes,
1412         $title, $checkitem, $found
1413     );
1414
1415     my %reserving_borrower_data2 = (
1416         firstname =>  'Kirk',
1417         surname => 'Reservation',
1418         categorycode => $patron_category->{categorycode},
1419         branchcode => $branch,
1420     );
1421     my $reserving_borrowernumber2 = AddMember(%reserving_borrower_data2);
1422     AddReserve(
1423         $branch, $reserving_borrowernumber2, $biblionumber,
1424         $bibitems,  $priority, $resdate, $expdate, $notes,
1425         $title, $checkitem, $found
1426     );
1427
1428     {
1429         my ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber1, 1);
1430         is($renewokay, 0, 'Bug 17641 - should cover the case where 2 books are both reserved, so failing');
1431     }
1432
1433     my $barcode3 = 'R00110003';
1434     my ( $item_bibnum3, $item_bibitemnum3, $itemnumber3 ) = AddItem(
1435         {
1436             homebranch       => $branch,
1437             holdingbranch    => $branch,
1438             barcode          => $barcode3,
1439             replacementprice => 12.00,
1440             itype            => $itemtype
1441         },
1442         $biblionumber
1443     );
1444
1445     {
1446         my ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber1, 1);
1447         is($renewokay, 1, 'Bug 17641 - should cover the case where 2 books are reserved, but a third one is available');
1448     }
1449 };
1450
1451 subtest 'CanBookBeIssued + AllowMultipleIssuesOnABiblio' => sub {
1452     plan tests => 5;
1453
1454     my $library = $builder->build( { source => 'Branch' } );
1455     my $patron  = $builder->build( { source => 'Borrower' } );
1456
1457     my $biblioitem = $builder->build( { source => 'Biblioitem' } );
1458     my $biblionumber = $biblioitem->{biblionumber};
1459     my $item_1 = $builder->build(
1460         {   source => 'Item',
1461             value  => {
1462                 homebranch    => $library->{branchcode},
1463                 holdingbranch => $library->{branchcode},
1464                 notforloan    => 0,
1465                 itemlost      => 0,
1466                 withdrawn     => 0,
1467                 biblionumber  => $biblionumber,
1468             }
1469         }
1470     );
1471     my $item_2 = $builder->build(
1472         {   source => 'Item',
1473             value  => {
1474                 homebranch    => $library->{branchcode},
1475                 holdingbranch => $library->{branchcode},
1476                 notforloan    => 0,
1477                 itemlost      => 0,
1478                 withdrawn     => 0,
1479                 biblionumber  => $biblionumber,
1480             }
1481         }
1482     );
1483
1484     my ( $error, $question, $alerts );
1485     my $issue = AddIssue( $patron, $item_1->{barcode}, dt_from_string->add( days => 1 ) );
1486
1487     t::lib::Mocks::mock_preference('AllowMultipleIssuesOnABiblio', 0);
1488     ( $error, $question, $alerts ) = CanBookBeIssued( $patron, $item_2->{barcode} );
1489     is( keys(%$error) + keys(%$alerts),  0, 'No error or alert should be raised' );
1490     is( $question->{BIBLIO_ALREADY_ISSUED}, 1, 'BIBLIO_ALREADY_ISSUED question flag should be set if AllowMultipleIssuesOnABiblio=0 and issue already exists' );
1491
1492     t::lib::Mocks::mock_preference('AllowMultipleIssuesOnABiblio', 1);
1493     ( $error, $question, $alerts ) = CanBookBeIssued( $patron, $item_2->{barcode} );
1494     is( keys(%$error) + keys(%$question) + keys(%$alerts),  0, 'No BIBLIO_ALREADY_ISSUED flag should be set if AllowMultipleIssuesOnABiblio=1' );
1495
1496     # Add a subscription
1497     Koha::Subscription->new({ biblionumber => $biblionumber })->store;
1498
1499     t::lib::Mocks::mock_preference('AllowMultipleIssuesOnABiblio', 0);
1500     ( $error, $question, $alerts ) = CanBookBeIssued( $patron, $item_2->{barcode} );
1501     is( keys(%$error) + keys(%$question) + keys(%$alerts),  0, 'No BIBLIO_ALREADY_ISSUED flag should be set if it is a subscription' );
1502
1503     t::lib::Mocks::mock_preference('AllowMultipleIssuesOnABiblio', 1);
1504     ( $error, $question, $alerts ) = CanBookBeIssued( $patron, $item_2->{barcode} );
1505     is( keys(%$error) + keys(%$question) + keys(%$alerts),  0, 'No BIBLIO_ALREADY_ISSUED flag should be set if it is a subscription' );
1506 };
1507
1508 subtest 'AddReturn + CumulativeRestrictionPeriods' => sub {
1509     plan tests => 8;
1510
1511     my $library = $builder->build( { source => 'Branch' } );
1512     my $patron  = $builder->build( { source => 'Borrower' } );
1513
1514     # Add 2 items
1515     my $biblioitem_1 = $builder->build( { source => 'Biblioitem' } );
1516     my $item_1 = $builder->build(
1517         {
1518             source => 'Item',
1519             value  => {
1520                 homebranch    => $library->{branchcode},
1521                 holdingbranch => $library->{branchcode},
1522                 notforloan    => 0,
1523                 itemlost      => 0,
1524                 withdrawn     => 0,
1525                 biblionumber  => $biblioitem_1->{biblionumber}
1526             }
1527         }
1528     );
1529     my $biblioitem_2 = $builder->build( { source => 'Biblioitem' } );
1530     my $item_2 = $builder->build(
1531         {
1532             source => 'Item',
1533             value  => {
1534                 homebranch    => $library->{branchcode},
1535                 holdingbranch => $library->{branchcode},
1536                 notforloan    => 0,
1537                 itemlost      => 0,
1538                 withdrawn     => 0,
1539                 biblionumber  => $biblioitem_2->{biblionumber}
1540             }
1541         }
1542     );
1543
1544     # And the issuing rule
1545     Koha::IssuingRules->search->delete;
1546     my $rule = Koha::IssuingRule->new(
1547         {
1548             categorycode => '*',
1549             itemtype     => '*',
1550             branchcode   => '*',
1551             maxissueqty  => 99,
1552             issuelength  => 1,
1553             firstremind  => 1,        # 1 day of grace
1554             finedays     => 2,        # 2 days of fine per day of overdue
1555             lengthunit   => 'days',
1556         }
1557     );
1558     $rule->store();
1559
1560     # Patron cannot issue item_1, they have overdues
1561     my $five_days_ago = dt_from_string->subtract( days => 5 );
1562     my $ten_days_ago  = dt_from_string->subtract( days => 10 );
1563     AddIssue( $patron, $item_1->{barcode}, $five_days_ago );    # Add an overdue
1564     AddIssue( $patron, $item_2->{barcode}, $ten_days_ago )
1565       ;    # Add another overdue
1566
1567     t::lib::Mocks::mock_preference( 'CumulativeRestrictionPeriods', '0' );
1568     AddReturn( $item_1->{barcode}, $library->{branchcode},
1569         undef, undef, dt_from_string );
1570     my $debarments = Koha::Patron::Debarments::GetDebarments(
1571         { borrowernumber => $patron->{borrowernumber}, type => 'SUSPENSION' } );
1572     is( scalar(@$debarments), 1 );
1573
1574     # FIXME Is it right? I'd have expected 5 * 2 - 1 instead
1575     # Same for the others
1576     my $expected_expiration = output_pref(
1577         {
1578             dt         => dt_from_string->add( days => ( 5 - 1 ) * 2 ),
1579             dateformat => 'sql',
1580             dateonly   => 1
1581         }
1582     );
1583     is( $debarments->[0]->{expiration}, $expected_expiration );
1584
1585     AddReturn( $item_2->{barcode}, $library->{branchcode},
1586         undef, undef, dt_from_string );
1587     $debarments = Koha::Patron::Debarments::GetDebarments(
1588         { borrowernumber => $patron->{borrowernumber}, type => 'SUSPENSION' } );
1589     is( scalar(@$debarments), 1 );
1590     $expected_expiration = output_pref(
1591         {
1592             dt         => dt_from_string->add( days => ( 10 - 1 ) * 2 ),
1593             dateformat => 'sql',
1594             dateonly   => 1
1595         }
1596     );
1597     is( $debarments->[0]->{expiration}, $expected_expiration );
1598
1599     Koha::Patron::Debarments::DelUniqueDebarment(
1600         { borrowernumber => $patron->{borrowernumber}, type => 'SUSPENSION' } );
1601
1602     t::lib::Mocks::mock_preference( 'CumulativeRestrictionPeriods', '1' );
1603     AddIssue( $patron, $item_1->{barcode}, $five_days_ago );    # Add an overdue
1604     AddIssue( $patron, $item_2->{barcode}, $ten_days_ago )
1605       ;    # Add another overdue
1606     AddReturn( $item_1->{barcode}, $library->{branchcode},
1607         undef, undef, dt_from_string );
1608     $debarments = Koha::Patron::Debarments::GetDebarments(
1609         { borrowernumber => $patron->{borrowernumber}, type => 'SUSPENSION' } );
1610     is( scalar(@$debarments), 1 );
1611     $expected_expiration = output_pref(
1612         {
1613             dt         => dt_from_string->add( days => ( 5 - 1 ) * 2 ),
1614             dateformat => 'sql',
1615             dateonly   => 1
1616         }
1617     );
1618     is( $debarments->[0]->{expiration}, $expected_expiration );
1619
1620     AddReturn( $item_2->{barcode}, $library->{branchcode},
1621         undef, undef, dt_from_string );
1622     $debarments = Koha::Patron::Debarments::GetDebarments(
1623         { borrowernumber => $patron->{borrowernumber}, type => 'SUSPENSION' } );
1624     is( scalar(@$debarments), 1 );
1625     $expected_expiration = output_pref(
1626         {
1627             dt => dt_from_string->add( days => ( 5 - 1 ) * 2 + ( 10 - 1 ) * 2 ),
1628             dateformat => 'sql',
1629             dateonly   => 1
1630         }
1631     );
1632     is( $debarments->[0]->{expiration}, $expected_expiration );
1633 };
1634
1635 sub set_userenv {
1636     my ( $library ) = @_;
1637     C4::Context->set_userenv(0,0,0,'firstname','surname', $library->{branchcode}, $library->{branchname}, '', '', '');
1638 }
1639
1640 1;