use Modern::Perl;
-use Test::More tests => 62;
+use Test::More tests => 68;
use Test::MockModule;
use Test::Warn;
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;
# 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';
branchcode => $branch_1,
itemtype => undef,
rules => {
- holdallowed => 1,
+ holdallowed => 'from_home_library',
returnbranch => 'homebranch',
}
}
branchcode => $branch_2,
itemtype => undef,
rules => {
- holdallowed => 2,
+ holdallowed => 'from_any_library',
returnbranch => 'homebranch',
}
}
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,
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 <<<
####
value => {
itemnumber => $item->itemnumber,
datearrived => undef,
+ datecancelled => undef
}
});
$item->damaged(0)->store;
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);
};
+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(*)