Bug 17600: Standardize our EXPORT_OK
[srvgit] / t / db_dependent / Reserves.t
index e9f1de6..e748f11 100755 (executable)
@@ -17,7 +17,7 @@
 
 use Modern::Perl;
 
-use Test::More tests => 62;
+use Test::More tests => 68;
 use Test::MockModule;
 use Test::Warn;
 
@@ -27,11 +27,12 @@ use t::lib::TestBuilder;
 use MARC::Record;
 use DateTime::Duration;
 
-use C4::Circulation;
+use C4::Circulation qw( AddReturn AddIssue );
 use C4::Items;
-use C4::Biblio;
+use C4::Biblio qw( GetMarcBiblio GetMarcFromKohaField ModBiblio );
 use C4::Members;
-use C4::Reserves;
+use C4::Reserves qw( AddReserve CheckReserves GetReservesControlBranch ModReserve ModReserveAffect ReserveSlip CalculatePriority CanReserveBeCanceledFromOpac CanBookBeReserved IsAvailableForItemLevelRequest MoveReserve ChargeReserveFee RevertWaitingStatus CanItemBeReserved MergeHolds );
+use Koha::ActionLogs;
 use Koha::Caches;
 use Koha::DateUtils;
 use Koha::Holds;
@@ -87,10 +88,7 @@ my $bibnum = $builder->build_sample_biblio({frameworkcode => $frameworkcode})->b
 # Create a helper item instance for testing
 my $item = $builder->build_sample_item({ biblionumber => $bibnum, library => $branch_1, itype => $itemtype });
 
-my $biblio_with_no_item = $builder->build({
-    source => 'Biblio'
-});
-
+my $biblio_with_no_item = $builder->build_sample_biblio;
 
 # Modify item; setting barcode.
 my $testbarcode = '97531';
@@ -200,7 +198,7 @@ Koha::CirculationRules->set_rules(
         branchcode   => $branch_1,
         itemtype     => undef,
         rules        => {
-            holdallowed  => 1,
+            holdallowed  => 'from_home_library',
             returnbranch => 'homebranch',
         }
     }
@@ -212,7 +210,7 @@ Koha::CirculationRules->set_rules(
         branchcode   => $branch_2,
         itemtype     => undef,
         rules        => {
-            holdallowed  => 2,
+            holdallowed  => 'from_any_library',
             returnbranch => 'homebranch',
         }
     }
@@ -557,6 +555,9 @@ $cancancel = CanReserveBeCanceledFromOpac($canres->{reserve_id}, $requesters{$br
 is($cancancel, 0, 'Reserve in transfer status cant be canceled');
 
 $dbh->do('DELETE FROM reserves', undef, ($bibnum));
+is( CanReserveBeCanceledFromOpac($canres->{resserve_id}, $requesters{$branch_1}), undef,
+    'Cannot cancel a deleted hold' );
+
 AddReserve(
     {
         branchcode     => $branch_1,
@@ -601,7 +602,7 @@ Koha::Patrons->find( $borrowernumber )->set({ dateofbirth => $borrower->{dateofb
 
 is( C4::Reserves::CanBookBeReserved($borrowernumber, $biblionumber)->{status} , 'OK', "Reserving a 'PEGI 16' Biblio by a 30 year old borrower succeeds");
 
-is( C4::Reserves::CanBookBeReserved($borrowernumber, $biblio_with_no_item->{biblionumber})->{status} , '', "Biblio with no item. Status is empty");
+is( C4::Reserves::CanBookBeReserved($borrowernumber, $biblio_with_no_item->biblionumber)->{status} , '', "Biblio with no item. Status is empty");
        ####
 ####### EO Bug 13113 <<<
        ####
@@ -869,6 +870,7 @@ subtest 'ReservesNeedReturns' => sub {
         value => {
           itemnumber  => $item->itemnumber,
           datearrived => undef,
+          datecancelled => undef
         }
     });
     $item->damaged(0)->store;
@@ -1026,7 +1028,7 @@ subtest 'MoveReserve additional test' => sub {
     is($patron_2->holds->next()->reserve_id, $reserve_2, "The 2nd patron has a hold");
 
     # Fake the holds queue
-    $dbh->do(q{INSERT INTO hold_fill_targets VALUES (?, ?, ?, ?, ?)},undef,($patron_1->borrowernumber,$biblio->biblionumber,$item_1->itemnumber,$item_1->homebranch,0));
+    $dbh->do(q{INSERT INTO hold_fill_targets VALUES (?, ?, ?, ?, ?,?)},undef,($patron_1->borrowernumber,$biblio->biblionumber,$item_1->itemnumber,$item_1->homebranch,0,$reserve_1));
 
     # The 2nd hold should be filed even if the item is preselected for the first hold
     MoveReserve($item_1->itemnumber,$patron_2->borrowernumber);
@@ -1035,6 +1037,301 @@ subtest 'MoveReserve additional test' => sub {
 
 };
 
+subtest 'RevertWaitingStatus' => sub {
+
+    plan tests => 2;
+
+    # Create the items and patrons we need
+    my $biblio  = $builder->build_sample_biblio();
+    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
+    my $itype   = $builder->build_object(
+        { class => "Koha::ItemTypes", value => { notforloan => 0 } } );
+    my $item_1 = $builder->build_sample_item(
+        {
+            biblionumber => $biblio->biblionumber,
+            itype        => $itype->itemtype,
+            library      => $library->branchcode
+        }
+    );
+    my $patron_1 = $builder->build_object( { class => "Koha::Patrons" } );
+    my $patron_2 = $builder->build_object( { class => "Koha::Patrons" } );
+    my $patron_3 = $builder->build_object( { class => "Koha::Patrons" } );
+    my $patron_4 = $builder->build_object( { class => "Koha::Patrons" } );
+
+    # Place a hold on the title for both patrons
+    my $priority = 1;
+    my $hold_1 = place_item_hold( $patron_1, $item_1, $library, $priority );
+    my $hold_2 = place_item_hold( $patron_2, $item_1, $library, $priority );
+    my $hold_3 = place_item_hold( $patron_3, $item_1, $library, $priority );
+    my $hold_4 = place_item_hold( $patron_4, $item_1, $library, $priority );
+
+    $hold_1->set_waiting;
+    AddIssue( $patron_3->unblessed, $item_1->barcode, undef, 'revert' );
+
+    my $holds = $biblio->holds;
+    is( $holds->count, 3, 'One hold has been deleted' );
+    is_deeply(
+        [
+            $holds->next->priority, $holds->next->priority,
+            $holds->next->priority
+        ],
+        [ 1, 2, 3 ],
+        'priorities have been reordered'
+    );
+};
+
+subtest 'CheckReserves additional tests' => sub {
+
+    plan tests => 8;
+
+    my $item = $builder->build_sample_item;
+    my $reserve1 = $builder->build_object(
+        {
+            class => "Koha::Holds",
+            value => {
+                found            => undef,
+                priority         => 1,
+                itemnumber       => undef,
+                biblionumber     => $item->biblionumber,
+                waitingdate      => undef,
+                cancellationdate => undef,
+                item_level_hold  => 0,
+                lowestPriority   => 0,
+                expirationdate   => undef,
+                suspend_until    => undef,
+                suspend          => 0,
+                itemtype         => undef,
+            }
+        }
+    );
+    my $reserve2 = $builder->build_object(
+        {
+            class => "Koha::Holds",
+            value => {
+                found            => undef,
+                priority         => 2,
+                biblionumber     => $item->biblionumber,
+                borrowernumber   => $reserve1->borrowernumber,
+                itemnumber       => undef,
+                waitingdate      => undef,
+                cancellationdate => undef,
+                item_level_hold  => 0,
+                lowestPriority   => 0,
+                expirationdate   => undef,
+                suspend_until    => undef,
+                suspend          => 0,
+                itemtype         => undef,
+            }
+        }
+    );
+
+    my $tmp_holdsqueue = $builder->build(
+        {
+            source => 'TmpHoldsqueue',
+            value  => {
+                borrowernumber => $reserve1->borrowernumber,
+                biblionumber   => $reserve1->biblionumber,
+            }
+        }
+    );
+    my $fill_target = $builder->build(
+        {
+            source => 'HoldFillTarget',
+            value  => {
+                borrowernumber     => $reserve1->borrowernumber,
+                biblionumber       => $reserve1->biblionumber,
+                itemnumber         => $item->itemnumber,
+                item_level_request => 0,
+            }
+        }
+    );
+
+    ModReserveAffect( $item->itemnumber, $reserve1->borrowernumber, 1,
+        $reserve1->reserve_id );
+    my ( $status, $matched_reserve, $possible_reserves ) =
+      CheckReserves( $item->itemnumber );
+
+    is( $status, 'Transferred', "We found a reserve" );
+    is( $matched_reserve->{reserve_id},
+        $reserve1->reserve_id, "We got the Transit reserve" );
+    is( scalar @$possible_reserves, 2, 'We do get both reserves' );
+
+    my $patron_B = $builder->build_object({ class => "Koha::Patrons" });
+    my $item_A = $builder->build_sample_item;
+    my $item_B = $builder->build_sample_item({
+        homebranch => $patron_B->branchcode,
+        biblionumber => $item_A->biblionumber,
+        itype => $item_A->itype
+    });
+    Koha::CirculationRules->set_rules(
+        {
+            branchcode   => undef,
+            categorycode => undef,
+            itemtype     => $item_A->itype,
+            rules        => {
+                reservesallowed => 25,
+                holds_per_record => 1,
+            }
+        }
+    );
+    Koha::CirculationRules->set_rule({
+        branchcode => undef,
+        itemtype   => $item_A->itype,
+        rule_name  => 'holdallowed',
+        rule_value => 'from_home_library'
+    });
+    my $reserve_id = AddReserve(
+        {
+            branchcode     => $patron_B->branchcode,
+            borrowernumber => $patron_B->borrowernumber,
+            biblionumber   => $item_A->biblionumber,
+            priority       => 1,
+            itemnumber     => undef,
+        }
+    );
+
+    ok( $reserve_id, "We can place a record level hold because one item is owned by patron's home library");
+    t::lib::Mocks::mock_preference('ReservesControlBranch', 'ItemHomeLibrary');
+    ( $status, $matched_reserve, $possible_reserves ) = CheckReserves( $item_A->itemnumber );
+    is( $status, "", "We do not fill the hold with item A because it is not from the patron's homebranch");
+    Koha::CirculationRules->set_rule({
+        branchcode => $item_A->homebranch,
+        itemtype   => $item_A->itype,
+        rule_name  => 'holdallowed',
+        rule_value => 'from_any_library'
+    });
+    ( $status, $matched_reserve, $possible_reserves ) = CheckReserves( $item_A->itemnumber );
+    is( $status, "Reserved", "We fill the hold with item A because item's branch rule says allow any");
+
+
+    # Changing the control branch should change only the rule we get
+    t::lib::Mocks::mock_preference('ReservesControlBranch', 'PatronLibrary');
+    ( $status, $matched_reserve, $possible_reserves ) = CheckReserves( $item_A->itemnumber );
+    is( $status, "", "We do not fill the hold with item A because it is not from the patron's homebranch");
+    Koha::CirculationRules->set_rule({
+        branchcode   => $patron_B->branchcode,
+        itemtype   => $item_A->itype,
+        rule_name  => 'holdallowed',
+        rule_value => 'from_any_library'
+    });
+    ( $status, $matched_reserve, $possible_reserves ) = CheckReserves( $item_A->itemnumber );
+    is( $status, "Reserved", "We fill the hold with item A because patron's branch rule says allow any");
+
+};
+
+subtest 'AllowHoldOnPatronPossession test' => sub {
+
+    plan tests => 4;
+
+    # Create the items and patrons we need
+    my $biblio = $builder->build_sample_biblio();
+    my $itype = $builder->build_object({ class => "Koha::ItemTypes", value => { notforloan => 0 } });
+    my $item = $builder->build_sample_item({ biblionumber => $biblio->biblionumber,notforloan => 0, itype => $itype->itemtype });
+    my $patron = $builder->build_object({ class => "Koha::Patrons",
+                                          value => { branchcode => $item->homebranch }});
+
+    C4::Circulation::AddIssue($patron->unblessed,
+                              $item->barcode);
+    t::lib::Mocks::mock_preference('AllowHoldsOnPatronsPossessions', 0);
+
+    is(C4::Reserves::CanBookBeReserved($patron->borrowernumber,
+                                       $item->biblionumber)->{status},
+       'alreadypossession',
+       'Patron cannot place hold on a book loaned to itself');
+
+    is(C4::Reserves::CanItemBeReserved($patron->borrowernumber,
+                                       $item->itemnumber)->{status},
+       'alreadypossession',
+       'Patron cannot place hold on an item loaned to itself');
+
+    t::lib::Mocks::mock_preference('AllowHoldsOnPatronsPossessions', 1);
+
+    is(C4::Reserves::CanBookBeReserved($patron->borrowernumber,
+                                       $item->biblionumber)->{status},
+       'OK',
+       'Patron can place hold on a book loaned to itself');
+
+    is(C4::Reserves::CanItemBeReserved($patron->borrowernumber,
+                                       $item->itemnumber)->{status},
+       'OK',
+       'Patron can place hold on an item loaned to itself');
+};
+
+subtest 'MergeHolds' => sub {
+
+    plan tests => 1;
+
+    my $biblio_1  = $builder->build_sample_biblio();
+    my $biblio_2  = $builder->build_sample_biblio();
+    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
+    my $itype   = $builder->build_object(
+        { class => "Koha::ItemTypes", value => { notforloan => 0 } } );
+    my $item_1 = $builder->build_sample_item(
+        {
+            biblionumber => $biblio_1->biblionumber,
+            itype        => $itype->itemtype,
+            library      => $library->branchcode
+        }
+    );
+    my $patron_1 = $builder->build_object( { class => "Koha::Patrons" } );
+
+    # Place a hold on $biblio_1
+    my $priority = 1;
+    place_item_hold( $patron_1, $item_1, $library, $priority );
+
+    # Move and make sure hold is now on $biblio_2
+    C4::Reserves::MergeHolds($dbh, $biblio_2->biblionumber, $biblio_1->biblionumber);
+    is( $biblio_2->holds->count, 1, 'Hold has been transferred' );
+};
+
+subtest 'ModReserveAffect logging' => sub {
+
+    plan tests => 4;
+
+    my $item = $builder->build_sample_item;
+    my $patron = $builder->build_object(
+        {
+            class => "Koha::Patrons",
+            value => { branchcode => $item->homebranch }
+        }
+    );
+
+    t::lib::Mocks::mock_userenv({ patron => $patron });
+    t::lib::Mocks::mock_preference('HoldsLog', 1);
+
+    my $reserve_id = AddReserve(
+        {
+            branchcode     => $item->homebranch,
+            borrowernumber => $patron->borrowernumber,
+            biblionumber   => $item->biblionumber,
+            priority       => 1,
+            itemnumber     => $item->itemnumber,
+        }
+    );
+
+    my $hold = Koha::Holds->find($reserve_id);
+    my $previous_timestamp = '1970-01-01 12:34:56';
+    $hold->timestamp($previous_timestamp)->store;
+
+    $hold = Koha::Holds->find($reserve_id);
+    is( $hold->timestamp, $previous_timestamp, 'Make sure the previous timestamp has been used' );
+
+    # Avoid warnings
+    my $reserve_mock = Test::MockModule->new('C4::Reserves');
+    $reserve_mock->mock( '_koha_notify_reserve', undef );
+
+    # Mark it waiting
+    ModReserveAffect( $item->itemnumber, $patron->borrowernumber );
+
+    $hold->discard_changes;
+    ok( $hold->is_waiting, 'Hold has been set waiting' );
+    isnt( $hold->timestamp, $previous_timestamp, 'The timestamp has been modified' );
+
+    my $log = Koha::ActionLogs->search({ module => 'HOLDS', action => 'MODIFY', object => $hold->reserve_id })->next;
+    my $expected = sprintf q{'timestamp' => '%s'}, $hold->timestamp;
+    like( $log->info, qr{$expected}, 'Timestamp logged is the current one' );
+};
+
 sub count_hold_print_messages {
     my $message_count = $dbh->selectall_arrayref(q{
         SELECT COUNT(*)