From 1f55d38918c23c7e268b2bca4c0edb5042dc3eea Mon Sep 17 00:00:00 2001 From: Tomas Cohen Arazi Date: Wed, 1 Sep 2021 18:00:59 -0300 Subject: [PATCH] Bug 28529: Make biblio-level hold itemtype count against max rules The current situation is that biblio-level holds can be assigned an item type, so they can only be fulfilled by items matching that specified item type (be it item-level itype or the fallback to biblio-level). But there's the situation in which max holds limits for a specific item type can be overridden by using biblio-level holds with item type selection (AllowHoldItemTypeSelection) enabled. To test: 1. Have a patron of category 'Staff' (S) 2. Have 3 records with items with the 'BK' item type, and maybe others 3. Enable AllowHoldItemTypeSelection 4. Set a limit of 2 max holds for that category+item type 5. In the OPAC. Place bibio-level holds, with item type contraint to 'BK' on those 3 records => FAIL: You can place the 3 holds 6. Cancel the holds 7. Apply this patch and restart all 8. Repeat 5 => SUCCESS: You can only place 2 holds 9. Run: $ kshell t/db_dependent/Reserves.t => SUCCESS: Tests pass! 10. Sign off :-D Signed-off-by: David Nind Signed-off-by: Martin Renvoize Signed-off-by: Tomas Cohen Arazi (cherry picked from commit 81bdce5c4390e1914ff6cfb2adb469027904f659) Signed-off-by: Lucas Gass --- C4/Reserves.pm | 52 ++++++++++++++++--- .../bootstrap/en/modules/opac-reserve.tt | 6 +-- opac/opac-reserve.pl | 25 +++++++-- 3 files changed, 68 insertions(+), 15 deletions(-) diff --git a/C4/Reserves.pm b/C4/Reserves.pm index 5193a5454c..9beae19d62 100644 --- a/C4/Reserves.pm +++ b/C4/Reserves.pm @@ -344,6 +344,37 @@ sub CanBookBeReserved{ return { status =>'alreadypossession' }; } + if ( $params->{itemtype} ) { + + # biblio-level, item type-contrained + my $patron = Koha::Patrons->find($borrowernumber); + my $reservesallowed = Koha::CirculationRules->get_effective_rule( + { + itemtype => $params->{itemtype}, + categorycode => $patron->categorycode, + branchcode => $pickup_branchcode, + rule_name => 'reservesallowed', + } + )->rule_value; + + $reservesallowed = ( $reservesallowed eq '' ) ? undef : $reservesallowed; + + my $count = $patron->holds->search( + { + '-or' => [ + { 'me.itemtype' => $params->{itemtype} }, + { 'item.itype' => $params->{itemtype} } + ] + }, + { + join => ['item'] + } + )->count; + + return { status => '' } + if defined $reservesallowed and $reservesallowed < $count + 1; + } + my $items; #get items linked via host records my @hostitemnumbers = get_hostitemnumbers_of($biblionumber); @@ -524,16 +555,25 @@ sub CanItemBeReserved { # If using item-level itypes, fall back to the record # level itemtype if the hold has no associated item - $querycount .= - C4::Context->preference('item-level_itypes') - ? " AND COALESCE( items.itype, biblioitems.itemtype ) = ?" - : " AND biblioitems.itemtype = ?" - if defined $ruleitemtype; + if ( defined $ruleitemtype ) { + if ( C4::Context->preference('item-level_itypes') ) { + $querycount .= q{ + AND ( COALESCE( items.itype, biblioitems.itemtype ) = ? + OR reserves.itemtype = ? ) + }; + } + else { + $querycount .= q{ + AND ( biblioitems.itemtype = ? + OR reserves.itemtype = ? ) + }; + } + } my $sthcount = $dbh->prepare($querycount); if ( defined $ruleitemtype ) { - $sthcount->execute( $patron->borrowernumber, $reserves_control_branch, $ruleitemtype ); + $sthcount->execute( $patron->borrowernumber, $reserves_control_branch, $ruleitemtype, $ruleitemtype ); } else { $sthcount->execute( $patron->borrowernumber, $reserves_control_branch ); diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-reserve.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-reserve.tt index 27b294fac2..4e8b2d1ba2 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-reserve.tt +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-reserve.tt @@ -295,15 +295,11 @@ [% IF Koha.Preference('AllowHoldItemTypeSelection') %] - [% itemtypes = [] %] - [% FOREACH item IN bibitemloo.itemLoop %] - [% itemtypes.push( item.itype ) %] - [%- END %]
  • diff --git a/opac/opac-reserve.pl b/opac/opac-reserve.pl index b8ffca4e44..e62a3ba9af 100755 --- a/opac/opac-reserve.pl +++ b/opac/opac-reserve.pl @@ -274,13 +274,17 @@ if ( $query->param('place_reserve') ) { my $patron_expiration_date = $query->param("expiration_date_$biblioNum"); + my $itemtype = $query->param('itemtype') || undef; + $itemtype = undef if $itemNum; + my $rank = $biblioData->{rank}; my $patron = Koha::Patrons->find( $borrowernumber ); if ( $item ) { $canreserve = 1 if CanItemBeReserved( $patron, $item, $branch )->{status} eq 'OK'; } else { - $canreserve = 1 if CanBookBeReserved( $borrowernumber, $biblioNum, $branch )->{status} eq 'OK'; + $canreserve = 1 + if CanBookBeReserved( $borrowernumber, $biblioNum, $branch, { itemtype => $itemtype } )->{status} eq 'OK'; # Inserts a null into the 'itemnumber' field of 'reserves' table. $itemNum = undef; @@ -302,9 +306,6 @@ if ( $query->param('place_reserve') ) { } } - my $itemtype = $query->param('itemtype') || undef; - $itemtype = undef if $itemNum; - # Here we actually do the reserveration. Stage 3. if ($canreserve) { my $reserve_id = AddReserve( @@ -633,6 +634,22 @@ foreach my $biblioNum (@biblionumbers) { $biblioLoopIter{holdable} &&= $status eq 'OK'; $biblioLoopIter{already_patron_possession} = $status eq 'alreadypossession'; + if ( $biblioLoopIter{holdable} and C4::Context->preference('AllowHoldItemTypeSelection') ) { + # build the allowed item types loop + my $rs = $biblio->items->search( + undef, + { select => [ { distinct => 'itype' } ], + as => 'item_type' + } + ); + + my @item_types = + grep { CanBookBeReserved( $borrowernumber, $biblioNum, $branch, { itemtype => $_ } )->{status} eq 'OK' } + $rs->get_column('item_type'); + + $biblioLoopIter{allowed_item_types} = \@item_types; + } + if ( $status eq 'recall' ) { $biblioLoopIter{recall} = 1; } -- 2.39.5