From 6dc17eeb8df23cf77f559dfa836631fa1c7718b5 Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Sat, 6 Feb 2016 03:59:31 +0000 Subject: [PATCH] Bug 15534 - Add the ability to prevent a patron from placing a hold on a record with available items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some libraries would like to prevent patrons from placing holds on items where there are other items available for the patron to check out. Test Plan: 1) Apply this patch 2) Run updatedatabase.pl 3) Browse to the circulation rules 4) Note the new option for "On shelf holds allowed" 5) Set the rule to "If all unavailable", set "item level holds" to allow 6) Find a patron/branch/itemtype applicable to this rule 7) Ensure at least one item on the record is available for the patron to check out 8) Attempt to place a hold for the item 9) Note you cannot place the hold 10) Check the available item out to another patron 11) Note you can now place a hold for the first patron Signed-off-by: Andreas Hedström Mace Works as intended! --- C4/Reserves.pm | 26 ++++- .../prog/en/modules/admin/smart-rules.tt | 12 +- .../Holds/DisallowHoldIfItemsAvailable.t | 120 ++++++++++++++++++++ 3 files changed, 155 insertions(+), 3 deletions(-) create mode 100755 t/db_dependent/Holds/DisallowHoldIfItemsAvailable.t diff --git a/C4/Reserves.pm b/C4/Reserves.pm index e0d616b..15192d3 100644 --- a/C4/Reserves.pm +++ b/C4/Reserves.pm @@ -41,6 +41,8 @@ use Koha::Database; use Koha::Hold; use Koha::Holds; use Koha::Libraries; +use Koha::Items; +use Koha::ItemTypes; use List::MoreUtils qw( firstidx any ); use Carp; @@ -1493,8 +1495,30 @@ sub IsAvailableForItemLevelRequest { $item->{withdrawn} || ($item->{damaged} && !C4::Context->preference('AllowHoldsOnDamagedItems')); + my $on_shelf_holds = _OnShelfHoldsAllowed($itype,$borrower->{categorycode},$item->{holdingbranch}); + + if ( $on_shelf_holds == 1 ) { + return 1; + } elsif ( $on_shelf_holds == 2 ) { + my @items = + Koha::Items->search( { biblionumber => $item->{biblionumber} } ); + + my $any_available = 0; + + foreach my $i (@items) { + $any_available = 1 + unless $i->itemlost + || $i->{notforloan} > 0 + || $i->withdrawn + || $i->onloan + || GetReserveStatus( $i->id ) eq "Waiting" + || ( $i->damaged + && !C4::Context->preference('AllowHoldsOnDamagedItems') ) + || Koha::ItemTypes->find( $i->effective_itemtype() )->notforloan; + } - return 1 if _OnShelfHoldsAllowed($itype,$borrower->{categorycode},$item->{holdingbranch}); + return $any_available ? 0 : 1; + } return $item->{onloan} || GetReserveStatus($item->{itemnumber}) eq "Waiting"; } diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt index c7e27ad..90d1a08 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt @@ -248,7 +248,14 @@ for="tobranch">Clone these rules to: Edit @@ -318,7 +325,8 @@ for="tobranch">Clone these rules to: - + + diff --git a/t/db_dependent/Holds/DisallowHoldIfItemsAvailable.t b/t/db_dependent/Holds/DisallowHoldIfItemsAvailable.t new file mode 100755 index 0000000..54a8b7f --- /dev/null +++ b/t/db_dependent/Holds/DisallowHoldIfItemsAvailable.t @@ -0,0 +1,120 @@ +#!/usr/bin/perl + +use Modern::Perl; + +use C4::Context; +use C4::Items; +use C4::Circulation; +use Koha::IssuingRule; + +use Test::More tests => 4; + +use t::lib::TestBuilder; + +BEGIN { + use FindBin; + use lib $FindBin::Bin; + use_ok('C4::Reserves'); +} + +my $schema = Koha::Database->schema; +$schema->storage->txn_begin; +my $dbh = C4::Context->dbh; + +my $builder = t::lib::TestBuilder->new; + +my $library1 = $builder->build({ + source => 'Branch', +}); + +# Now, set a userenv +C4::Context->_new_userenv('xxx'); +C4::Context->set_userenv(0,0,0,'firstname','surname', $library1->{branchcode}, 'Midway Public Library', '', '', ''); + +my $bib_title = "Test Title"; + +my $borrower1 = $builder->build({ + source => 'Borrower', + value => { + branchcode => $library1->{branchcode}, + dateexpiry => '3000-01-01', + } +}); + +my $borrower2 = $builder->build({ + source => 'Borrower', + value => { + branchcode => $library1->{branchcode}, + dateexpiry => '3000-01-01', + } +}); + +# Test hold_fulfillment_policy +my ( $itemtype ) = @{ $dbh->selectrow_arrayref("SELECT itemtype FROM itemtypes LIMIT 1") }; +my $borrowernumber1 = $borrower1->{borrowernumber}; +my $borrowernumber2 = $borrower2->{borrowernumber}; +my $library_A = $library1->{branchcode}; + +$dbh->do("INSERT INTO biblio (frameworkcode, author, title, datecreated) VALUES ('', 'Koha test', '$bib_title', '2011-02-01')"); + +my $biblionumber = $dbh->selectrow_array("SELECT biblionumber FROM biblio WHERE title = '$bib_title'") + or BAIL_OUT("Cannot find newly created biblio record"); + +$dbh->do("INSERT INTO biblioitems (biblionumber, marcxml, itemtype) VALUES ($biblionumber, '', '$itemtype')"); + +my $biblioitemnumber = + $dbh->selectrow_array("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber = $biblionumber") + or BAIL_OUT("Cannot find newly created biblioitems record"); + +$dbh->do(" + INSERT INTO items (barcode, biblionumber, biblioitemnumber, homebranch, holdingbranch, notforloan, damaged, itemlost, withdrawn, onloan, itype) + VALUES ('AllowHoldIf1', $biblionumber, $biblioitemnumber, '$library_A', '$library_A', 0, 0, 0, 0, NULL, '$itemtype') +"); + +my $itemnumber1 = + $dbh->selectrow_array("SELECT itemnumber FROM items WHERE biblionumber = $biblionumber") + or BAIL_OUT("Cannot find newly created item"); + +my $item1 = GetItem( $itemnumber1 ); + +$dbh->do(" + INSERT INTO items (barcode, biblionumber, biblioitemnumber, homebranch, holdingbranch, notforloan, damaged, itemlost, withdrawn, onloan, itype) + VALUES ('AllowHoldIf2', $biblionumber, $biblioitemnumber, '$library_A', '$library_A', 0, 0, 0, 0, NULL, '$itemtype') +"); + +my $itemnumber2 = + $dbh->selectrow_array("SELECT itemnumber FROM items WHERE biblionumber = $biblionumber ORDER BY itemnumber DESC") + or BAIL_OUT("Cannot find newly created item"); + +my $item2 = GetItem( $itemnumber2 ); + +$dbh->do("DELETE FROM issuingrules"); +my $rule = Koha::IssuingRule->new( + { + categorycode => '*', + itemtype => '*', + branchcode => '*', + maxissueqty => 99, + issuelength => 7, + lengthunit => 8, + reservesallowed => 99, + onshelfholds => 2, + } +); +$rule->store(); + +my $is = IsAvailableForItemLevelRequest( $item1, $borrower1); +is( $is, 0, "Item cannot be held, 2 items available" ); + +AddIssue( $borrower2, $item1->{barcode} ); + +$is = IsAvailableForItemLevelRequest( $item1, $borrower1); +is( $is, 0, "Item cannot be held, 1 item available" ); + +AddIssue( $borrower2, $item2->{barcode} ); + +$is = IsAvailableForItemLevelRequest( $item1, $borrower1); +is( $is, 1, "Item can be held, no items available" ); + +# Cleanup +$schema->storage->txn_rollback; -- 1.7.10.4