Bug 17600: Standardize our EXPORT_OK
[srvgit] / t / db_dependent / SIP / Transaction.t
index bc7af8b..1f8cab6 100755 (executable)
@@ -4,19 +4,24 @@
 # Current state is very rudimentary. Please help to extend it!
 
 use Modern::Perl;
-use Test::More tests => 6;
+use Test::More tests => 12;
 
 use Koha::Database;
 use t::lib::TestBuilder;
 use t::lib::Mocks;
+use C4::SIP::ILS;
 use C4::SIP::ILS::Patron;
 use C4::SIP::ILS::Transaction::RenewAll;
 use C4::SIP::ILS::Transaction::Checkout;
 use C4::SIP::ILS::Transaction::FeePayment;
 use C4::SIP::ILS::Transaction::Hold;
+use C4::SIP::ILS::Transaction::Checkout;
+use C4::SIP::ILS::Transaction::Checkin;
 
-use C4::Reserves;
+use C4::Reserves qw( AddReserve ModReserve ModReserveAffect RevertWaitingStatus );
 use Koha::CirculationRules;
+use Koha::Item::Transfer;
+use Koha::DateUtils qw( dt_from_string output_pref );
 
 my $schema = Koha::Database->new->schema;
 $schema->storage->txn_begin;
@@ -46,25 +51,20 @@ subtest fill_holds_at_checkout => sub {
     t::lib::Mocks::mock_userenv({ branchcode => $branch->{branchcode}, flags => 1 });
 
     my $itype = $builder->build({ source => 'Itemtype', value =>{notforloan=>0} });
-    my $biblio = $builder->build({ source => 'Biblio' });
-    my $biblioitem = $builder->build({ source => 'Biblioitem', value=>{biblionumber=>$biblio->{biblionumber}} });
-    my $item1 = $builder->build({ source => 'Item', value => {
+    my $item1 = $builder->build_sample_item({
         barcode       => 'barcode4test',
         homebranch    => $branch->{branchcode},
         holdingbranch => $branch->{branchcode},
-        biblionumber  => $biblio->{biblionumber},
         itype         => $itype->{itemtype},
         notforloan       => 0,
-        }
-    });
-    my $item2 = $builder->build({ source => 'Item', value => {
+    })->unblessed;
+    my $item2 = $builder->build_sample_item({
         homebranch    => $branch->{branchcode},
         holdingbranch => $branch->{branchcode},
-        biblionumber  => $biblio->{biblionumber},
+        biblionumber  => $item1->{biblionumber},
         itype         => $itype->{itemtype},
         notforloan       => 0,
-        }
-    });
+    })->unblessed;
 
     Koha::CirculationRules->set_rules(
         {
@@ -85,18 +85,18 @@ subtest fill_holds_at_checkout => sub {
         {
             branchcode     => $branch->{branchcode},
             borrowernumber => $borrower->{borrowernumber},
-            biblionumber   => $biblio->{biblionumber}
+            biblionumber   => $item1->{biblionumber}
         }
     );
     my $reserve2 = AddReserve(
         {
             branchcode     => $branch->{branchcode},
             borrowernumber => $borrower->{borrowernumber},
-            biblionumber   => $biblio->{biblionumber}
+            biblionumber   => $item1->{biblionumber}
         }
     );
 
-    my $bib = Koha::Biblios->find( $biblio->{biblionumber} );
+    my $bib = Koha::Biblios->find( $item1->{biblionumber} );
     is( $bib->holds->count(), 2, "Bib has 2 holds");
 
     my $sip_patron = C4::SIP::ILS::Patron->new( $borrower->{cardnumber} );
@@ -209,4 +209,399 @@ subtest cancel_hold => sub {
     is( $item->holds->count(), 0,  "Item has 0 holds remaining");
 };
 
+subtest do_hold => sub {
+    plan tests => 8;
+
+    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
+    my $patron_1 = $builder->build_object(
+        {
+            class => 'Koha::Patrons',
+            value => {
+                branchcode => $library->branchcode,
+            }
+        }
+    );
+    my $patron_2 = $builder->build_object(
+        {
+            class => 'Koha::Patrons',
+            value => {
+                branchcode   => $library->branchcode,
+                categorycode => $patron_1->categorycode,
+            }
+        }
+    );
+
+    t::lib::Mocks::mock_userenv(
+        { branchcode => $library->branchcode, flags => 1 } );
+
+    my $item = $builder->build_sample_item(
+        {
+            library => $library->branchcode,
+        }
+    );
+
+    my $reserve1 = AddReserve(
+        {
+            branchcode     => $library->branchcode,
+            borrowernumber => $patron_1->borrowernumber,
+            biblionumber   => $item->biblio->biblionumber,
+            itemnumber     => $item->itemnumber,
+        }
+    );
+    is( $item->biblio->holds->count(), 1, "Hold was placed on bib" );
+    is( $item->holds->count(),         1, "Hold was placed on specific item" );
+
+    my $sip_patron  = C4::SIP::ILS::Patron->new( $patron_2->cardnumber );
+    my $sip_item    = C4::SIP::ILS::Item->new( $item->barcode );
+    my $transaction = C4::SIP::ILS::Transaction::Hold->new();
+    is(
+        ref $transaction,
+        "C4::SIP::ILS::Transaction::Hold",
+        "New transaction created"
+    );
+    is( $transaction->patron($sip_patron),
+        $sip_patron, "Patron assigned to transaction" );
+    is( $transaction->item($sip_item),
+        $sip_item, "Item assigned to transaction" );
+    my $hold = $transaction->do_hold();
+    is( $item->biblio->holds->count(), 2, "Bib has 2 holds" );
+
+    my $THE_hold = $patron_2->holds->next;
+    is( $THE_hold->priority, 2, 'Hold placed from SIP should have a correct priority of 2');
+    is( $THE_hold->branchcode, $patron_2->branchcode, 'Hold placed from SIP should have the branchcode set' );
+};
+
+subtest do_checkin => sub {
+    plan tests => 12;
+
+    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
+    my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
+    my $patron = $builder->build_object(
+        {
+            class => 'Koha::Patrons',
+            value => {
+                branchcode => $library->branchcode,
+            }
+        }
+    );
+
+    t::lib::Mocks::mock_userenv(
+        { branchcode => $library->branchcode, flags => 1 } );
+
+    my $item = $builder->build_sample_item(
+        {
+            library => $library->branchcode,
+        }
+    );
+
+
+    # Checkout
+    my $sip_patron  = C4::SIP::ILS::Patron->new( $patron->cardnumber );
+    my $sip_item    = C4::SIP::ILS::Item->new( $item->barcode );
+    my $co_transaction = C4::SIP::ILS::Transaction::Checkout->new();
+    is( $co_transaction->patron($sip_patron),
+        $sip_patron, "Patron assigned to transaction" );
+    is( $co_transaction->item($sip_item),
+        $sip_item, "Item assigned to transaction" );
+    my $checkout = $co_transaction->do_checkout();
+    is( $patron->checkouts->count, 1, 'Checkout should have been done successfully');
+
+    # Checkin
+    my $ci_transaction = C4::SIP::ILS::Transaction::Checkin->new();
+    is( $ci_transaction->patron($sip_patron),
+        $sip_patron, "Patron assigned to transaction" );
+    is( $ci_transaction->item($sip_item),
+        $sip_item, "Item assigned to transaction" );
+
+    my $checkin = $ci_transaction->do_checkin($library->branchcode, C4::SIP::Sip::timestamp);
+    is( $patron->checkouts->count, 0, 'Checkin should have been done successfully');
+
+    # Test checkin without return date
+    $co_transaction->do_checkout;
+    is( $patron->checkouts->count, 1, 'Checkout should have been done successfully');
+    $ci_transaction->do_checkin($library->branchcode, undef);
+    is( $patron->checkouts->count, 0, 'Checkin should have been done successfully');
+
+    my $result  = $ci_transaction->do_checkin($library2->branchcode, undef);
+    is($ci_transaction->alert_type,'04',"Checkin of item no issued at another branch succeeds");
+    is_deeply(
+        $result,
+        {
+            messages => {
+                'NotIssued'       => $item->barcode,
+                'WasTransfered'   => $library->branchcode,
+                'TransferTrigger' => 'ReturnToHome'
+            }
+        },
+        "Messages show not issued and transferred"
+    );
+    is( $ci_transaction->item->destination_loc,$library->branchcode,"Item destination correctly set");
+
+    subtest 'Checkin an in transit item' => sub {
+
+        plan tests => 5;
+
+        my $library_1 = $builder->build_object({ class => 'Koha::Libraries' });
+        my $library_2 = $builder->build_object({ class => 'Koha::Libraries' });
+
+        my $patron = $builder->build_object({ class => 'Koha::Patrons', value => {branchcode => $library_1->branchcode, }});
+        my $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );
+        my $item = $builder->build_sample_item({ library => $library_1->branchcode });
+        my $sip_item   = C4::SIP::ILS::Item->new( $item->barcode );
+
+        t::lib::Mocks::mock_userenv(
+            { branchcode => $library_1->branchcode, flags => 1 } );
+
+        my $reserve = AddReserve(
+            {
+                branchcode     => $library_1->branchcode,
+                borrowernumber => $patron->borrowernumber,
+                biblionumber   => $item->biblionumber,
+            }
+        );
+
+        ModReserveAffect( $item->itemnumber, $patron->borrowernumber ); # Mark waiting
+
+        my $ci_transaction = C4::SIP::ILS::Transaction::Checkin->new();
+        is( $ci_transaction->patron($sip_patron),
+            $sip_patron, "Patron assigned to transaction" );
+        is( $ci_transaction->item($sip_item),
+            $sip_item, "Item assigned to transaction" );
+
+        my $checkin = $ci_transaction->do_checkin($library_2->branchcode, C4::SIP::Sip::timestamp);
+
+        my $hold = Koha::Holds->find($reserve);
+        is( $hold->found, 'T', );
+        is( $hold->itemnumber, $item->itemnumber, );
+        is( Koha::Checkouts->search({itemnumber => $item->itemnumber})->count, 0, );
+    };
+};
+
+subtest do_checkout_with_holds => sub {
+    plan tests => 7;
+
+    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
+    my $patron = $builder->build_object(
+        {
+            class => 'Koha::Patrons',
+            value => {
+                branchcode => $library->branchcode,
+            }
+        }
+    );
+    my $patron2 = $builder->build_object(
+        {
+            class => 'Koha::Patrons',
+            value => {
+                branchcode => $library->branchcode,
+            }
+        }
+    );
+
+    t::lib::Mocks::mock_userenv(
+        { branchcode => $library->branchcode, flags => 1 } );
+
+    my $item = $builder->build_sample_item(
+        {
+            library => $library->branchcode,
+        }
+    );
+
+    my $reserve = AddReserve(
+        {
+                branchcode     => $library->branchcode,
+                borrowernumber => $patron2->borrowernumber,
+                biblionumber   => $item->biblionumber,
+        }
+    );
+
+    my $sip_patron  = C4::SIP::ILS::Patron->new( $patron->cardnumber );
+    my $sip_item    = C4::SIP::ILS::Item->new( $item->barcode );
+    my $co_transaction = C4::SIP::ILS::Transaction::Checkout->new();
+    is( $co_transaction->patron($sip_patron),
+        $sip_patron, "Patron assigned to transaction" );
+    is( $co_transaction->item($sip_item),
+        $sip_item, "Item assigned to transaction" );
+
+    # Test attached holds
+    ModReserveAffect( $item->itemnumber, $patron->borrowernumber, 0, $reserve ); # Mark waiting (W)
+    my $hold = Koha::Holds->find($reserve);
+    $co_transaction->do_checkout();
+    is( $patron->checkouts->count, 0, 'Checkout was not done due to attached hold (W)');
+
+    $hold->set_transfer;
+    $co_transaction->do_checkout();
+    is( $patron->checkouts->count, 0, 'Checkout was not done due to attached hold (T)');
+
+    $hold->set_processing;
+    $co_transaction->do_checkout();
+    is( $patron->checkouts->count, 0, 'Checkout was not done due to attached hold (P)');
+
+    # Test non-attached holds
+    C4::Reserves::RevertWaitingStatus({ itemnumber => $hold->itemnumber });
+    t::lib::Mocks::mock_preference('AllowItemsOnHoldCheckoutSIP', '0');
+    $co_transaction->do_checkout();
+    is( $patron->checkouts->count, 0, 'Checkout refused due to hold and AllowItemsOnHoldCheckoutSIP');
+
+    t::lib::Mocks::mock_preference('AllowItemsOnHoldCheckoutSIP', '1');
+    $co_transaction->do_checkout();
+    is( $patron->checkouts->count, 1, 'Checkout allowed due to hold and AllowItemsOnHoldCheckoutSIP');
+};
+
+subtest checkin_lost => sub {
+    plan tests => 2;
+
+    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
+
+    t::lib::Mocks::mock_userenv(
+        { branchcode => $library->branchcode, flags => 1 } );
+
+    my $item = $builder->build_sample_item(
+        {
+            library     => $library->branchcode,
+        }
+    );
+
+    $item->itemlost(1)->itemlost_on(dt_from_string)->store();
+
+    my $instituation = {
+        id             => $library->id,
+        implementation => "ILS",
+        policy         => {
+            checkin  => "true",
+            renewal  => "true",
+            checkout => "true",
+            timeout  => 100,
+            retries  => 5,
+        }
+    };
+    my $ils = C4::SIP::ILS->new( $instituation );
+
+    t::lib::Mocks::mock_preference('BlockReturnOfLostItems', '1');
+    my $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode );
+    is( $circ->{screen_msg}, 'Item lost, return not allowed', "Got correct screen message" );
+
+    t::lib::Mocks::mock_preference('BlockReturnOfLostItems', '0');
+    $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode );
+    is( $circ->{screen_msg}, 'Item not checked out', "Got 'Item not checked out' screen message" );
+};
+
+subtest checkin_withdrawn => sub {
+    plan tests => 2;
+
+    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
+
+    t::lib::Mocks::mock_userenv(
+        { branchcode => $library->branchcode, flags => 1 } );
+
+    my $item = $builder->build_sample_item(
+        {
+            library     => $library->branchcode,
+        }
+    );
+
+    $item->withdrawn(1)->withdrawn_on(dt_from_string)->store();
+
+    my $instituation = {
+        id             => $library->id,
+        implementation => "ILS",
+        policy         => {
+            checkin  => "true",
+            renewal  => "true",
+            checkout => "true",
+            timeout  => 100,
+            retries  => 5,
+        }
+    };
+    my $ils = C4::SIP::ILS->new( $instituation );
+
+    t::lib::Mocks::mock_preference('BlockReturnOfWithdrawnItems', '1');
+    my $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode );
+    is( $circ->{screen_msg}, 'Item withdrawn, return not allowed', "Got correct screen message" );
+
+    t::lib::Mocks::mock_preference('BlockReturnOfWithdrawnItems', '0');
+    $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp, undef, $library->branchcode );
+    is( $circ->{screen_msg}, 'Item not checked out', "Got 'Item not checked out' screen message" );
+};
+
+subtest item_circulation_status => sub {
+    plan tests => 7;
+
+    my $library  = $builder->build_object( { class => 'Koha::Libraries' } );
+    my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
+
+    my $patron = $builder->build_object(
+        {
+            class => 'Koha::Patrons',
+            value => {
+                branchcode => $library->branchcode,
+            }
+        }
+    );
+
+    t::lib::Mocks::mock_userenv(
+        { branchcode => $library->branchcode, flags => 1 } );
+
+    my $item = $builder->build_sample_item(
+        {
+            library => $library->branchcode,
+        }
+    );
+
+    my $sip_item = C4::SIP::ILS::Item->new( $item->barcode );
+    my $status = $sip_item->sip_circulation_status;
+    is( $status, '03', "Item circulation status is available");
+
+    my $transfer = Koha::Item::Transfer->new({
+        itemnumber => $item->id,
+        datesent   => '2020-01-01',
+        frombranch => $library->branchcode,
+        tobranch   => $library2->branchcode,
+    })->store();
+
+    $sip_item = C4::SIP::ILS::Item->new( $item->barcode );
+    $status = $sip_item->sip_circulation_status;
+    is( $status, '10', "Item circulation status is in transit" );
+
+    $transfer->delete;
+
+    my $claim = Koha::Checkouts::ReturnClaim->new({
+        itemnumber     => $item->id,
+        borrowernumber => $patron->id,
+        created_by     => $patron->id,
+    })->store();
+
+    $sip_item = C4::SIP::ILS::Item->new( $item->barcode );
+    $status = $sip_item->sip_circulation_status;
+    is( $status, '11', "Item circulation status is claimed returned" );
+
+    $claim->delete;
+
+    $item->itemlost(1)->store();
+    $sip_item = C4::SIP::ILS::Item->new( $item->barcode );
+    $status = $sip_item->sip_circulation_status;
+    is( $status, '12', "Item circulation status is lost" );
+    $item->itemlost(0)->store();
+
+    my $location = $item->location;
+    $item->location("CART")->store();
+    $sip_item = C4::SIP::ILS::Item->new( $item->barcode );
+    $status = $sip_item->sip_circulation_status;
+    is( $status, '09', "Item circulation status is waiting to be re-shelved" );
+    $item->location($location)->store();
+
+    my $nfl = $item->notforloan;
+    $item->notforloan(-1)->store();
+    $sip_item = C4::SIP::ILS::Item->new( $item->barcode );
+    $status = $sip_item->sip_circulation_status;
+    is( $status, '02', "Item circulation status is on order" );
+    $item->notforloan($nfl)->store();
+
+    my $damaged = $item->damaged;
+    $item->damaged(1)->store();
+    $sip_item = C4::SIP::ILS::Item->new( $item->barcode );
+    $status = $sip_item->sip_circulation_status;
+    is( $status, '01', "Item circulation status is damaged" );
+    $item->damaged(0)->store();
+};
 $schema->storage->txn_rollback;