3 # Tests for SIP::ILS::Transaction
4 # Current state is very rudimentary. Please help to extend it!
7 use Test::More tests => 18;
11 use t::lib::TestBuilder;
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;
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 );
28 my $schema = Koha::Database->new->schema;
29 $schema->storage->txn_begin;
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);
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" );
42 subtest fill_holds_at_checkout => sub {
45 my $category = $builder->build( { source => 'Category', value => { category_type => 'A' } } );
46 my $branch = $builder->build( { source => 'Branch' } );
47 my $borrower = $builder->build(
51 branchcode => $branch->{branchcode},
52 categorycode => $category->{categorycode}
56 t::lib::Mocks::mock_userenv( { branchcode => $branch->{branchcode}, flags => 1 } );
58 my $itype = $builder->build( { source => 'Itemtype', value => { notforloan => 0 } } );
59 my $item1 = $builder->build_sample_item(
61 barcode => 'barcode4test',
62 homebranch => $branch->{branchcode},
63 holdingbranch => $branch->{branchcode},
64 itype => $itype->{itemtype},
68 my $item2 = $builder->build_sample_item(
70 homebranch => $branch->{branchcode},
71 holdingbranch => $branch->{branchcode},
72 biblionumber => $item1->{biblionumber},
73 itype => $itype->{itemtype},
78 Koha::CirculationRules->set_rules(
80 categorycode => $borrower->{categorycode},
81 branchcode => $branch->{branchcode},
82 itemtype => $itype->{itemtype},
86 holds_per_record => 3,
93 my $reserve1 = AddReserve(
95 branchcode => $branch->{branchcode},
96 borrowernumber => $borrower->{borrowernumber},
97 biblionumber => $item1->{biblionumber}
100 my $reserve2 = AddReserve(
102 branchcode => $branch->{branchcode},
103 borrowernumber => $borrower->{borrowernumber},
104 biblionumber => $item1->{biblionumber}
108 my $bib = Koha::Biblios->find( $item1->{biblionumber} );
109 is( $bib->holds->count(), 2, "Bib has 2 holds" );
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;
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" );
127 subtest "FeePayment->pay tests" => sub {
131 # Create a borrower and add some outstanding debts to their account
132 my $patron = $builder->build( { source => 'Borrower' } );
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' } );
138 # Instantiate a new FeePayment transaction object
139 my $trans = C4::SIP::ILS::Transaction::FeePayment->new();
142 "C4::SIP::ILS::Transaction::FeePayment",
143 "New fee transaction created"
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,
154 ok( $ok, "FeePayment transaction succeeded" );
155 $debt1->discard_changes;
157 $debt1->amountoutstanding + 0, 0,
158 "Debt1 was reduced to 0 as expected"
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" );
166 subtest cancel_hold => sub {
169 my $library = $builder->build_object( { class => 'Koha::Libraries' } );
170 my $patron = $builder->build_object(
172 class => 'Koha::Patrons',
174 branchcode => $library->branchcode,
178 t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
180 my $item = $builder->build_sample_item(
182 library => $library->branchcode,
186 Koha::CirculationRules->set_rules(
188 categorycode => $patron->categorycode,
189 branchcode => $library->branchcode,
190 itemtype => $item->effective_itemtype,
193 reservesallowed => 3,
194 holds_per_record => 3,
196 lengthunit => 'days',
201 my $reserve1 = AddReserve(
203 branchcode => $library->branchcode,
204 borrowernumber => $patron->borrowernumber,
205 biblionumber => $item->biblio->biblionumber,
206 itemnumber => $item->itemnumber,
209 is( $item->biblio->holds->count(), 1, "Hold was placed on bib" );
210 is( $item->holds->count(), 1, "Hold was placed on specific item" );
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" );
223 subtest do_hold => sub {
226 my $library = $builder->build_object(
228 class => 'Koha::Libraries',
229 value => { pickup_location => 1 }
232 my $patron_1 = $builder->build_object(
234 class => 'Koha::Patrons',
236 branchcode => $library->branchcode,
240 my $patron_2 = $builder->build_object(
242 class => 'Koha::Patrons',
244 branchcode => $library->branchcode,
245 categorycode => $patron_1->categorycode,
250 t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
252 my $item = $builder->build_sample_item(
254 library => $library->branchcode,
258 my $reserve1 = AddReserve(
260 branchcode => $library->branchcode,
261 borrowernumber => $patron_1->borrowernumber,
262 biblionumber => $item->biblio->biblionumber,
263 itemnumber => $item->itemnumber,
266 is( $item->biblio->holds->count(), 1, "Hold was placed on bib" );
267 is( $item->holds->count(), 1, "Hold was placed on specific item" );
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();
274 "C4::SIP::ILS::Transaction::Hold",
275 "New transaction created"
278 $transaction->patron($sip_patron),
279 $sip_patron, "Patron assigned to transaction"
282 $transaction->item($sip_item),
283 $sip_item, "Item assigned to transaction"
285 my $hold = $transaction->do_hold();
286 is( $item->biblio->holds->count(), 2, "Bib has 2 holds" );
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' );
293 subtest "Placing holds via SIP check CanItemBeReserved" => sub {
296 my $library = $builder->build_object(
298 class => 'Koha::Libraries',
299 value => { pickup_location => 0 }
302 my $patron_1 = $builder->build_object(
304 class => 'Koha::Patrons',
306 branchcode => $library->branchcode,
310 my $patron_2 = $builder->build_object(
312 class => 'Koha::Patrons',
314 branchcode => $library->branchcode,
315 categorycode => $patron_1->categorycode,
320 t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
322 my $item = $builder->build_sample_item(
324 library => $library->branchcode,
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();
333 "C4::SIP::ILS::Transaction::Hold",
334 "New transaction created"
337 $transaction->patron($sip_patron),
338 $sip_patron, "Patron assigned to transaction"
341 $transaction->item($sip_item),
342 $sip_item, "Item assigned to transaction"
344 my $hold = $transaction->do_hold();
346 is( $transaction->ok, 0, "Hold was not allowed" );
349 subtest do_checkin => sub {
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(
358 class => 'Koha::Patrons',
360 branchcode => $library->branchcode,
365 t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
367 my $item = $builder->build_sample_item(
369 library => $library->branchcode,
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();
378 $co_transaction->patron($sip_patron),
379 $sip_patron, "Patron assigned to transaction"
382 $co_transaction->item($sip_item),
383 $sip_item, "Item assigned to transaction"
385 my $checkout = $co_transaction->do_checkout();
386 is( $patron->checkouts->count, 1, 'Checkout should have been done successfully' );
389 my $ci_transaction = C4::SIP::ILS::Transaction::Checkin->new();
391 $ci_transaction->patron($sip_patron),
392 $sip_patron, "Patron assigned to transaction"
395 $ci_transaction->item($sip_item),
396 $sip_item, "Item assigned to transaction"
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' );
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' );
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" );
414 'NotIssued' => $item->barcode,
415 'WasTransfered' => $library->branchcode,
416 'TransferTrigger' => 'ReturnToHome'
419 "Messages show not issued and transferred"
421 is( $ci_transaction->item->destination_loc, $library->branchcode, "Item destination correctly set" );
423 subtest 'Checkin an in transit item' => sub {
427 my $library_1 = $builder->build_object( { class => 'Koha::Libraries' } );
428 my $library_2 = $builder->build_object( { class => 'Koha::Libraries' } );
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 );
436 t::lib::Mocks::mock_userenv( { branchcode => $library_1->branchcode, flags => 1 } );
438 my $reserve = AddReserve(
440 branchcode => $library_1->branchcode,
441 borrowernumber => $patron->borrowernumber,
442 biblionumber => $item->biblionumber,
446 ModReserveAffect( $item->itemnumber, $patron->borrowernumber ); # Mark waiting
448 my $ci_transaction = C4::SIP::ILS::Transaction::Checkin->new();
450 $ci_transaction->patron($sip_patron),
451 $sip_patron, "Patron assigned to transaction"
454 $ci_transaction->item($sip_item),
455 $sip_item, "Item assigned to transaction"
458 my $checkin = $ci_transaction->do_checkin( $library_2->branchcode, C4::SIP::Sip::timestamp );
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, );
466 subtest 'Checkin message' => sub {
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(
476 branchcode => 'HMBR',
477 branchname => 'Homebranch'
484 implementation => "ILS",
493 my $ils = C4::SIP::ILS->new($institution);
494 my $item = $builder->build_sample_item(
496 library => $library->branchcode,
497 homebranch => $homebranch->{'branchcode'},
498 location => 'My Location',
499 permanent_location => 'My Permanent Location',
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,
507 is( $circ->{screen_msg}, '', "Checkin message is not displayed when show_checkin_message is disabled" );
509 $server->{account}->{show_checkin_message} = 1;
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,
517 $circ->{screen_msg}, 'Item checked-in: Homebranch - My Location.',
518 "Checkin message when show_checkin_message is enabled and UseLocationAsAQInSIP is disabled"
521 t::lib::Mocks::mock_preference( 'UseLocationAsAQInSIP', '1' );
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,
529 $circ->{screen_msg}, 'Item checked-in: My Permanent Location - My Location.',
530 "Checkin message when both show_checkin_message and UseLocationAsAQInSIP are enabled"
535 subtest 'Checkin with fines' => sub {
538 my $mockILS = Test::MockObject->new;
539 my $server = { ils => $mockILS };
540 my $library = $builder->build_object( { class => 'Koha::Libraries' } );
543 implementation => "ILS",
552 my $ils = C4::SIP::ILS->new($institution);
553 my $item = $builder->build_sample_item(
555 library => $library->branchcode,
559 # show_outstanding_amount disabled
560 my $patron = $builder->build_object(
562 class => 'Koha::Patrons',
564 branchcode => $library->branchcode,
568 my $circ = $ils->checkout( $patron->cardnumber, $item->barcode, undef, undef, $server->{account} );
569 my $fee1 = $builder->build(
571 source => 'Accountline',
573 borrowernumber => $patron->borrowernumber,
574 amountoutstanding => 12,
575 debit_type_code => 'OVERDUE',
576 itemnumber => $item->itemnumber
580 $circ = $ils->checkin(
581 $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode, undef, undef,
584 is( $circ->{screen_msg}, '', "The fine is not displayed on checkin when show_outstanding_amount is disabled" );
586 # show_outstanding_amount enabled
587 $patron = $builder->build_object(
589 class => 'Koha::Patrons',
591 branchcode => $library->branchcode,
595 $circ = $ils->checkout( $patron->cardnumber, $item->barcode, undef, undef, $server->{account} );
597 $fee1 = $builder->build(
599 source => 'Accountline',
601 borrowernumber => $patron->borrowernumber,
602 amountoutstanding => 12,
603 debit_type_code => 'OVERDUE',
604 itemnumber => $item->itemnumber
609 $server->{account}->{show_outstanding_amount} = 1;
610 $circ = $ils->checkout( $patron->cardnumber, $item->barcode, undef, undef, $server->{account} );
612 $circ = $ils->checkin(
613 $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode, undef, undef,
617 $circ->{screen_msg}, 'You owe $12.00 for this item.',
618 "The fine is displayed on checkin when show_outstanding_amount is enabled"
624 subtest do_checkout_with_sysprefs_override => sub {
627 my $mockILS = Test::MockObject->new;
628 my $server = { ils => $mockILS };
629 my $library = $builder->build_object( { class => 'Koha::Libraries' } );
632 implementation => "ILS",
641 my $ils = C4::SIP::ILS->new($institution);
642 my $item = $builder->build_sample_item(
644 library => $library->branchcode,
648 my $patron_under_noissuescharge = $builder->build_object(
650 class => 'Koha::Patrons',
652 branchcode => $library->branchcode,
657 my $fee_under_noissuescharge = $builder->build(
659 source => 'Accountline',
661 borrowernumber => $patron_under_noissuescharge->borrowernumber,
662 amountoutstanding => 4,
663 debit_type_code => 'OVERDUE',
668 my $patron_over_noissuescharge = $builder->build_object(
671 class => 'Koha::Patrons',
673 branchcode => $library->branchcode,
678 my $fee_over_noissuescharge = $builder->build(
680 source => 'Accountline',
682 borrowernumber => $patron_over_noissuescharge->borrowernumber,
683 amountoutstanding => 6,
684 debit_type_code => 'OVERDUE',
689 $server->{account}->{override_fine_on_checkout} = 0;
691 t::lib::Mocks::mock_preference( 'AllFinesNeedOverride', '0' );
692 t::lib::Mocks::mock_preference( 'AllowFineOverride', '0' );
694 $ils->checkout( $patron_under_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
696 $patron_under_noissuescharge->checkouts->count, 1,
697 'Checkout is allowed when the amount is under noissuecharge, AllFinesNeedOverride and AllowFineOverride disabled'
700 $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode );
702 $circ = $ils->checkout( $patron_over_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
704 $patron_over_noissuescharge->checkouts->count, 0,
705 'Checkout is blocked when the amount is over noissuecharge, AllFinesNeedOverride and AllowFineOverride disabled'
708 t::lib::Mocks::mock_preference( 'AllFinesNeedOverride', '0' );
709 t::lib::Mocks::mock_preference( 'AllowFineOverride', '1' );
712 $ils->checkout( $patron_under_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
714 $patron_under_noissuescharge->checkouts->count, 1,
715 'Checkout is allowed when the amount is under noissuecharge, AllFinesNeedOverride disabled and AllowFineOverride enabled'
718 $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode );
720 $circ = $ils->checkout( $patron_over_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
722 $patron_over_noissuescharge->checkouts->count, 0,
723 'Checkout is blocked when the amount is over noissuecharge, AllFinesNeedOverride disabled and AllowFineOverride enabled'
726 t::lib::Mocks::mock_preference( 'AllFinesNeedOverride', '1' );
727 t::lib::Mocks::mock_preference( 'AllowFineOverride', '0' );
730 $ils->checkout( $patron_under_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
732 $patron_under_noissuescharge->checkouts->count, 0,
733 'Checkout is blocked when the amount is under noissuecharge, AllFinesNeedOverride enabled and AllowFineOverride disabled'
736 $circ = $ils->checkout( $patron_over_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
738 $patron_over_noissuescharge->checkouts->count, 0,
739 'Checkout is blocked when the amount is over noissuecharge, AllFinesNeedOverride enabled and AllowFineOverride disabled'
742 t::lib::Mocks::mock_preference( 'AllFinesNeedOverride', '1' );
743 t::lib::Mocks::mock_preference( 'AllowFineOverride', '1' );
746 $ils->checkout( $patron_under_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
748 $patron_under_noissuescharge->checkouts->count, 0,
749 'Checkout is blocked when the amount is under noissuecharge, AllFinesNeedOverride and AllowFineOverride enabled'
752 $circ = $ils->checkout( $patron_over_noissuescharge->cardnumber, $item->barcode, undef, undef, $server->{account} );
754 $patron_over_noissuescharge->checkouts->count, 0,
755 'Checkout is blocked when the amount is over noissuecharge, AllFinesNeedOverride and AllowFineOverride enabled'
759 subtest do_checkout_with_patron_blocked => sub {
762 my $mockILS = Test::MockObject->new;
763 my $server = { ils => $mockILS };
764 my $library = $builder->build_object( { class => 'Koha::Libraries' } );
767 implementation => "ILS",
776 my $ils = C4::SIP::ILS->new($institution);
777 my $item = $builder->build_sample_item(
779 library => $library->branchcode,
783 my $expired_patron = $builder->build_object(
785 class => 'Koha::Patrons',
787 branchcode => $library->branchcode,
788 dateexpiry => '2020/01/03',
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" );
795 my $fines_patron = $builder->build_object(
797 class => 'Koha::Patrons',
799 branchcode => $library->branchcode,
803 my $fee1 = $builder->build(
805 source => 'Accountline',
807 borrowernumber => $fines_patron->borrowernumber,
808 amountoutstanding => 10,
809 debit_type_code => 'OVERDUE',
814 my $fines_sip_patron = C4::SIP::ILS::Patron->new( $fines_patron->cardnumber );
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" );
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(
824 class => 'Koha::Patrons',
826 branchcode => $library->branchcode,
827 debarred => '9999/01/01',
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" );
835 my $overdue_patron = $builder->build_object(
837 class => 'Koha::Patrons',
839 branchcode => $library->branchcode,
844 my $odue = $builder->build(
848 borrowernumber => $overdue_patron->borrowernumber,
849 date_due => '2017-01-01',
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" );
860 subtest do_checkout_with_noblock => sub {
863 my $library = $builder->build_object( { class => 'Koha::Libraries' } );
864 my $patron = $builder->build_object(
866 class => 'Koha::Patrons',
868 branchcode => $library->branchcode,
869 debarred => '9999/01/01',
874 t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
876 my $item = $builder->build_sample_item(
878 library => $library->branchcode,
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();
886 $co_transaction->patron($sip_patron),
887 $sip_patron, "Patron assigned to transaction"
890 $co_transaction->item($sip_item),
891 $sip_item, "Item assigned to transaction"
894 $co_transaction->do_checkout( undef, '19990102 030405' );
896 is( $patron->checkouts->count, 1, 'No Block checkout was performed for debarred patron' );
899 subtest do_checkout_with_holds => sub {
902 my $library = $builder->build_object( { class => 'Koha::Libraries' } );
903 my $patron = $builder->build_object(
905 class => 'Koha::Patrons',
907 branchcode => $library->branchcode,
911 my $patron2 = $builder->build_object(
913 class => 'Koha::Patrons',
915 branchcode => $library->branchcode,
920 t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
922 my $item = $builder->build_sample_item(
924 library => $library->branchcode,
928 my $reserve = AddReserve(
930 branchcode => $library->branchcode,
931 borrowernumber => $patron2->borrowernumber,
932 biblionumber => $item->biblionumber,
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();
940 $co_transaction->patron($sip_patron),
941 $sip_patron, "Patron assigned to transaction"
944 $co_transaction->item($sip_item),
945 $sip_item, "Item assigned to transaction"
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)' );
955 $co_transaction->do_checkout();
956 is( $patron->checkouts->count, 0, 'Checkout was not done due to attached hold (T)' );
958 $hold->set_processing;
959 $co_transaction->do_checkout();
960 is( $patron->checkouts->count, 0, 'Checkout was not done due to attached hold (P)' );
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' );
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' );
973 subtest checkin_lost => sub {
976 my $library = $builder->build_object( { class => 'Koha::Libraries' } );
978 t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
980 my $item = $builder->build_sample_item(
982 library => $library->branchcode,
986 $item->itemlost(1)->itemlost_on(dt_from_string)->store();
990 implementation => "ILS",
999 my $ils = C4::SIP::ILS->new($instituation);
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" );
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" );
1010 subtest checkin_withdrawn => sub {
1013 my $library = $builder->build_object( { class => 'Koha::Libraries' } );
1015 t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
1017 my $item = $builder->build_sample_item(
1019 library => $library->branchcode,
1023 $item->withdrawn(1)->withdrawn_on(dt_from_string)->store();
1025 my $instituation = {
1027 implementation => "ILS",
1036 my $ils = C4::SIP::ILS->new($instituation);
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" );
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" );
1047 subtest _get_sort_bin => sub {
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;
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
1068 $branch:homebranch:Z\r
1071 t::lib::Mocks::mock_preference( 'SIP2SortBinMapping', $rules );
1073 my $item_cd = $builder->build_sample_item(
1075 library => $library->branchcode,
1080 my $item_book = $builder->build_sample_item(
1082 library => $library->branchcode,
1084 itemcallnumber => '200.01'
1088 my $item_book2 = $builder->build_sample_item(
1090 library => $library2->branchcode,
1096 my $item_dvd = $builder->build_sample_item(
1098 library => $library2->branchcode,
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)" );
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)" );
1120 $bin = C4::SIP::ILS::Transaction::Checkin::_get_sort_bin( $item_book2, $library2->branchcode );
1121 is( $bin, '6', "Rules with multiple field matches" );
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";
1128 subtest item_circulation_status => sub {
1131 my $library = $builder->build_object( { class => 'Koha::Libraries' } );
1132 my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
1134 my $patron = $builder->build_object(
1136 class => 'Koha::Patrons',
1138 branchcode => $library->branchcode,
1143 t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
1145 my $item = $builder->build_sample_item(
1147 library => $library->branchcode,
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" );
1155 my $transfer = Koha::Item::Transfer->new(
1157 itemnumber => $item->id,
1158 datesent => '2020-01-01',
1159 frombranch => $library->branchcode,
1160 tobranch => $library2->branchcode,
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" );
1170 my $claim = Koha::Checkouts::ReturnClaim->new(
1172 itemnumber => $item->id,
1173 borrowernumber => $patron->id,
1174 created_by => $patron->id,
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" );
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();
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();
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();
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();
1214 subtest do_checkout_with_recalls => sub {
1217 my $library = $builder->build_object( { class => 'Koha::Libraries' } );
1218 my $patron = $builder->build_object(
1220 class => 'Koha::Patrons',
1222 branchcode => $library->branchcode,
1226 my $patron2 = $builder->build_object(
1228 class => 'Koha::Patrons',
1230 branchcode => $library->branchcode,
1235 t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode, flags => 1 } );
1237 my $item = $builder->build_sample_item(
1239 library => $library->branchcode,
1243 t::lib::Mocks::mock_preference( 'UseRecalls', 1 );
1244 Koha::CirculationRules->set_rule(
1246 branchcode => undef,
1247 categorycode => undef,
1249 rule_name => 'recalls_allowed',
1254 my $recall1 = Koha::Recall->new(
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,
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();
1270 $co_transaction->patron($sip_patron),
1271 $sip_patron, "Patron assigned to transaction"
1274 $co_transaction->item($sip_item),
1275 $sip_item, "Item assigned to transaction"
1278 # Test recalls made by another patron
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' );
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' );
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' );
1294 $recall1->set_cancelled;
1296 my $recall2 = Koha::Recall->new(
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,
1308 # Test recalls made by SIP patron
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' );
1317 $schema->storage->txn_rollback;