Bug 34868: (QA follow-up) Rename new option, add comments
[koha.git] / t / db_dependent / SIP / Transaction.t
1 #!/usr/bin/perl
2
3 # Tests for SIP::ILS::Transaction
4 # Current state is very rudimentary. Please help to extend it!
5
6 use Modern::Perl;
7 use Test::More tests => 18;
8 use Test::Warn;
9
10 use Koha::Database;
11 use t::lib::TestBuilder;
12 use t::lib::Mocks;
13 use C4::SIP::ILS;
14 use C4::SIP::ILS::Patron;
15 use C4::SIP::ILS::Transaction::RenewAll;
16 use C4::SIP::ILS::Transaction::Checkout;
17 use C4::SIP::ILS::Transaction::FeePayment;
18 use C4::SIP::ILS::Transaction::Hold;
19 use C4::SIP::ILS::Transaction::Checkout;
20 use C4::SIP::ILS::Transaction::Checkin;
21
22 use C4::Reserves qw( AddReserve ModReserve ModReserveAffect RevertWaitingStatus );
23 use Koha::CirculationRules;
24 use Koha::Item::Transfer;
25 use Koha::DateUtils qw( dt_from_string output_pref );
26 use Koha::Recalls;
27
28 my $schema = Koha::Database->new->schema;
29 $schema->storage->txn_begin;
30
31 my $builder    = t::lib::TestBuilder->new();
32 my $borr1      = $builder->build( { source => 'Borrower' } );
33 my $card       = $borr1->{cardnumber};
34 my $sip_patron = C4::SIP::ILS::Patron->new($card);
35
36 # Create transaction RenewAll, assign patron, and run (no items)
37 my $transaction = C4::SIP::ILS::Transaction::RenewAll->new();
38 is( ref $transaction,                  "C4::SIP::ILS::Transaction::RenewAll", "New transaction created" );
39 is( $transaction->patron($sip_patron), $sip_patron,                           "Patron assigned to transaction" );
40 isnt( $transaction->do_renew_all, undef, "RenewAll on zero items" );
41
42 subtest fill_holds_at_checkout => sub {
43     plan tests => 6;
44
45     my $category = $builder->build( { source => 'Category', value => { category_type => 'A' } } );
46     my $branch   = $builder->build( { source => 'Branch' } );
47     my $borrower = $builder->build(
48         {
49             source => 'Borrower',
50             value  => {
51                 branchcode   => $branch->{branchcode},
52                 categorycode => $category->{categorycode}
53             }
54         }
55     );
56     t::lib::Mocks::mock_userenv( { branchcode => $branch->{branchcode}, flags => 1 } );
57
58     my $itype = $builder->build( { source => 'Itemtype', value => { notforloan => 0 } } );
59     my $item1 = $builder->build_sample_item(
60         {
61             barcode       => 'barcode4test',
62             homebranch    => $branch->{branchcode},
63             holdingbranch => $branch->{branchcode},
64             itype         => $itype->{itemtype},
65             notforloan    => 0,
66         }
67     )->unblessed;
68     my $item2 = $builder->build_sample_item(
69         {
70             homebranch    => $branch->{branchcode},
71             holdingbranch => $branch->{branchcode},
72             biblionumber  => $item1->{biblionumber},
73             itype         => $itype->{itemtype},
74             notforloan    => 0,
75         }
76     )->unblessed;
77
78     Koha::CirculationRules->set_rules(
79         {
80             categorycode => $borrower->{categorycode},
81             branchcode   => $branch->{branchcode},
82             itemtype     => $itype->{itemtype},
83             rules        => {
84                 onshelfholds     => 1,
85                 reservesallowed  => 3,
86                 holds_per_record => 3,
87                 issuelength      => 5,
88                 lengthunit       => 'days',
89             }
90         }
91     );
92
93     my $reserve1 = AddReserve(
94         {
95             branchcode     => $branch->{branchcode},
96             borrowernumber => $borrower->{borrowernumber},
97             biblionumber   => $item1->{biblionumber}
98         }
99     );
100     my $reserve2 = AddReserve(
101         {
102             branchcode     => $branch->{branchcode},
103             borrowernumber => $borrower->{borrowernumber},
104             biblionumber   => $item1->{biblionumber}
105         }
106     );
107
108     my $bib = Koha::Biblios->find( $item1->{biblionumber} );
109     is( $bib->holds->count(), 2, "Bib has 2 holds" );
110
111     my $sip_patron  = C4::SIP::ILS::Patron->new( $borrower->{cardnumber} );
112     my $sip_item    = C4::SIP::ILS::Item->new( $item1->{barcode} );
113     my $transaction = C4::SIP::ILS::Transaction::Checkout->new();
114     is( ref $transaction,                  "C4::SIP::ILS::Transaction::Checkout", "New transaction created" );
115     is( $transaction->patron($sip_patron), $sip_patron,                           "Patron assigned to transaction" );
116     is( $transaction->item($sip_item),     $sip_item,                             "Item assigned to transaction" );
117     my $checkout = $transaction->do_checkout();
118     use Data::Dumper;    # Temporary debug statement
119     is( $bib->holds->count(), 1, "Bib has 1 holds remaining" ) or diag Dumper $checkout;
120
121     t::lib::Mocks::mock_preference( 'itemBarcodeInputFilter', 'whitespace' );
122     $sip_item    = C4::SIP::ILS::Item->new(' barcode 4 test ');
123     $transaction = C4::SIP::ILS::Transaction::Checkout->new();
124     is( $sip_item->{barcode}, $item1->{barcode}, "Item assigned to transaction" );
125 };
126
127 subtest "FeePayment->pay tests" => sub {
128
129     plan tests => 5;
130
131     # Create a borrower and add some outstanding debts to their account
132     my $patron = $builder->build( { source => 'Borrower' } );
133     my $account =
134         Koha::Account->new( { patron_id => $patron->{borrowernumber} } );
135     my $debt1 = $account->add_debit( { type => 'ACCOUNT', amount => 100, interface => 'commandline' } );
136     my $debt2 = $account->add_debit( { type => 'ACCOUNT', amount => 200, interface => 'commandline' } );
137
138     # Instantiate a new FeePayment transaction object
139     my $trans = C4::SIP::ILS::Transaction::FeePayment->new();
140     is(
141         ref $trans,
142         "C4::SIP::ILS::Transaction::FeePayment",
143         "New fee transaction created"
144     );
145
146     # Test the 'pay' method
147     # FIXME: pay should not require a borrowernumber
148     # (we should reach out to the transaction which should contain a patron object)
149     my $pay_type = '00';          # 00 - Cash, 01 - VISA, 02 - Creditcard
150     my $ok       = $trans->pay(
151         $patron->{borrowernumber}, 100, $pay_type, $debt1->id, 0,
152         0
153     );
154     ok( $ok, "FeePayment transaction succeeded" );
155     $debt1->discard_changes;
156     is(
157         $debt1->amountoutstanding + 0, 0,
158         "Debt1 was reduced to 0 as expected"
159     );
160     my $offsets = Koha::Account::Offsets->search( { debit_id => $debt1->id, credit_id => { '!=' => undef } } );
161     is( $offsets->count, 1, "FeePayment produced an offset line correctly" );
162     my $credit = $offsets->next->credit;
163     is( $credit->payment_type, 'SIP00', "Payment type was set correctly" );
164 };
165
166 subtest cancel_hold => sub {
167     plan tests => 7;
168
169     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
170     my $patron  = $builder->build_object(
171         {
172             class => 'Koha::Patrons',
173             value => {
174                 branchcode => $library->branchcode,
175             }
176         }
177     );
178     t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
179
180     my $item = $builder->build_sample_item(
181         {
182             library => $library->branchcode,
183         }
184     );
185
186     Koha::CirculationRules->set_rules(
187         {
188             categorycode => $patron->categorycode,
189             branchcode   => $library->branchcode,
190             itemtype     => $item->effective_itemtype,
191             rules        => {
192                 onshelfholds     => 1,
193                 reservesallowed  => 3,
194                 holds_per_record => 3,
195                 issuelength      => 5,
196                 lengthunit       => 'days',
197             }
198         }
199     );
200
201     my $reserve1 = AddReserve(
202         {
203             branchcode     => $library->branchcode,
204             borrowernumber => $patron->borrowernumber,
205             biblionumber   => $item->biblio->biblionumber,
206             itemnumber     => $item->itemnumber,
207         }
208     );
209     is( $item->biblio->holds->count(), 1, "Hold was placed on bib" );
210     is( $item->holds->count(),         1, "Hold was placed on specific item" );
211
212     my $sip_patron  = C4::SIP::ILS::Patron->new( $patron->cardnumber );
213     my $sip_item    = C4::SIP::ILS::Item->new( $item->barcode );
214     my $transaction = C4::SIP::ILS::Transaction::Hold->new();
215     is( ref $transaction,                  "C4::SIP::ILS::Transaction::Hold", "New transaction created" );
216     is( $transaction->patron($sip_patron), $sip_patron,                       "Patron assigned to transaction" );
217     is( $transaction->item($sip_item),     $sip_item,                         "Item assigned to transaction" );
218     my $hold = $transaction->drop_hold();
219     is( $item->biblio->holds->count(), 0, "Bib has 0 holds remaining" );
220     is( $item->holds->count(),         0, "Item has 0 holds remaining" );
221 };
222
223 subtest do_hold => sub {
224     plan tests => 8;
225
226     my $library = $builder->build_object(
227         {
228             class => 'Koha::Libraries',
229             value => { pickup_location => 1 }
230         }
231     );
232     my $patron_1 = $builder->build_object(
233         {
234             class => 'Koha::Patrons',
235             value => {
236                 branchcode => $library->branchcode,
237             }
238         }
239     );
240     my $patron_2 = $builder->build_object(
241         {
242             class => 'Koha::Patrons',
243             value => {
244                 branchcode   => $library->branchcode,
245                 categorycode => $patron_1->categorycode,
246             }
247         }
248     );
249
250     t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
251
252     my $item = $builder->build_sample_item(
253         {
254             library => $library->branchcode,
255         }
256     );
257
258     my $reserve1 = AddReserve(
259         {
260             branchcode     => $library->branchcode,
261             borrowernumber => $patron_1->borrowernumber,
262             biblionumber   => $item->biblio->biblionumber,
263             itemnumber     => $item->itemnumber,
264         }
265     );
266     is( $item->biblio->holds->count(), 1, "Hold was placed on bib" );
267     is( $item->holds->count(),         1, "Hold was placed on specific item" );
268
269     my $sip_patron  = C4::SIP::ILS::Patron->new( $patron_2->cardnumber );
270     my $sip_item    = C4::SIP::ILS::Item->new( $item->barcode );
271     my $transaction = C4::SIP::ILS::Transaction::Hold->new();
272     is(
273         ref $transaction,
274         "C4::SIP::ILS::Transaction::Hold",
275         "New transaction created"
276     );
277     is(
278         $transaction->patron($sip_patron),
279         $sip_patron, "Patron assigned to transaction"
280     );
281     is(
282         $transaction->item($sip_item),
283         $sip_item, "Item assigned to transaction"
284     );
285     my $hold = $transaction->do_hold();
286     is( $item->biblio->holds->count(), 2, "Bib has 2 holds" );
287
288     my $THE_hold = $patron_2->holds->next;
289     is( $THE_hold->priority,   2,                     'Hold placed from SIP should have a correct priority of 2' );
290     is( $THE_hold->branchcode, $patron_2->branchcode, 'Hold placed from SIP should have the branchcode set' );
291 };
292
293 subtest "Placing holds via SIP check CanItemBeReserved" => sub {
294     plan tests => 4;
295
296     my $library = $builder->build_object(
297         {
298             class => 'Koha::Libraries',
299             value => { pickup_location => 0 }
300         }
301     );
302     my $patron_1 = $builder->build_object(
303         {
304             class => 'Koha::Patrons',
305             value => {
306                 branchcode => $library->branchcode,
307             }
308         }
309     );
310     my $patron_2 = $builder->build_object(
311         {
312             class => 'Koha::Patrons',
313             value => {
314                 branchcode   => $library->branchcode,
315                 categorycode => $patron_1->categorycode,
316             }
317         }
318     );
319
320     t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
321
322     my $item = $builder->build_sample_item(
323         {
324             library => $library->branchcode,
325         }
326     );
327
328     my $sip_patron  = C4::SIP::ILS::Patron->new( $patron_2->cardnumber );
329     my $sip_item    = C4::SIP::ILS::Item->new( $item->barcode );
330     my $transaction = C4::SIP::ILS::Transaction::Hold->new();
331     is(
332         ref $transaction,
333         "C4::SIP::ILS::Transaction::Hold",
334         "New transaction created"
335     );
336     is(
337         $transaction->patron($sip_patron),
338         $sip_patron, "Patron assigned to transaction"
339     );
340     is(
341         $transaction->item($sip_item),
342         $sip_item, "Item assigned to transaction"
343     );
344     my $hold = $transaction->do_hold();
345
346     is( $transaction->ok, 0, "Hold was not allowed" );
347 };
348
349 subtest do_checkin => sub {
350     plan tests => 14;
351
352     my $mockILS  = Test::MockObject->new;
353     my $server   = { ils => $mockILS };
354     my $library  = $builder->build_object( { class => 'Koha::Libraries' } );
355     my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
356     my $patron   = $builder->build_object(
357         {
358             class => 'Koha::Patrons',
359             value => {
360                 branchcode => $library->branchcode,
361             }
362         }
363     );
364
365     t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
366
367     my $item = $builder->build_sample_item(
368         {
369             library => $library->branchcode,
370         }
371     );
372
373     # Checkout
374     my $sip_patron     = C4::SIP::ILS::Patron->new( $patron->cardnumber );
375     my $sip_item       = C4::SIP::ILS::Item->new( $item->barcode );
376     my $co_transaction = C4::SIP::ILS::Transaction::Checkout->new();
377     is(
378         $co_transaction->patron($sip_patron),
379         $sip_patron, "Patron assigned to transaction"
380     );
381     is(
382         $co_transaction->item($sip_item),
383         $sip_item, "Item assigned to transaction"
384     );
385     my $checkout = $co_transaction->do_checkout();
386     is( $patron->checkouts->count, 1, 'Checkout should have been done successfully' );
387
388     # Checkin
389     my $ci_transaction = C4::SIP::ILS::Transaction::Checkin->new();
390     is(
391         $ci_transaction->patron($sip_patron),
392         $sip_patron, "Patron assigned to transaction"
393     );
394     is(
395         $ci_transaction->item($sip_item),
396         $sip_item, "Item assigned to transaction"
397     );
398
399     my $checkin = $ci_transaction->do_checkin( $library->branchcode, C4::SIP::Sip::timestamp );
400     is( $patron->checkouts->count, 0, 'Checkin should have been done successfully' );
401
402     # Test checkin without return date
403     $co_transaction->do_checkout;
404     is( $patron->checkouts->count, 1, 'Checkout should have been done successfully' );
405     $ci_transaction->do_checkin( $library->branchcode, undef );
406     is( $patron->checkouts->count, 0, 'Checkin should have been done successfully' );
407
408     my $result = $ci_transaction->do_checkin( $library2->branchcode, undef );
409     is( $ci_transaction->alert_type, '04', "Checkin of item no issued at another branch succeeds" );
410     is_deeply(
411         $result,
412         {
413             messages => {
414                 'NotIssued'       => $item->barcode,
415                 'WasTransfered'   => $library->branchcode,
416                 'TransferTrigger' => 'ReturnToHome'
417             }
418         },
419         "Messages show not issued and transferred"
420     );
421     is( $ci_transaction->item->destination_loc, $library->branchcode, "Item destination correctly set" );
422
423     subtest 'Checkin an in transit item' => sub {
424
425         plan tests => 5;
426
427         my $library_1 = $builder->build_object( { class => 'Koha::Libraries' } );
428         my $library_2 = $builder->build_object( { class => 'Koha::Libraries' } );
429
430         my $patron =
431             $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $library_1->branchcode, } } );
432         my $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );
433         my $item       = $builder->build_sample_item( { library => $library_1->branchcode } );
434         my $sip_item   = C4::SIP::ILS::Item->new( $item->barcode );
435
436         t::lib::Mocks::mock_userenv( { branchcode => $library_1->branchcode, flags => 1 } );
437
438         my $reserve = AddReserve(
439             {
440                 branchcode     => $library_1->branchcode,
441                 borrowernumber => $patron->borrowernumber,
442                 biblionumber   => $item->biblionumber,
443             }
444         );
445
446         ModReserveAffect( $item->itemnumber, $patron->borrowernumber );    # Mark waiting
447
448         my $ci_transaction = C4::SIP::ILS::Transaction::Checkin->new();
449         is(
450             $ci_transaction->patron($sip_patron),
451             $sip_patron, "Patron assigned to transaction"
452         );
453         is(
454             $ci_transaction->item($sip_item),
455             $sip_item, "Item assigned to transaction"
456         );
457
458         my $checkin = $ci_transaction->do_checkin( $library_2->branchcode, C4::SIP::Sip::timestamp );
459
460         my $hold = Koha::Holds->find($reserve);
461         is( $hold->found,                                                          'T', );
462         is( $hold->itemnumber,                                                     $item->itemnumber, );
463         is( Koha::Checkouts->search( { itemnumber => $item->itemnumber } )->count, 0, );
464     };
465
466     subtest 'Checkin message' => sub {
467         plan tests => 3;
468         my $mockILS    = Test::MockObject->new;
469         my $server     = { ils => $mockILS };
470         my $library    = $builder->build_object( { class => 'Koha::Libraries' } );
471         my $patron     = $builder->build_object( { class => 'Koha::Patrons' } );
472         my $homebranch = $builder->build(
473             {
474                 source => 'Branch',
475                 value  => {
476                     branchcode => 'HMBR',
477                     branchname => 'Homebranch'
478                 }
479             }
480         );
481
482         my $institution = {
483             id             => $library->id,
484             implementation => "ILS",
485             policy         => {
486                 checkin  => "true",
487                 renewal  => "true",
488                 checkout => "true",
489                 timeout  => 100,
490                 retries  => 5,
491             }
492         };
493         my $ils  = C4::SIP::ILS->new($institution);
494         my $item = $builder->build_sample_item(
495             {
496                 library            => $library->branchcode,
497                 homebranch         => $homebranch->{'branchcode'},
498                 location           => 'My Location',
499                 permanent_location => 'My Permanent Location',
500             }
501         );
502         my $circ = $ils->checkout( $patron->cardnumber, $item->barcode, undef, undef, $server->{account} );
503         $circ = $ils->checkin(
504             $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode, undef, undef,
505             $server->{account}
506         );
507         is( $circ->{screen_msg}, '', "Checkin message is not displayed when show_checkin_message is disabled" );
508
509         $server->{account}->{show_checkin_message} = 1;
510
511         $circ = $ils->checkout( $patron->cardnumber, $item->barcode, undef, undef, $server->{account} );
512         $circ = $ils->checkin(
513             $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode, undef, undef,
514             $server->{account}
515         );
516         is(
517             $circ->{screen_msg}, 'Item checked-in: Homebranch - My Location.',
518             "Checkin message when show_checkin_message is enabled and UseLocationAsAQInSIP is disabled"
519         );
520
521         t::lib::Mocks::mock_preference( 'UseLocationAsAQInSIP', '1' );
522
523         $circ = $ils->checkout( $patron->cardnumber, $item->barcode, undef, undef, $server->{account} );
524         $circ = $ils->checkin(
525             $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode, undef, undef,
526             $server->{account}
527         );
528         is(
529             $circ->{screen_msg}, 'Item checked-in: My Permanent Location - My Location.',
530             "Checkin message when both show_checkin_message and UseLocationAsAQInSIP are enabled"
531         );
532
533     };
534
535     subtest 'Checkin with fines' => sub {
536         plan tests => 2;
537
538         my $mockILS     = Test::MockObject->new;
539         my $server      = { ils => $mockILS };
540         my $library     = $builder->build_object( { class => 'Koha::Libraries' } );
541         my $institution = {
542             id             => $library->id,
543             implementation => "ILS",
544             policy         => {
545                 checkin  => "true",
546                 renewal  => "true",
547                 checkout => "true",
548                 timeout  => 100,
549                 retries  => 5,
550             }
551         };
552         my $ils  = C4::SIP::ILS->new($institution);
553         my $item = $builder->build_sample_item(
554             {
555                 library => $library->branchcode,
556             }
557         );
558
559         # show_outstanding_amount disabled
560         my $patron = $builder->build_object(
561             {
562                 class => 'Koha::Patrons',
563                 value => {
564                     branchcode => $library->branchcode,
565                 }
566             }
567         );
568         my $circ = $ils->checkout( $patron->cardnumber, $item->barcode, undef, undef, $server->{account} );
569         my $fee1 = $builder->build(
570             {
571                 source => 'Accountline',
572                 value  => {
573                     borrowernumber    => $patron->borrowernumber,
574                     amountoutstanding => 12,
575                     debit_type_code   => 'OVERDUE',
576                     itemnumber        => $item->itemnumber
577                 }
578             }
579         );
580         $circ = $ils->checkin(
581             $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode, undef, undef,
582             $server->{account}
583         );
584         is( $circ->{screen_msg}, '', "The fine is not displayed on checkin when show_outstanding_amount is disabled" );
585
586         # show_outstanding_amount enabled
587         $patron = $builder->build_object(
588             {
589                 class => 'Koha::Patrons',
590                 value => {
591                     branchcode => $library->branchcode,
592                 }
593             }
594         );
595         $circ = $ils->checkout( $patron->cardnumber, $item->barcode, undef, undef, $server->{account} );
596
597         $fee1 = $builder->build(
598             {
599                 source => 'Accountline',
600                 value  => {
601                     borrowernumber    => $patron->borrowernumber,
602                     amountoutstanding => 12,
603                     debit_type_code   => 'OVERDUE',
604                     itemnumber        => $item->itemnumber
605                 }
606             }
607         );
608
609         $server->{account}->{show_outstanding_amount} = 1;
610         $circ = $ils->checkout( $patron->cardnumber, $item->barcode, undef, undef, $server->{account} );
611
612         $circ = $ils->checkin(
613             $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode, undef, undef,
614             $server->{account}
615         );
616         is(
617             $circ->{screen_msg}, 'You owe $12.00 for this item.',
618             "The fine is displayed on checkin when show_outstanding_amount is enabled"
619         );
620
621     };
622 };
623
624 subtest do_checkout_with_sysprefs_override => sub {
625     plan tests => 8;
626
627     my $mockILS     = Test::MockObject->new;
628     my $server      = { ils => $mockILS };
629     my $library     = $builder->build_object( { class => 'Koha::Libraries' } );
630     my $institution = {
631         id             => $library->id,
632         implementation => "ILS",
633         policy         => {
634             checkin  => "true",
635             renewal  => "true",
636             checkout => "true",
637             timeout  => 100,
638             retries  => 5,
639         }
640     };
641     my $ils  = C4::SIP::ILS->new($institution);
642     my $item = $builder->build_sample_item(
643         {
644             library => $library->branchcode,
645         }
646     );
647
648     my $patron_under_noissuescharge = $builder->build_object(
649         {
650             class => 'Koha::Patrons',
651             value => {
652                 branchcode => $library->branchcode,
653             }
654         }
655     );
656
657     my $fee_under_noissuescharge = $builder->build(
658         {
659             source => 'Accountline',
660             value  => {
661                 borrowernumber    => $patron_under_noissuescharge->borrowernumber,
662                 amountoutstanding => 4,
663                 debit_type_code   => 'OVERDUE',
664             }
665         }
666     );
667
668     my $patron_over_noissuescharge = $builder->build_object(
669
670         {
671             class => 'Koha::Patrons',
672             value => {
673                 branchcode => $library->branchcode,
674             }
675         }
676     );
677
678     my $fee_over_noissuescharge = $builder->build(
679         {
680             source => 'Accountline',
681             value  => {
682                 borrowernumber    => $patron_over_noissuescharge->borrowernumber,
683                 amountoutstanding => 6,
684                 debit_type_code   => 'OVERDUE',
685             }
686         }
687     );
688
689     $server->{account}->{override_fine_on_checkout} = 0;
690
691     t::lib::Mocks::mock_preference( 'AllFinesNeedOverride', '0' );
692     t::lib::Mocks::mock_preference( 'AllowFineOverride',    '0' );
693     my $circ =
694         $ils->checkout( $patron_under_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
695     is(
696         $patron_under_noissuescharge->checkouts->count, 1,
697         'Checkout is allowed when the amount is under noissuecharge, AllFinesNeedOverride and AllowFineOverride disabled'
698     );
699
700     $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode );
701
702     $circ = $ils->checkout( $patron_over_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
703     is(
704         $patron_over_noissuescharge->checkouts->count, 0,
705         'Checkout is blocked when the amount is over noissuecharge, AllFinesNeedOverride and AllowFineOverride disabled'
706     );
707
708     t::lib::Mocks::mock_preference( 'AllFinesNeedOverride', '0' );
709     t::lib::Mocks::mock_preference( 'AllowFineOverride',    '1' );
710
711     $circ =
712         $ils->checkout( $patron_under_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
713     is(
714         $patron_under_noissuescharge->checkouts->count, 1,
715         'Checkout is allowed when the amount is under noissuecharge, AllFinesNeedOverride disabled and AllowFineOverride enabled'
716     );
717
718     $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode );
719
720     $circ = $ils->checkout( $patron_over_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
721     is(
722         $patron_over_noissuescharge->checkouts->count, 0,
723         'Checkout is blocked when the amount is over noissuecharge, AllFinesNeedOverride disabled and AllowFineOverride enabled'
724     );
725
726     t::lib::Mocks::mock_preference( 'AllFinesNeedOverride', '1' );
727     t::lib::Mocks::mock_preference( 'AllowFineOverride',    '0' );
728
729     $circ =
730         $ils->checkout( $patron_under_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
731     is(
732         $patron_under_noissuescharge->checkouts->count, 0,
733         'Checkout is blocked when the amount is under noissuecharge, AllFinesNeedOverride enabled and AllowFineOverride disabled'
734     );
735
736     $circ = $ils->checkout( $patron_over_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
737     is(
738         $patron_over_noissuescharge->checkouts->count, 0,
739         'Checkout is blocked when the amount is over noissuecharge, AllFinesNeedOverride enabled and AllowFineOverride disabled'
740     );
741
742     t::lib::Mocks::mock_preference( 'AllFinesNeedOverride', '1' );
743     t::lib::Mocks::mock_preference( 'AllowFineOverride',    '1' );
744
745     $circ =
746         $ils->checkout( $patron_under_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
747     is(
748         $patron_under_noissuescharge->checkouts->count, 0,
749         'Checkout is blocked when the amount is under noissuecharge, AllFinesNeedOverride and AllowFineOverride enabled'
750     );
751
752     $circ = $ils->checkout( $patron_over_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
753     is(
754         $patron_over_noissuescharge->checkouts->count, 0,
755         'Checkout is blocked when the amount is over noissuecharge, AllFinesNeedOverride and AllowFineOverride enabled'
756     );
757 };
758
759 subtest do_checkout_with_patron_blocked => sub {
760     plan tests => 5;
761
762     my $mockILS     = Test::MockObject->new;
763     my $server      = { ils => $mockILS };
764     my $library     = $builder->build_object( { class => 'Koha::Libraries' } );
765     my $institution = {
766         id             => $library->id,
767         implementation => "ILS",
768         policy         => {
769             checkin  => "true",
770             renewal  => "true",
771             checkout => "true",
772             timeout  => 100,
773             retries  => 5,
774         }
775     };
776     my $ils  = C4::SIP::ILS->new($institution);
777     my $item = $builder->build_sample_item(
778         {
779             library => $library->branchcode,
780         }
781     );
782
783     my $expired_patron = $builder->build_object(
784         {
785             class => 'Koha::Patrons',
786             value => {
787                 branchcode => $library->branchcode,
788                 dateexpiry => '2020/01/03',
789             }
790         }
791     );
792     my $circ = $ils->checkout( $expired_patron->cardnumber, $item->barcode );
793     is( $circ->{screen_msg}, 'Patron expired on 01/03/2020', "Got correct expired screen message" );
794
795     my $fines_patron = $builder->build_object(
796         {
797             class => 'Koha::Patrons',
798             value => {
799                 branchcode => $library->branchcode,
800             }
801         }
802     );
803     my $fee1 = $builder->build(
804         {
805             source => 'Accountline',
806             value  => {
807                 borrowernumber    => $fines_patron->borrowernumber,
808                 amountoutstanding => 10,
809                 debit_type_code   => 'OVERDUE',
810             }
811         }
812     );
813
814     my $fines_sip_patron = C4::SIP::ILS::Patron->new( $fines_patron->cardnumber );
815
816     $circ = $ils->checkout( $fines_patron->cardnumber, $item->barcode, undef, undef, $server->{account} );
817     is( $circ->{screen_msg}, 'Patron has fines', "Got correct fines screen message" );
818
819     $server->{account}->{show_outstanding_amount} = 1;
820     $circ = $ils->checkout( $fines_patron->cardnumber, $item->barcode, undef, undef, $server->{account} );
821     is( $circ->{screen_msg}, 'Patron has fines - You owe $10.00.', "Got correct fines with amount screen message" );
822     my $debarred_patron = $builder->build_object(
823         {
824             class => 'Koha::Patrons',
825             value => {
826                 branchcode => $library->branchcode,
827                 debarred   => '9999/01/01',
828             }
829         }
830     );
831     my $debarred_sip_patron = C4::SIP::ILS::Patron->new( $debarred_patron->cardnumber );
832     $circ = $ils->checkout( $debarred_patron->cardnumber, $item->barcode );
833     is( $circ->{screen_msg}, 'Patron debarred', "Got correct debarred screen message" );
834
835     my $overdue_patron = $builder->build_object(
836         {
837             class => 'Koha::Patrons',
838             value => {
839                 branchcode => $library->branchcode,
840             }
841         }
842     );
843
844     my $odue = $builder->build(
845         {
846             source => 'Issue',
847             value  => {
848                 borrowernumber => $overdue_patron->borrowernumber,
849                 date_due       => '2017-01-01',
850             }
851         }
852     );
853     t::lib::Mocks::mock_preference( 'OverduesBlockCirc', 'block' );
854     my $overdue_sip_patron = C4::SIP::ILS::Patron->new( $overdue_patron->cardnumber );
855     $circ = $ils->checkout( $overdue_patron->cardnumber, $item->barcode );
856     is( $circ->{screen_msg}, 'Patron blocked', "Got correct blocked screen message" );
857
858 };
859
860 subtest do_checkout_with_noblock => sub {
861     plan tests => 3;
862
863     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
864     my $patron  = $builder->build_object(
865         {
866             class => 'Koha::Patrons',
867             value => {
868                 branchcode => $library->branchcode,
869                 debarred   => '9999/01/01',
870             },
871         }
872     );
873
874     t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
875
876     my $item = $builder->build_sample_item(
877         {
878             library => $library->branchcode,
879         }
880     );
881
882     my $sip_patron     = C4::SIP::ILS::Patron->new( $patron->cardnumber );
883     my $sip_item       = C4::SIP::ILS::Item->new( $item->barcode );
884     my $co_transaction = C4::SIP::ILS::Transaction::Checkout->new();
885     is(
886         $co_transaction->patron($sip_patron),
887         $sip_patron, "Patron assigned to transaction"
888     );
889     is(
890         $co_transaction->item($sip_item),
891         $sip_item, "Item assigned to transaction"
892     );
893
894     $co_transaction->do_checkout( undef, '19990102    030405' );
895
896     is( $patron->checkouts->count, 1, 'No Block checkout was performed for debarred patron' );
897 };
898
899 subtest do_checkout_with_holds => sub {
900     plan tests => 7;
901
902     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
903     my $patron  = $builder->build_object(
904         {
905             class => 'Koha::Patrons',
906             value => {
907                 branchcode => $library->branchcode,
908             }
909         }
910     );
911     my $patron2 = $builder->build_object(
912         {
913             class => 'Koha::Patrons',
914             value => {
915                 branchcode => $library->branchcode,
916             }
917         }
918     );
919
920     t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
921
922     my $item = $builder->build_sample_item(
923         {
924             library => $library->branchcode,
925         }
926     );
927
928     my $reserve = AddReserve(
929         {
930             branchcode     => $library->branchcode,
931             borrowernumber => $patron2->borrowernumber,
932             biblionumber   => $item->biblionumber,
933         }
934     );
935
936     my $sip_patron     = C4::SIP::ILS::Patron->new( $patron->cardnumber );
937     my $sip_item       = C4::SIP::ILS::Item->new( $item->barcode );
938     my $co_transaction = C4::SIP::ILS::Transaction::Checkout->new();
939     is(
940         $co_transaction->patron($sip_patron),
941         $sip_patron, "Patron assigned to transaction"
942     );
943     is(
944         $co_transaction->item($sip_item),
945         $sip_item, "Item assigned to transaction"
946     );
947
948     # Test attached holds
949     ModReserveAffect( $item->itemnumber, $patron->borrowernumber, 0, $reserve );    # Mark waiting (W)
950     my $hold = Koha::Holds->find($reserve);
951     $co_transaction->do_checkout();
952     is( $patron->checkouts->count, 0, 'Checkout was not done due to attached hold (W)' );
953
954     $hold->set_transfer;
955     $co_transaction->do_checkout();
956     is( $patron->checkouts->count, 0, 'Checkout was not done due to attached hold (T)' );
957
958     $hold->set_processing;
959     $co_transaction->do_checkout();
960     is( $patron->checkouts->count, 0, 'Checkout was not done due to attached hold (P)' );
961
962     # Test non-attached holds
963     C4::Reserves::RevertWaitingStatus( { itemnumber => $hold->itemnumber } );
964     t::lib::Mocks::mock_preference( 'AllowItemsOnHoldCheckoutSIP', '0' );
965     $co_transaction->do_checkout();
966     is( $patron->checkouts->count, 0, 'Checkout refused due to hold and AllowItemsOnHoldCheckoutSIP' );
967
968     t::lib::Mocks::mock_preference( 'AllowItemsOnHoldCheckoutSIP', '1' );
969     $co_transaction->do_checkout();
970     is( $patron->checkouts->count, 1, 'Checkout allowed due to hold and AllowItemsOnHoldCheckoutSIP' );
971 };
972
973 subtest checkin_lost => sub {
974     plan tests => 2;
975
976     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
977
978     t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
979
980     my $item = $builder->build_sample_item(
981         {
982             library => $library->branchcode,
983         }
984     );
985
986     $item->itemlost(1)->itemlost_on(dt_from_string)->store();
987
988     my $instituation = {
989         id             => $library->id,
990         implementation => "ILS",
991         policy         => {
992             checkin  => "true",
993             renewal  => "true",
994             checkout => "true",
995             timeout  => 100,
996             retries  => 5,
997         }
998     };
999     my $ils = C4::SIP::ILS->new($instituation);
1000
1001     t::lib::Mocks::mock_preference( 'BlockReturnOfLostItems', '1' );
1002     my $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode );
1003     is( $circ->{screen_msg}, 'Item lost, return not allowed', "Got correct screen message" );
1004
1005     t::lib::Mocks::mock_preference( 'BlockReturnOfLostItems', '0' );
1006     $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode );
1007     is( $circ->{screen_msg}, 'Item not checked out', "Got 'Item not checked out' screen message" );
1008 };
1009
1010 subtest checkin_withdrawn => sub {
1011     plan tests => 2;
1012
1013     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
1014
1015     t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
1016
1017     my $item = $builder->build_sample_item(
1018         {
1019             library => $library->branchcode,
1020         }
1021     );
1022
1023     $item->withdrawn(1)->withdrawn_on(dt_from_string)->store();
1024
1025     my $instituation = {
1026         id             => $library->id,
1027         implementation => "ILS",
1028         policy         => {
1029             checkin  => "true",
1030             renewal  => "true",
1031             checkout => "true",
1032             timeout  => 100,
1033             retries  => 5,
1034         }
1035     };
1036     my $ils = C4::SIP::ILS->new($instituation);
1037
1038     t::lib::Mocks::mock_preference( 'BlockReturnOfWithdrawnItems', '1' );
1039     my $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode );
1040     is( $circ->{screen_msg}, 'Item withdrawn, return not allowed', "Got correct screen message" );
1041
1042     t::lib::Mocks::mock_preference( 'BlockReturnOfWithdrawnItems', '0' );
1043     $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode );
1044     is( $circ->{screen_msg}, 'Item not checked out', "Got 'Item not checked out' screen message" );
1045 };
1046
1047 subtest _get_sort_bin => sub {
1048     plan tests => 6;
1049
1050     my $library  = $builder->build_object( { class => 'Koha::Libraries' } );
1051     my $branch   = $library->branchcode;
1052     my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
1053     my $branch2  = $library2->branchcode;
1054
1055     # Rules starts with malformed line, empty line and a comment to prove they are skipped
1056     my $rules = <<"RULES";
1057 $branch:homebranch:ne:\$holdingbranch:X\r
1058 $branch:effective_itemtype:eq:CD:0\r
1059 $branch:itemcallnumber:<:340:1\r
1060 $branch:itemcallnumber:<:370:2\r
1061 $branch:itemcallnumber:<:600:3\r
1062 $branch2:homebranch:ne:\$holdingbranch:X\r
1063 $branch2:effective_itemtype:eq:CD:4\r
1064 $branch2:itemcallnumber:>:600:5\r
1065 $branch2:effective_itemtype:eq:BOOK:ccode:eq:TEEN:6\r
1066 # Comment line to skip\r
1067 \r
1068 $branch:homebranch:Z\r
1069 RULES
1070
1071     t::lib::Mocks::mock_preference( 'SIP2SortBinMapping', $rules );
1072
1073     my $item_cd = $builder->build_sample_item(
1074         {
1075             library => $library->branchcode,
1076             itype   => 'CD'
1077         }
1078     );
1079
1080     my $item_book = $builder->build_sample_item(
1081         {
1082             library        => $library->branchcode,
1083             itype          => 'BOOK',
1084             itemcallnumber => '200.01'
1085         }
1086     );
1087
1088     my $item_book2 = $builder->build_sample_item(
1089         {
1090             library => $library2->branchcode,
1091             itype   => 'BOOK',
1092             ccode   => 'TEEN'
1093         }
1094     );
1095
1096     my $item_dvd = $builder->build_sample_item(
1097         {
1098             library => $library2->branchcode,
1099             itype  => 'DVD'
1100         }
1101     );
1102
1103     my $bin;
1104
1105     # Set holdingbranch as though item returned to library other than homebranch (As AddReturn would)
1106     $item_cd->holdingbranch( $library2->branchcode )->store();
1107     $bin = C4::SIP::ILS::Transaction::Checkin::_get_sort_bin( $item_cd, $library2->branchcode );
1108     is( $bin, 'X', "Item parameter on RHS of comparison works (ne comparator)" );
1109
1110     # Reset holdingbranch as though item returned to home library
1111     $item_cd->holdingbranch( $library->branchcode )->store();
1112     $bin = C4::SIP::ILS::Transaction::Checkin::_get_sort_bin( $item_cd, $library->branchcode );
1113     is( $bin, '0', "Fixed value on RHS of comparison works (eq comparator)" );
1114     $bin = C4::SIP::ILS::Transaction::Checkin::_get_sort_bin( $item_book, $library->branchcode );
1115     is( $bin, '1', "Rules applied in order (< comparator)" );
1116     $item_book->itemcallnumber('350.20')->store();
1117     $bin = C4::SIP::ILS::Transaction::Checkin::_get_sort_bin( $item_book, $library->branchcode );
1118     is( $bin, '2', "Rules applied in order (< comparator)" );
1119
1120     $bin = C4::SIP::ILS::Transaction::Checkin::_get_sort_bin( $item_book2, $library2->branchcode );
1121     is( $bin, '6', "Rules with multiple field matches" );
1122
1123     warnings_are { $bin = C4::SIP::ILS::Transaction::Checkin::_get_sort_bin( $item_dvd, $library2->branchcode ); }
1124     ["Malformed preference line found: '$branch:homebranch:Z'"],
1125         "Comments and blank lines are skipped, Malformed lines are warned and skipped";
1126 };
1127
1128 subtest item_circulation_status => sub {
1129     plan tests => 8;
1130
1131     my $library  = $builder->build_object( { class => 'Koha::Libraries' } );
1132     my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
1133
1134     my $patron = $builder->build_object(
1135         {
1136             class => 'Koha::Patrons',
1137             value => {
1138                 branchcode => $library->branchcode,
1139             }
1140         }
1141     );
1142
1143     t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
1144
1145     my $item = $builder->build_sample_item(
1146         {
1147             library => $library->branchcode,
1148         }
1149     );
1150
1151     my $sip_item = C4::SIP::ILS::Item->new( $item->barcode );
1152     my $status   = $sip_item->sip_circulation_status;
1153     is( $status, '03', "Item circulation status is available" );
1154
1155     my $transfer = Koha::Item::Transfer->new(
1156         {
1157             itemnumber => $item->id,
1158             datesent   => '2020-01-01',
1159             frombranch => $library->branchcode,
1160             tobranch   => $library2->branchcode,
1161         }
1162     )->store();
1163
1164     $sip_item = C4::SIP::ILS::Item->new( $item->barcode );
1165     $status   = $sip_item->sip_circulation_status;
1166     is( $status, '10', "Item circulation status is in transit" );
1167
1168     $transfer->delete;
1169
1170     my $claim = Koha::Checkouts::ReturnClaim->new(
1171         {
1172             itemnumber     => $item->id,
1173             borrowernumber => $patron->id,
1174             created_by     => $patron->id,
1175         }
1176     )->store();
1177
1178     $sip_item = C4::SIP::ILS::Item->new( $item->barcode );
1179     $status   = $sip_item->sip_circulation_status;
1180     is( $status, '11', "Item circulation status is claimed returned" );
1181
1182     $claim->delete;
1183
1184     $item->itemlost(1)->store();
1185     $sip_item = C4::SIP::ILS::Item->new( $item->barcode );
1186     $status   = $sip_item->sip_circulation_status;
1187     is( $status, '12', "Item circulation status is lost" );
1188     $status = $sip_item->sip_circulation_status( { account => { lost_status_for_missing => "1" } } );
1189     is( $status, '13', "Item circulation status is missing" );
1190     $item->itemlost(0)->store();
1191
1192     my $location = $item->location;
1193     $item->location("CART")->store();
1194     $sip_item = C4::SIP::ILS::Item->new( $item->barcode );
1195     $status   = $sip_item->sip_circulation_status;
1196     is( $status, '09', "Item circulation status is waiting to be re-shelved" );
1197     $item->location($location)->store();
1198
1199     my $nfl = $item->notforloan;
1200     $item->notforloan(-1)->store();
1201     $sip_item = C4::SIP::ILS::Item->new( $item->barcode );
1202     $status   = $sip_item->sip_circulation_status;
1203     is( $status, '02', "Item circulation status is on order" );
1204     $item->notforloan($nfl)->store();
1205
1206     my $damaged = $item->damaged;
1207     $item->damaged(1)->store();
1208     $sip_item = C4::SIP::ILS::Item->new( $item->barcode );
1209     $status   = $sip_item->sip_circulation_status;
1210     is( $status, '01', "Item circulation status is damaged" );
1211     $item->damaged(0)->store();
1212 };
1213
1214 subtest do_checkout_with_recalls => sub {
1215     plan tests => 7;
1216
1217     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
1218     my $patron  = $builder->build_object(
1219         {
1220             class => 'Koha::Patrons',
1221             value => {
1222                 branchcode => $library->branchcode,
1223             }
1224         }
1225     );
1226     my $patron2 = $builder->build_object(
1227         {
1228             class => 'Koha::Patrons',
1229             value => {
1230                 branchcode => $library->branchcode,
1231             }
1232         }
1233     );
1234
1235     t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
1236
1237     my $item = $builder->build_sample_item(
1238         {
1239             library => $library->branchcode,
1240         }
1241     );
1242
1243     t::lib::Mocks::mock_preference( 'UseRecalls', 1 );
1244     Koha::CirculationRules->set_rule(
1245         {
1246             branchcode   => undef,
1247             categorycode => undef,
1248             itemtype     => undef,
1249             rule_name    => 'recalls_allowed',
1250             rule_value   => '10',
1251         }
1252     );
1253
1254     my $recall1 = Koha::Recall->new(
1255         {
1256             patron_id         => $patron2->borrowernumber,
1257             created_date      => \'NOW()',
1258             biblio_id         => $item->biblio->biblionumber,
1259             pickup_library_id => $library->branchcode,
1260             item_id           => $item->itemnumber,
1261             expiration_date   => undef,
1262             item_level        => 1
1263         }
1264     )->store;
1265
1266     my $sip_patron     = C4::SIP::ILS::Patron->new( $patron->cardnumber );
1267     my $sip_item       = C4::SIP::ILS::Item->new( $item->barcode );
1268     my $co_transaction = C4::SIP::ILS::Transaction::Checkout->new();
1269     is(
1270         $co_transaction->patron($sip_patron),
1271         $sip_patron, "Patron assigned to transaction"
1272     );
1273     is(
1274         $co_transaction->item($sip_item),
1275         $sip_item, "Item assigned to transaction"
1276     );
1277
1278     # Test recalls made by another patron
1279
1280     $recall1->set_waiting( { item => $item } );
1281     $co_transaction->do_checkout();
1282     is( $patron->checkouts->count, 0, 'Checkout was not done due to waiting recall' );
1283
1284     $recall1->revert_waiting;
1285     $recall1->start_transfer( { item => $item } );
1286     $co_transaction->do_checkout();
1287     is( $patron->checkouts->count, 0, 'Checkout was not done due to recall in transit' );
1288
1289     $recall1->revert_transfer;
1290     $recall1->update( { item_id => undef, item_level => 0 } );
1291     $co_transaction->do_checkout();
1292     is( $patron->checkouts->count, 0, 'Checkout was not done due to a biblio-level recall that this item could fill' );
1293
1294     $recall1->set_cancelled;
1295
1296     my $recall2 = Koha::Recall->new(
1297         {
1298             patron_id         => $patron->borrowernumber,
1299             created_date      => \'NOW()',
1300             biblio_id         => $item->biblio->biblionumber,
1301             pickup_library_id => $library->branchcode,
1302             item_id           => $item->itemnumber,
1303             expiration_date   => undef,
1304             item_level        => 1
1305         }
1306     )->store;
1307
1308     # Test recalls made by SIP patron
1309
1310     $recall2->set_waiting( { item => $item } );
1311     $co_transaction->do_checkout();
1312     is( $patron->checkouts->count, 1, 'Checkout was done because recalled item was allocated to them' );
1313     $recall2 = Koha::Recalls->find( $recall2->id );
1314     is( $recall2->status, 'fulfilled', 'Recall is fulfilled by checked out item' );
1315 };
1316
1317 $schema->storage->txn_rollback;