+
+ $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
+ is( $ret->{status}, 'OK', 'Patron can place hold with branch/category rule of 5, category rule of 3' );
+
+ $rule_branch->delete();
+
+ $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
+ is( $ret->{status}, 'tooManyReserves', 'Patron cannot place hold with only a category rule of 3' );
+
+ $rule_all->delete();
+ $rule_branch->rule_value(3);
+ $rule_branch->store();
+
+ $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
+ is( $ret->{status}, 'tooManyReserves', 'Patron cannot place hold with only a branch/category rule of 3' );
+
+ $rule_branch->rule_value(5);
+ $rule_branch->update();
+ $rule_branch->rule_value(5);
+ $rule_branch->store();
+
+ $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
+ is( $ret->{status}, 'OK', 'Patron can place hold with branch/category rule of 5, category rule of 5' );
+};
+
+subtest 'Pickup location availability tests' => sub {
+ plan tests => 4;
+
+ $biblio = $builder->build_sample_biblio({ itemtype => 'ONLY1' });
+ my ( $item_bibnum, $item_bibitemnum, $itemnumber )
+ = AddItem( { homebranch => $branch_1, holdingbranch => $branch_1 }, $biblio->biblionumber );
+ #Add a default rule to allow some holds
+ $dbh->do(
+ q{INSERT INTO issuingrules (categorycode, branchcode, itemtype, reservesallowed, holds_per_record)
+ VALUES (?, ?, ?, ?, ?)},
+ {},
+ '*', '*', '*', 25, 99
+ );
+ my $item = Koha::Items->find($itemnumber);
+ my $branch_to = $builder->build({ source => 'Branch' })->{ branchcode };
+ my $library = Koha::Libraries->find($branch_to);
+ $library->pickup_location('1')->store;
+ my $patron = $builder->build({ source => 'Borrower' })->{ borrowernumber };
+
+ t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1);
+ t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype');
+
+ $library->pickup_location('1')->store;
+ is(CanItemBeReserved($patron, $item->itemnumber, $branch_to)->{status},
+ 'OK', 'Library is a pickup location');
+
+ my $limit = Koha::Item::Transfer::Limit->new({
+ fromBranch => $item->holdingbranch,
+ toBranch => $branch_to,
+ itemtype => $item->effective_itemtype,
+ })->store;
+ is(CanItemBeReserved($patron, $item->itemnumber, $branch_to),
+ 'cannotBeTransferred', 'Item cannot be transferred');
+ $limit->delete;
+
+ $library->pickup_location('0')->store;
+ is(CanItemBeReserved($patron, $item->itemnumber, $branch_to)->{status},
+ 'libraryNotPickupLocation', 'Library is not a pickup location');
+ is(CanItemBeReserved($patron, $item->itemnumber, 'nonexistent')->{status},
+ 'libraryNotFound', 'Cannot set unknown library as pickup location');
+};
+
+$schema->storage->txn_rollback;
+
+subtest 'CanItemBeReserved / holds_per_day tests' => sub {
+
+ plan tests => 9;
+
+ $schema->storage->txn_begin;
+
+ Koha::Holds->search->delete;
+ $dbh->do('DELETE FROM issues');
+ Koha::Items->search->delete;
+ Koha::Biblios->search->delete;
+
+ my $itemtype = $builder->build_object( { class => 'Koha::ItemTypes' } );
+ my $library = $builder->build_object( { class => 'Koha::Libraries' } );
+ my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
+
+ # Create 3 biblios with items
+ my $biblio_1 = $builder->build_sample_biblio({ itemtype => $itemtype->itemtype });
+ my ( undef, undef, $itemnumber_1 ) = AddItem(
+ { homebranch => $library->branchcode,
+ holdingbranch => $library->branchcode
+ },
+ $biblio_1->biblionumber
+ );
+ my $biblio_2 = $builder->build_sample_biblio({ itemtype => $itemtype->itemtype });
+ my ( undef, undef, $itemnumber_2 ) = AddItem(
+ { homebranch => $library->branchcode,
+ holdingbranch => $library->branchcode
+ },
+ $biblio_2->biblionumber
+ );
+ my $biblio_3 = $builder->build_sample_biblio({ itemtype => $itemtype->itemtype });
+ my ( undef, undef, $itemnumber_3 ) = AddItem(
+ { homebranch => $library->branchcode,
+ holdingbranch => $library->branchcode
+ },
+ $biblio_3->biblionumber
+ );
+
+ Koha::IssuingRules->search->delete;
+ my $issuingrule = Koha::IssuingRule->new(
+ { categorycode => '*',
+ branchcode => '*',
+ itemtype => $itemtype->itemtype,
+ reservesallowed => 1,
+ holds_per_record => 99,
+ holds_per_day => 2
+ }
+ )->store;
+
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $itemnumber_1 ),
+ { status => 'OK' },
+ 'Patron can reserve item with hold limit of 1, no holds placed'
+ );
+
+ AddReserve( $library->branchcode, $patron->borrowernumber, $biblio_1->biblionumber, '', 1, );
+
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $itemnumber_1 ),
+ { status => 'tooManyReserves', limit => 1 },
+ 'Patron cannot reserve item with hold limit of 1, 1 bib level hold placed'
+ );
+
+ # Raise reservesallowed to avoid tooManyReserves from it
+ $issuingrule->set( { reservesallowed => 3 } )->store;
+
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $itemnumber_2 ),
+ { status => 'OK' },
+ 'Patron can reserve item with 2 reserves daily cap'
+ );
+
+ # Add a second reserve
+ my $res_id = AddReserve( $library->branchcode, $patron->borrowernumber, $biblio_2->biblionumber, '', 1, );
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $itemnumber_2 ),
+ { status => 'tooManyReservesToday', limit => 2 },
+ 'Patron cannot a third item with 2 reserves daily cap'
+ );
+
+ # Update last hold so reservedate is in the past, so 2 holds, but different day
+ $hold = Koha::Holds->find($res_id);
+ my $yesterday = dt_from_string() - DateTime::Duration->new( days => 1 );
+ $hold->reservedate($yesterday)->store;
+
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $itemnumber_2 ),
+ { status => 'OK' },
+ 'Patron can reserve item with 2 bib level hold placed on different days, 2 reserves daily cap'
+ );
+
+ # Set holds_per_day to 0
+ $issuingrule->set( { holds_per_day => 0 } )->store;
+
+ # Delete existing holds
+ Koha::Holds->search->delete;
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $itemnumber_2 ),
+ { status => 'tooManyReservesToday', limit => 0 },
+ 'Patron cannot reserve if holds_per_day is 0 (i.e. 0 is 0)'
+ );
+
+ $issuingrule->set( { holds_per_day => undef } )->store;
+ Koha::Holds->search->delete;
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $itemnumber_2 ),
+ { status => 'OK' },
+ 'Patron can reserve if holds_per_day is undef (i.e. undef is unlimited daily cap)'
+ );
+ AddReserve( $library->branchcode, $patron->borrowernumber, $biblio_1->biblionumber, '', 1, );
+ AddReserve( $library->branchcode, $patron->borrowernumber, $biblio_2->biblionumber, '', 1, );
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $itemnumber_3 ),
+ { status => 'OK' },
+ 'Patron can reserve if holds_per_day is undef (i.e. undef is unlimited daily cap)'
+ );
+ AddReserve( $library->branchcode, $patron->borrowernumber, $biblio_3->biblionumber, '', 1, );
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $itemnumber_3 ),
+ { status => 'tooManyReserves', limit => 3 },
+ 'Unlimited daily holds, but reached reservesallowed'
+ );
+
+ $schema->storage->txn_rollback;
+};