+
+ for ( 1 .. 3 ) {
+ AddReserve(
+ {
+ branchcode => $branch_1,
+ borrowernumber => $borrowernumbers[0],
+ biblionumber => $biblio->biblionumber,
+ priority => 1,
+ }
+ );
+ }
+
+ my $count =
+ Koha::Holds->search( { borrowernumber => $borrowernumbers[0] } )->count();
+ is( $count, 3, 'Patron now has 3 holds' );
+
+ my $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
+ is( $ret->{status}, 'OK', 'Patron can place hold with no borrower circ rules' );
+
+ my $rule_all = Koha::CirculationRules->set_rule(
+ {
+ categorycode => $category->{categorycode},
+ branchcode => undef,
+ rule_name => 'max_holds',
+ rule_value => 3,
+ }
+ );
+
+ my $rule_branch = Koha::CirculationRules->set_rule(
+ {
+ branchcode => $branch_1,
+ categorycode => $category->{categorycode},
+ rule_name => 'max_holds',
+ rule_value => 5,
+ }
+ );
+
+ $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' });
+ $itemnumber = $builder->build_sample_item({ library => $branch_1, biblionumber => $biblio->biblionumber})->itemnumber;
+ #Add a default rule to allow some holds
+
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => undef,
+ categorycode => undef,
+ itemtype => undef,
+ rules => {
+ reservesallowed => 25,
+ holds_per_record => 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)->{status},
+ '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 => 10;
+
+ $schema->storage->txn_begin;
+
+ 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 $itemnumber_1 = $builder->build_sample_item({ library => $library->branchcode, biblionumber => $biblio_1->biblionumber})->itemnumber;
+ my $biblio_2 = $builder->build_sample_biblio({ itemtype => $itemtype->itemtype });
+ my $itemnumber_2 = $builder->build_sample_item({ library => $library->branchcode, biblionumber => $biblio_2->biblionumber})->itemnumber;
+ my $biblio_3 = $builder->build_sample_biblio({ itemtype => $itemtype->itemtype });
+ my $itemnumber_3 = $builder->build_sample_item({ library => $library->branchcode, biblionumber => $biblio_3->biblionumber})->itemnumber;
+
+ Koha::CirculationRules->set_rules(
+ {
+ categorycode => '*',
+ branchcode => '*',
+ itemtype => $itemtype->itemtype,
+ rules => {
+ reservesallowed => 1,
+ holds_per_record => 99,
+ holds_per_day => 2
+ }
+ }
+ );
+
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $itemnumber_1 ),
+ { status => 'OK' },
+ 'Patron can reserve item with hold limit of 1, no holds placed'
+ );
+
+ AddReserve(
+ {
+ branchcode => $library->branchcode,
+ borrowernumber => $patron->borrowernumber,
+ biblionumber => $biblio_1->biblionumber,
+ priority => 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
+ Koha::CirculationRules->set_rule(
+ {
+
+ categorycode => '*',
+ branchcode => '*',
+ itemtype => $itemtype->itemtype,
+ rule_name => 'reservesallowed',
+ rule_value => 3,
+ }
+ );
+
+ 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(
+ {
+ branchcode => $library->branchcode,
+ borrowernumber => $patron->borrowernumber,
+ biblionumber => $biblio_2->biblionumber,
+ priority => 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
+ Koha::CirculationRules->set_rule(
+ {
+
+ categorycode => '*',
+ branchcode => '*',
+ itemtype => $itemtype->itemtype,
+ rule_name => 'holds_per_day',
+ rule_value => 0,
+ }
+ );
+
+
+ # 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)'
+ );
+
+ Koha::CirculationRules->set_rule(
+ {
+
+ categorycode => '*',
+ branchcode => '*',
+ itemtype => $itemtype->itemtype,
+ rule_name => 'holds_per_day',
+ rule_value => undef,
+ }
+ );
+
+ 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(
+ {
+ branchcode => $library->branchcode,
+ borrowernumber => $patron->borrowernumber,
+ biblionumber => $biblio_1->biblionumber,
+ priority => 1,
+ }
+ );
+ AddReserve(
+ {
+ branchcode => $library->branchcode,
+ borrowernumber => $patron->borrowernumber,
+ biblionumber => $biblio_2->biblionumber,
+ priority => 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(
+ {
+ branchcode => $library->branchcode,
+ borrowernumber => $patron->borrowernumber,
+ biblionumber => $biblio_3->biblionumber,
+ priority => 1,
+ }
+ );
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $itemnumber_3 ),
+ { status => 'tooManyReserves', limit => 3 },
+ 'Unlimited daily holds, but reached reservesallowed'
+ );
+ #results should be the same for both ReservesControlBranch settings
+ t::lib::Mocks::mock_preference('ReservesControlBranch', 'ItemHomeLibrary');
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $itemnumber_3 ),
+ { status => 'tooManyReserves', limit => 3 },
+ 'Unlimited daily holds, but reached reservesallowed'
+ );
+
+ $schema->storage->txn_rollback;
+};
+
+subtest 'CanItemBeReserved / branch_not_in_hold_group' => sub {
+ plan tests => 9;
+
+ $schema->storage->txn_begin;
+
+ Koha::CirculationRules->set_rule(
+ {
+ branchcode => undef,
+ categorycode => undef,
+ itemtype => undef,
+ rule_name => 'reservesallowed',
+ rule_value => 25,
+ }
+ );
+
+ # Create item types
+ my $itemtype1 = $builder->build_object( { class => 'Koha::ItemTypes' } );
+ my $itemtype2 = $builder->build_object( { class => 'Koha::ItemTypes' } );
+
+ # Create libraries
+ my $library1 = $builder->build_object( { class => 'Koha::Libraries' } );
+ my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
+ my $library3 = $builder->build_object( { class => 'Koha::Libraries' } );
+
+ # Create library groups hierarchy
+ my $rootgroup = $builder->build_object( { class => 'Koha::Library::Groups', value => {ft_local_hold_group => 1} } );
+ my $group1 = $builder->build_object( { class => 'Koha::Library::Groups', value => {parent_id => $rootgroup->id, branchcode => $library1->branchcode}} );
+ my $group2 = $builder->build_object( { class => 'Koha::Library::Groups', value => {parent_id => $rootgroup->id, branchcode => $library2->branchcode} } );
+
+ # Create 2 patrons
+ my $patron1 = $builder->build_object( { class => 'Koha::Patrons', value => {branchcode => $library1->branchcode} } );
+ my $patron3 = $builder->build_object( { class => 'Koha::Patrons', value => {branchcode => $library3->branchcode} } );
+
+ # Create 3 biblios with items
+ my $biblio_1 = $builder->build_sample_biblio({ itemtype => $itemtype1->itemtype });
+ my $item_1 = $builder->build_sample_item(
+ {
+ biblionumber => $biblio_1->biblionumber,
+ library => $library1->branchcode
+ }
+ );
+ my $biblio_2 = $builder->build_sample_biblio({ itemtype => $itemtype2->itemtype });
+ my $item_2 = $builder->build_sample_item(
+ {
+ biblionumber => $biblio_2->biblionumber,
+ library => $library2->branchcode
+ }
+ );
+ my $itemnumber_2 = $item_2->itemnumber;
+ my $biblio_3 = $builder->build_sample_biblio({ itemtype => $itemtype1->itemtype });
+ my $item_3 = $builder->build_sample_item(
+ {
+ biblionumber => $biblio_3->biblionumber,
+ library => $library1->branchcode
+ }
+ );
+
+ # Test 1: Patron 3 can place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2 ),
+ { status => 'OK' },
+ 'Patron can place hold if no circ_rules where defined'
+ );
+
+ # Insert default circ rule of holds allowed only from local hold group for all libraries
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => undef,
+ itemtype => undef,
+ rules => {
+ holdallowed => 'from_local_hold_group',
+ hold_fulfillment_policy => 'any',
+ returnbranch => 'any'
+ }
+ }
+ );
+
+ # Test 2: Patron 1 can place hold
+ is_deeply(
+ CanItemBeReserved( $patron1->borrowernumber, $itemnumber_2 ),
+ { status => 'OK' },
+ 'Patron can place hold because patron\'s home library is part of hold group'
+ );
+
+ # Test 3: Patron 3 cannot place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2 ),
+ { status => 'branchNotInHoldGroup' },
+ 'Patron cannot place hold because patron\'s home library is not part of hold group'
+ );
+
+ # Insert default circ rule to "any" for library 2
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => $library2->branchcode,
+ itemtype => undef,
+ rules => {
+ holdallowed => 'from_any_library',
+ hold_fulfillment_policy => 'any',
+ returnbranch => 'any'
+ }
+ }
+ );
+
+ # Test 4: Patron 3 can place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2 ),
+ { status => 'OK' },
+ 'Patron can place hold if holdallowed is set to "any" for library 2'
+ );
+
+ # Update default circ rule to "hold group" for library 2
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => $library2->branchcode,
+ itemtype => undef,
+ rules => {
+ holdallowed => 'from_local_hold_group',
+ hold_fulfillment_policy => 'any',
+ returnbranch => 'any'
+ }
+ }
+ );
+
+ # Test 5: Patron 3 cannot place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2 ),
+ { status => 'branchNotInHoldGroup' },
+ 'Patron cannot place hold if holdallowed is set to "hold group" for library 2'
+ );
+
+ # Insert default item rule to "any" for itemtype 2
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => $library2->branchcode,
+ itemtype => $itemtype2->itemtype,
+ rules => {
+ holdallowed => 'from_any_library',
+ hold_fulfillment_policy => 'any',
+ returnbranch => 'any'
+ }
+ }
+ );
+
+ # Test 6: Patron 3 can place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2 ),
+ { status => 'OK' },
+ 'Patron can place hold if holdallowed is set to "any" for itemtype 2'
+ );
+
+ # Update default item rule to "hold group" for itemtype 2
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => $library2->branchcode,
+ itemtype => $itemtype2->itemtype,
+ rules => {
+ holdallowed => 'from_local_hold_group',
+ hold_fulfillment_policy => 'any',
+ returnbranch => 'any'
+ }
+ }
+ );
+
+ # Test 7: Patron 3 cannot place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2 ),
+ { status => 'branchNotInHoldGroup' },
+ 'Patron cannot place hold if holdallowed is set to "hold group" for itemtype 2'
+ );
+
+ # Insert branch item rule to "any" for itemtype 2 and library 2
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => $library2->branchcode,
+ itemtype => $itemtype2->itemtype,
+ rules => {
+ holdallowed => 'from_any_library',
+ hold_fulfillment_policy => 'any',
+ returnbranch => 'any'
+ }
+ }
+ );
+
+ # Test 8: Patron 3 can place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2 ),
+ { status => 'OK' },
+ 'Patron can place hold if holdallowed is set to "any" for itemtype 2 and library 2'
+ );
+
+ # Update branch item rule to "hold group" for itemtype 2 and library 2
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => $library2->branchcode,
+ itemtype => $itemtype2->itemtype,
+ rules => {
+ holdallowed => 'from_local_hold_group',
+ hold_fulfillment_policy => 'any',
+ returnbranch => 'any'
+ }
+ }
+ );
+
+ # Test 9: Patron 3 cannot place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2 ),
+ { status => 'branchNotInHoldGroup' },
+ 'Patron cannot place hold if holdallowed is set to "hold group" for itemtype 2 and library 2'
+ );
+
+ $schema->storage->txn_rollback;
+
+};
+
+subtest 'CanItemBeReserved / pickup_not_in_hold_group' => sub {
+ plan tests => 9;
+
+ $schema->storage->txn_begin;
+ Koha::CirculationRules->set_rule(
+ {
+ branchcode => undef,
+ categorycode => undef,
+ itemtype => undef,
+ rule_name => 'reservesallowed',
+ rule_value => 25,
+ }
+ );
+
+ # Create item types
+ my $itemtype1 = $builder->build_object( { class => 'Koha::ItemTypes' } );
+ my $itemtype2 = $builder->build_object( { class => 'Koha::ItemTypes' } );
+
+ # Create libraries
+ my $library1 = $builder->build_object( { class => 'Koha::Libraries', value => {pickup_location => 1} } );
+ my $library2 = $builder->build_object( { class => 'Koha::Libraries', value => {pickup_location => 1} } );
+ my $library3 = $builder->build_object( { class => 'Koha::Libraries', value => {pickup_location => 1} } );
+
+ # Create library groups hierarchy
+ my $rootgroup = $builder->build_object( { class => 'Koha::Library::Groups', value => {ft_local_hold_group => 1} } );
+ my $group1 = $builder->build_object( { class => 'Koha::Library::Groups', value => {parent_id => $rootgroup->id, branchcode => $library1->branchcode}} );
+ my $group2 = $builder->build_object( { class => 'Koha::Library::Groups', value => {parent_id => $rootgroup->id, branchcode => $library2->branchcode} } );
+
+ # Create 2 patrons
+ my $patron1 = $builder->build_object( { class => 'Koha::Patrons', value => {branchcode => $library1->branchcode} } );
+ my $patron3 = $builder->build_object( { class => 'Koha::Patrons', value => {branchcode => $library3->branchcode} } );
+
+ # Create 3 biblios with items
+ my $biblio_1 = $builder->build_sample_biblio({ itemtype => $itemtype1->itemtype });
+ my $item_1 = $builder->build_sample_item(
+ {
+ biblionumber => $biblio_1->biblionumber,
+ library => $library1->branchcode
+ }
+ );
+ my $biblio_2 = $builder->build_sample_biblio({ itemtype => $itemtype2->itemtype });
+ my $item_2 = $builder->build_sample_item(
+ {
+ biblionumber => $biblio_2->biblionumber,
+ library => $library2->branchcode
+ }
+ );
+ my $itemnumber_2 = $item_2->itemnumber;
+ my $biblio_3 = $builder->build_sample_biblio({ itemtype => $itemtype1->itemtype });
+ my $item_3 = $builder->build_sample_item(
+ {
+ biblionumber => $biblio_3->biblionumber,
+ library => $library1->branchcode
+ }
+ );
+
+ # Test 1: Patron 3 can place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2, $library3->branchcode ),
+ { status => 'OK' },
+ 'Patron can place hold if no circ_rules where defined'
+ );
+
+ # Insert default circ rule of holds allowed only from local hold group for all libraries
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => undef,
+ itemtype => undef,
+ rules => {
+ holdallowed => 'from_any_library',
+ hold_fulfillment_policy => 'holdgroup',
+ returnbranch => 'any'
+ }
+ }
+ );
+
+ # Test 2: Patron 1 can place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2, $library1->branchcode ),
+ { status => 'OK' },
+ 'Patron can place hold because pickup location is part of hold group'
+ );
+
+ # Test 3: Patron 3 cannot place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2, $library3->branchcode ),
+ { status => 'pickupNotInHoldGroup' },
+ 'Patron cannot place hold because pickup location is not part of hold group'
+ );
+
+ # Insert default circ rule to "any" for library 2
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => $library2->branchcode,
+ itemtype => undef,
+ rules => {
+ holdallowed => 'from_any_library',
+ hold_fulfillment_policy => 'any',
+ returnbranch => 'any'
+ }
+ }
+ );
+
+ # Test 4: Patron 3 can place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2, $library3->branchcode ),
+ { status => 'OK' },
+ 'Patron can place hold if default_branch_circ_rules is set to "any" for library 2'
+ );
+
+ # Update default circ rule to "hold group" for library 2
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => $library2->branchcode,
+ itemtype => undef,
+ rules => {
+ holdallowed => 'from_any_library',
+ hold_fulfillment_policy => 'holdgroup',
+ returnbranch => 'any'
+ }
+ }
+ );
+
+ # Test 5: Patron 3 cannot place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2, $library3->branchcode ),
+ { status => 'pickupNotInHoldGroup' },
+ 'Patron cannot place hold if hold_fulfillment_policy is set to "hold group" for library 2'
+ );
+
+ # Insert default item rule to "any" for itemtype 2
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => $library2->branchcode,
+ itemtype => $itemtype2->itemtype,
+ rules => {
+ holdallowed => 'from_any_library',
+ hold_fulfillment_policy => 'any',
+ returnbranch => 'any'
+ }
+ }
+ );
+
+ # Test 6: Patron 3 can place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2, $library3->branchcode ),
+ { status => 'OK' },
+ 'Patron can place hold if hold_fulfillment_policy is set to "any" for itemtype 2'
+ );
+
+ # Update default item rule to "hold group" for itemtype 2
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => $library2->branchcode,
+ itemtype => $itemtype2->itemtype,
+ rules => {
+ holdallowed => 'from_any_library',
+ hold_fulfillment_policy => 'holdgroup',
+ returnbranch => 'any'
+ }
+ }
+ );
+
+ # Test 7: Patron 3 cannot place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2, $library3->branchcode ),
+ { status => 'pickupNotInHoldGroup' },
+ 'Patron cannot place hold if hold_fulfillment_policy is set to "hold group" for itemtype 2'
+ );
+
+ # Insert branch item rule to "any" for itemtype 2 and library 2
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => $library2->branchcode,
+ itemtype => $itemtype2->itemtype,
+ rules => {
+ holdallowed => 'from_any_library',
+ hold_fulfillment_policy => 'any',
+ returnbranch => 'any'
+ }
+ }
+ );
+
+ # Test 8: Patron 3 can place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2, $library3->branchcode ),
+ { status => 'OK' },
+ 'Patron can place hold if hold_fulfillment_policy is set to "any" for itemtype 2 and library 2'
+ );
+
+ # Update branch item rule to "hold group" for itemtype 2 and library 2
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => $library2->branchcode,
+ itemtype => $itemtype2->itemtype,
+ rules => {
+ holdallowed => 'from_any_library',
+ hold_fulfillment_policy => 'holdgroup',
+ returnbranch => 'any'
+ }
+ }
+ );
+
+ # Test 9: Patron 3 cannot place hold
+ is_deeply(
+ CanItemBeReserved( $patron3->borrowernumber, $itemnumber_2, $library3->branchcode ),
+ { status => 'pickupNotInHoldGroup' },
+ 'Patron cannot place hold if hold_fulfillment_policy is set to "hold group" for itemtype 2 and library 2'
+ );
+
+ $schema->storage->txn_rollback;
+};
+
+subtest 'non priority holds' => sub {
+
+ plan tests => 6;
+
+ $schema->storage->txn_begin;
+
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => undef,
+ categorycode => undef,
+ itemtype => undef,
+ rules => {
+ renewalsallowed => 5,
+ reservesallowed => 5,
+ }
+ }
+ );
+
+ my $item = $builder->build_sample_item;
+
+ my $patron1 = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { branchcode => $item->homebranch }
+ }
+ );
+ my $patron2 = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { branchcode => $item->homebranch }
+ }
+ );
+
+ Koha::Checkout->new(
+ {
+ borrowernumber => $patron1->borrowernumber,
+ itemnumber => $item->itemnumber,
+ branchcode => $item->homebranch
+ }
+ )->store;
+
+ my $hid = AddReserve(
+ {
+ branchcode => $item->homebranch,
+ borrowernumber => $patron2->borrowernumber,
+ biblionumber => $item->biblionumber,
+ priority => 1,
+ itemnumber => $item->itemnumber,
+ }
+ );
+
+ my ( $ok, $err ) =
+ CanBookBeRenewed( $patron1->borrowernumber, $item->itemnumber );
+
+ ok( !$ok, 'Cannot renew' );
+ is( $err, 'on_reserve', 'Item is on hold' );
+
+ my $hold = Koha::Holds->find($hid);
+ $hold->non_priority(1)->store;
+
+ ( $ok, $err ) =
+ CanBookBeRenewed( $patron1->borrowernumber, $item->itemnumber );
+
+ ok( $ok, 'Can renew' );
+ is( $err, undef, 'Item is on non priority hold' );
+
+ my $patron3 = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { branchcode => $item->homebranch }
+ }
+ );
+
+ # Add second hold with non_priority = 0
+ AddReserve(
+ {
+ branchcode => $item->homebranch,
+ borrowernumber => $patron3->borrowernumber,
+ biblionumber => $item->biblionumber,
+ priority => 2,
+ itemnumber => $item->itemnumber,
+ }
+ );
+
+ ( $ok, $err ) =
+ CanBookBeRenewed( $patron1->borrowernumber, $item->itemnumber );
+
+ ok( !$ok, 'Cannot renew' );
+ is( $err, 'on_reserve', 'Item is on hold' );
+
+ $schema->storage->txn_rollback;
+
+};
+
+subtest 'CanItemBeReserved rule precedence tests' => sub {
+
+ plan tests => 3;
+
+ t::lib::Mocks::mock_preference('ReservesControlBranch', 'ItemHomeLibrary');
+ $schema->storage->txn_begin;
+ my $library = $builder->build_object( { class => 'Koha::Libraries', value => {
+ pickup_location => 1,
+ }});
+ my $item = $builder->build_sample_item({
+ homebranch => $library->branchcode,
+ holdingbranch => $library->branchcode
+ });
+ my $item2 = $builder->build_sample_item({
+ homebranch => $library->branchcode,
+ holdingbranch => $library->branchcode,
+ itype => $item->itype
+ });
+ my $patron = $builder->build_object({ class => 'Koha::Patrons', value => {
+ branchcode => $library->branchcode
+ }});
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => undef,
+ categorycode => $patron->categorycode,
+ itemtype => $item->itype,
+ rules => {
+ reservesallowed => 1,
+ }
+ }
+ );
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $item->itemnumber, $library->branchcode ),
+ { status => 'OK' },
+ 'Patron of specified category can place 1 hold on specified itemtype'
+ );
+ my $hold = $builder->build_object({ class => 'Koha::Holds', value => {
+ biblionumber => $item2->biblionumber,
+ itemnumber => $item2->itemnumber,
+ found => undef,
+ priority => 1,
+ branchcode => $library->branchcode,
+ borrowernumber => $patron->borrowernumber,
+ }});
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $item->itemnumber, $library->branchcode ),
+ { status => 'tooManyReserves', limit => 1 },
+ 'Patron of specified category can place 1 hold on specified itemtype, cannot place a second'
+ );
+ Koha::CirculationRules->set_rules(
+ {
+ branchcode => $library->branchcode,
+ categorycode => undef,
+ itemtype => undef,
+ rules => {
+ reservesallowed => 2,
+ }
+ }
+ );
+ is_deeply(
+ CanItemBeReserved( $patron->borrowernumber, $item->itemnumber, $library->branchcode ),
+ { status => 'OK' },
+ 'Patron of specified category can place 1 hold on specified itemtype if library rule for all types and categories set to 2'
+ );
+
+ $schema->storage->txn_rollback;
+
+};