X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;ds=sidebyside;f=C4%2FReserves.pm;h=e347295da9ec807d211ee3a500430fda3e2b6c25;hb=17003e56df810bf33ada03d49b02618ec41b6eee;hp=8a850e65efb00e0a6405cb263d907808e1736369;hpb=eddd6c218135180cbff697803ff0555a86fd706e;p=koha.git diff --git a/C4/Reserves.pm b/C4/Reserves.pm index 8a850e65ef..e347295da9 100644 --- a/C4/Reserves.pm +++ b/C4/Reserves.pm @@ -1,6 +1,3 @@ -# -*- tab-width: 8 -*- -# NOTE: This file uses standard 8-character tabs - package C4::Reserves; # Copyright 2000-2002 Katipo Communications @@ -18,25 +15,30 @@ package C4::Reserves; # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License along with -# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place, -# Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. use strict; -require Exporter; +#use warnings; FIXME - Bug 2505 use C4::Context; use C4::Biblio; +use C4::Members; +use C4::Items; use C4::Search; use C4::Circulation; use C4::Accounts; -our ($VERSION,@ISA,@EXPORT,@EXPORT_OK,%EXPORT_TAGS); - -my $library_name = C4::Context->preference("LibraryName"); +# for _koha_notify_reserve +use C4::Members::Messaging; +use C4::Members qw(); +use C4::Letters; +use C4::Branch qw( GetBranchDetail ); +use C4::Dates qw( format_date_in_iso ); +use List::MoreUtils qw( firstidx ); -# set the version for version checking -$VERSION = 3.00; +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); =head1 NAME @@ -82,43 +84,56 @@ C4::Reserves - Koha functions for dealing with reservation. =cut -@ISA = qw(Exporter); - -@EXPORT = qw( - &AddReserve +BEGIN { + # set the version for version checking + $VERSION = 3.01; + require Exporter; + @ISA = qw(Exporter); + @EXPORT = qw( + &AddReserve - &GetReservesFromItemnumber - &GetReservesFromBiblionumber - &GetReservesFromBorrowernumber - &GetReservesForBranch - &GetReservesToBranch - &GetReserveCount - &GetReserveFee - - &GetOtherReserves - - &ModReserveFill - &ModReserveAffect - &ModReserve - &ModReserveStatus - &ModReserveCancelAll - &ModReserveMinusPriority - - &CheckReserves - &CancelReserve -); + &GetReservesFromItemnumber + &GetReservesFromBiblionumber + &GetReservesFromBorrowernumber + &GetReservesForBranch + &GetReservesToBranch + &GetReserveCount + &GetReserveFee + &GetReserveInfo + &GetReserveStatus + + &GetOtherReserves + + &ModReserveFill + &ModReserveAffect + &ModReserve + &ModReserveStatus + &ModReserveCancelAll + &ModReserveMinusPriority + + &CheckReserves + &CanBookBeReserved + &CanItemBeReserved + &CancelReserve + &CancelExpiredReserves + &IsAvailableForItemLevelRequest + + &AlterPriority + &ToggleLowestPriority + ); +} =item AddReserve - AddReserve($branch,$borrowernumber,$biblionumber,$constraint,$bibitems,$priority,$notes,$title,$checkitem,$found) + AddReserve($branch,$borrowernumber,$biblionumber,$constraint,$bibitems,$priority,$resdate,$expdate,$notes,$title,$checkitem,$found) =cut sub AddReserve { my ( $branch, $borrowernumber, $biblionumber, - $constraint, $bibitems, $priority, $notes, + $constraint, $bibitems, $priority, $resdate, $expdate, $notes, $title, $checkitem, $found ) = @_; my $fee = @@ -126,9 +141,17 @@ sub AddReserve { $bibitems ); my $dbh = C4::Context->dbh; my $const = lc substr( $constraint, 0, 1 ); - my @datearr = localtime(time); - my $resdate = - ( 1900 + $datearr[5] ) . "-" . ( $datearr[4] + 1 ) . "-" . $datearr[3]; + $resdate = format_date_in_iso( $resdate ) if ( $resdate ); + $resdate = C4::Dates->today( 'iso' ) unless ( $resdate ); + if ($expdate) { + $expdate = format_date_in_iso( $expdate ); + } else { + undef $expdate; # make reserves.expirationdate default to null rather than '0000-00-00' + } + if ( C4::Context->preference( 'AllowHoldDateInFuture' ) ) { + # Make room in reserves for this before those of a later reserve date + $priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority ); + } my $waitingdate; # If the reserv had the waiting status, we had the value of the resdate @@ -149,60 +172,79 @@ sub AddReserve { my $usth = $dbh->prepare($query); $usth->execute( $borrowernumber, $nextacctno, $fee, "Reserve Charge - $title", $fee ); - $usth->finish; } #if ($const eq 'a'){ my $query = qq/ INSERT INTO reserves (borrowernumber,biblionumber,reservedate,branchcode,constrainttype, - priority,reservenotes,itemnumber,found,waitingdate) + priority,reservenotes,itemnumber,found,waitingdate,expirationdate) VALUES (?,?,?,?,?, - ?,?,?,?,?) + ?,?,?,?,?,?) /; my $sth = $dbh->prepare($query); $sth->execute( $borrowernumber, $biblionumber, $resdate, $branch, $const, $priority, $notes, $checkitem, - $found, $waitingdate + $found, $waitingdate, $expdate ); - $sth->finish; + + # Send e-mail to librarian if syspref is active + if(C4::Context->preference("emailLibrarianWhenHoldIsPlaced")){ + my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber); + my $biblio = GetBiblioData($biblionumber); + my $letter = C4::Letters::getletter( 'reserves', 'HOLDPLACED'); + my $admin_email_address = C4::Context->preference('KohaAdminEmailAddress'); + + my %keys = (%$borrower, %$biblio); + foreach my $key (keys %keys) { + my $replacefield = "<<$key>>"; + $letter->{content} =~ s/$replacefield/$keys{$key}/g; + $letter->{title} =~ s/$replacefield/$keys{$key}/g; + } + + C4::Letters::EnqueueLetter( + { letter => $letter, + borrowernumber => $borrowernumber, + message_transport_type => 'email', + from_address => $admin_email_address, + to_address => $admin_email_address, + } + ); + + + } + #} - if ( ( $const eq "o" ) || ( $const eq "e" ) ) { - my $numitems = @$bibitems; - my $i = 0; - while ( $i < $numitems ) { - my $biblioitem = @$bibitems[$i]; - my $query = qq/ - INSERT INTO reserveconstraints - (borrowernumber,biblionumber,reservedate,biblioitemnumber) - VALUES + ($const eq "o" || $const eq "e") or return; # FIXME: why not have a useful return value? + $query = qq/ + INSERT INTO reserveconstraints + (borrowernumber,biblionumber,reservedate,biblioitemnumber) + VALUES (?,?,?,?) - /; - my $sth = $dbh->prepare(""); - $sth->execute( $borrowernumber, $biblionumber, $resdate, - $biblioitem ); - $sth->finish; - $i++; - } + /; + $sth = $dbh->prepare($query); # keep prepare outside the loop! + foreach (@$bibitems) { + $sth->execute($borrowernumber, $biblionumber, $resdate, $_); } - return; + + return; # FIXME: why not have a useful return value? } =item GetReservesFromBiblionumber -@borrowerreserv=&GetReserves($biblionumber,$itemnumber,$borrowernumber); +($count, $title_reserves) = &GetReserves($biblionumber); -this function get the list of reservation for an C<$biblionumber>, C<$itemnumber> or C<$borrowernumber> -given on input arg. -Only 1 argument has to be passed. +This function gets the list of reservations for one C<$biblionumber>, returning a count +of the reserves and an arrayref pointing to the reserves for C<$biblionumber>. =cut sub GetReservesFromBiblionumber { - my ( $biblionumber, $itemnumber, $borrowernumber ) = @_; + my ($biblionumber) = shift or return (0, []); + my ($all_dates) = shift; my $dbh = C4::Context->dbh; # Find the desired items in the reserves @@ -216,51 +258,49 @@ sub GetReservesFromBiblionumber { constrainttype, found, itemnumber, - reservenotes + reservenotes, + expirationdate, + lowestPriority FROM reserves - WHERE cancellationdate IS NULL - AND (found <> \'F\' OR found IS NULL) - AND biblionumber = ? - ORDER BY priority"; + WHERE biblionumber = ? "; + unless ( $all_dates ) { + $query .= "AND reservedate <= CURRENT_DATE()"; + } + $query .= "ORDER BY priority"; my $sth = $dbh->prepare($query); $sth->execute($biblionumber); my @results; my $i = 0; while ( my $data = $sth->fetchrow_hashref ) { - # FIXME - What is this if-statement doing? How do constraints work? - if ( $data->{constrainttype} eq 'o' ) { + # FIXME - What is this doing? How do constraints work? + if ($data->{constrainttype} eq 'o') { $query = ' SELECT biblioitemnumber - FROM reserveconstraints - WHERE biblionumber = ? - AND borrowernumber = ? - AND reservedate = ? + FROM reserveconstraints + WHERE biblionumber = ? + AND borrowernumber = ? + AND reservedate = ? '; my $csth = $dbh->prepare($query); - $csth->execute( $data->{biblionumber}, $data->{borrowernumber}, - $data->{reservedate}, ); - + $csth->execute($data->{biblionumber}, $data->{borrowernumber}, $data->{reservedate}); my @bibitemno; while ( my $bibitemnos = $csth->fetchrow_array ) { - push( @bibitemno, $bibitemnos ); + push( @bibitemno, $bibitemnos ); # FIXME: inefficient: use fetchall_arrayref } - my $count = @bibitemno; - + my $count = scalar @bibitemno; + # if we have two or more different specific itemtypes # reserved by same person on same day my $bdata; if ( $count > 1 ) { - $bdata = GetBiblioItemData( $bibitemno[$i] ); - $i++; + $bdata = GetBiblioItemData( $bibitemno[$i] ); # FIXME: This doesn't make sense. + $i++; # $i can increase each pass, but the next @bibitemno might be smaller? } else { - # Look up the book we just found. $bdata = GetBiblioItemData( $bibitemno[0] ); } - $csth->finish; - # Add the results of this latest search to the current # results. # FIXME - An 'each' would probably be more efficient. @@ -270,7 +310,6 @@ sub GetReservesFromBiblionumber { } push @results, $data; } - $sth->finish; return ( $#results + 1, \@results ); } @@ -283,15 +322,16 @@ sub GetReservesFromBiblionumber { =cut sub GetReservesFromItemnumber { - my ( $itemnumber ) = @_; + my ( $itemnumber, $all_dates ) = @_; my $dbh = C4::Context->dbh; my $query = " SELECT reservedate,borrowernumber,branchcode FROM reserves WHERE itemnumber=? - AND cancellationdate IS NULL - AND (found <> 'F' OR found IS NULL) "; + unless ( $all_dates ) { + $query .= " AND reservedate <= CURRENT_DATE()"; + } my $sth_res = $dbh->prepare($query); $sth_res->execute($itemnumber); my ( $reservedate, $borrowernumber,$branchcode ) = $sth_res->fetchrow_array; @@ -315,7 +355,6 @@ sub GetReservesFromBorrowernumber { SELECT * FROM reserves WHERE borrowernumber=? - AND cancellationdate IS NULL AND found =? ORDER BY reservedate "); @@ -325,8 +364,6 @@ sub GetReservesFromBorrowernumber { SELECT * FROM reserves WHERE borrowernumber=? - AND cancellationdate IS NULL - AND (found != 'F' or found is null) ORDER BY reservedate "); $sth->execute($borrowernumber); @@ -335,7 +372,178 @@ sub GetReservesFromBorrowernumber { return @$data; } #------------------------------------------------------------------------------------- +=item CanBookBeReserved + +$error = &CanBookBeReserved($borrowernumber, $biblionumber) + +=cut + +sub CanBookBeReserved{ + my ($borrowernumber, $biblionumber) = @_; + my $dbh = C4::Context->dbh; + my $biblio = GetBiblioData($biblionumber); + my $borrower = C4::Members::GetMember(borrowernumber=>$borrowernumber); + my $controlbranch = C4::Context->preference('ReservesControlBranch'); + my $itype = C4::Context->preference('item-level_itypes'); + my $reservesrights= 0; + my $reservescount = 0; + + # we retrieve the user rights + my @args; + my $rightsquery = "SELECT categorycode, itemtype, branchcode, reservesallowed + FROM issuingrules + WHERE categorycode IN (?, '*')"; + push @args,$borrower->{categorycode}; + + if($controlbranch eq "ItemHomeLibrary"){ + $rightsquery .= " AND branchcode = '*'"; + }elsif($controlbranch eq "PatronLibrary"){ + $rightsquery .= " AND branchcode IN (?,'*')"; + push @args, $borrower->{branchcode}; + } + + if(not $itype){ + $rightsquery .= " AND itemtype IN (?,'*')"; + push @args, $biblio->{itemtype}; + }else{ + $rightsquery .= " AND itemtype = '*'"; + } + + $rightsquery .= " ORDER BY categorycode DESC, itemtype DESC, branchcode DESC"; + my $sthrights = $dbh->prepare($rightsquery); + $sthrights->execute(@args); + + if(my $row = $sthrights->fetchrow_hashref()){ + $reservesrights = $row->{reservesallowed}; + } + + @args = (); + # we count how many reserves the borrower have + my $countquery = "SELECT count(*) as count + FROM reserves + LEFT JOIN items USING (itemnumber) + LEFT JOIN biblioitems ON (reserves.biblionumber=biblioitems.biblionumber) + LEFT JOIN borrowers USING (borrowernumber) + WHERE borrowernumber = ? + "; + push @args, $borrowernumber; + + if(not $itype){ + $countquery .= "AND itemtype = ?"; + push @args, $biblio->{itemtype}; + } + + if($controlbranch eq "PatronLibrary"){ + $countquery .= " AND borrowers.branchcode = ? "; + push @args, $borrower->{branchcode}; + } + + my $sthcount = $dbh->prepare($countquery); + $sthcount->execute(@args); + + if(my $row = $sthcount->fetchrow_hashref()){ + $reservescount = $row->{count}; + } + if($reservescount < $reservesrights){ + return 1; + }else{ + return 0; + } + +} + +=item CanItemBeReserved + +$error = &CanItemBeReserved($borrowernumber, $itemnumber) + +this function return 1 if an item can be issued by this borrower. + +=cut + +sub CanItemBeReserved{ + my ($borrowernumber, $itemnumber) = @_; + + my $dbh = C4::Context->dbh; + my $allowedreserves = 0; + + my $controlbranch = C4::Context->preference('ReservesControlBranch'); + my $itype = C4::Context->preference('item-level_itypes') ? "itype" : "itemtype"; + + # we retrieve borrowers and items informations # + my $item = GetItem($itemnumber); + my $borrower = C4::Members::GetMember('borrowernumber'=>$borrowernumber); + + # we retrieve user rights on this itemtype and branchcode + my $sth = $dbh->prepare("SELECT categorycode, itemtype, branchcode, reservesallowed + FROM issuingrules + WHERE (categorycode in (?,'*') ) + AND (itemtype IN (?,'*')) + AND (branchcode IN (?,'*')) + ORDER BY + categorycode DESC, + itemtype DESC, + branchcode DESC;" + ); + + my $querycount ="SELECT + count(*) as count + FROM reserves + LEFT JOIN items USING (itemnumber) + LEFT JOIN biblioitems ON (reserves.biblionumber=biblioitems.biblionumber) + LEFT JOIN borrowers USING (borrowernumber) + WHERE borrowernumber = ? + "; + + + my $itemtype = $item->{$itype}; + my $categorycode = $borrower->{categorycode}; + my $branchcode = ""; + my $branchfield = "reserves.branchcode"; + + if( $controlbranch eq "ItemHomeLibrary" ){ + $branchfield = "items.homebranch"; + $branchcode = $item->{homebranch}; + }elsif( $controlbranch eq "PatronLibrary" ){ + $branchfield = "borrowers.branchcode"; + $branchcode = $borrower->{branchcode}; + } + + # we retrieve rights + $sth->execute($categorycode, $itemtype, $branchcode); + if(my $rights = $sth->fetchrow_hashref()){ + $itemtype = $rights->{itemtype}; + $allowedreserves = $rights->{reservesallowed}; + }else{ + $itemtype = '*'; + } + + # we retrieve count + + $querycount .= "AND $branchfield = ?"; + + $querycount .= " AND $itype = ?" if ($itemtype ne "*"); + my $sthcount = $dbh->prepare($querycount); + + if($itemtype eq "*"){ + $sthcount->execute($borrowernumber, $branchcode); + }else{ + $sthcount->execute($borrowernumber, $branchcode, $itemtype); + } + + my $reservecount = "0"; + if(my $rowcount = $sthcount->fetchrow_hashref()){ + $reservecount = $rowcount->{count}; + } + + # we check if it's ok or not + if( $reservecount < $allowedreserves ){ + return 1; + }else{ + return 0; + } +} +#-------------------------------------------------------------------------------- =item GetReserveCount $number = &GetReserveCount($borrowernumber); @@ -353,14 +561,10 @@ sub GetReserveCount { SELECT COUNT(*) AS counter FROM reserves WHERE borrowernumber = ? - AND cancellationdate IS NULL - AND (found != \'F\' OR found IS NULL) '; my $sth = $dbh->prepare($query); $sth->execute($borrowernumber); my $row = $sth->fetchrow_hashref; - $sth->finish; - return $row->{counter}; } @@ -389,7 +593,7 @@ sub GetOtherReserves { ); #launch the subroutine dotransfer - C4::Circulation::ModItemTransfer( + C4::Items::ModItemTransfer( $itemnumber, $iteminfo->{'holdingbranch'}, $checkreserves->{'branchcode'} @@ -492,8 +696,7 @@ sub GetReserveFee { while ( my $itdata = $sth2->fetchrow_hashref ) { my $sth3 = $dbh->prepare( "SELECT * FROM issues - WHERE itemnumber = ? - AND returndate IS NULL" + WHERE itemnumber = ?" ); $sth3->execute( $itdata->{'itemnumber'} ); if ( my $isdata = $sth3->fetchrow_hashref ) { @@ -532,9 +735,8 @@ sub GetReservesToBranch { my $sth = $dbh->prepare( "SELECT borrowernumber,reservedate,itemnumber,timestamp FROM reserves - WHERE priority='0' AND cancellationdate is null - AND branchcode=? - AND found IS NULL " + WHERE priority='0' + AND branchcode=?" ); $sth->execute( $frombranch ); my @transreserv; @@ -543,7 +745,6 @@ sub GetReservesToBranch { $transreserv[$i] = $data; $i++; } - $sth->finish; return (@transreserv); } @@ -559,7 +760,6 @@ sub GetReservesForBranch { my $query = "SELECT borrowernumber,reservedate,itemnumber,waitingdate FROM reserves WHERE priority='0' - AND cancellationdate IS NULL AND found='W' "; if ($frombranch){ $query .= " AND branchcode=? "; @@ -578,13 +778,25 @@ sub GetReservesForBranch { $transreserv[$i] = $data; $i++; } - $sth->finish; return (@transreserv); } +sub GetReserveStatus { + my ($itemnumber) = @_; + + my $dbh = C4::Context->dbh; + + my $itemstatus = $dbh->prepare("SELECT found FROM reserves WHERE itemnumber = ?"); + + $itemstatus->execute($itemnumber); + my ($found) = $itemstatus->fetchrow_array; + return $found; +} + =item CheckReserves ($status, $reserve) = &CheckReserves($itemnumber); + ($status, $reserve) = &CheckReserves(undef, $barcode); Find a book in the reserves. @@ -612,65 +824,50 @@ sub CheckReserves { my ( $item, $barcode ) = @_; my $dbh = C4::Context->dbh; my $sth; + my $select = " + SELECT items.biblionumber, + items.biblioitemnumber, + itemtypes.notforloan, + items.notforloan AS itemnotforloan, + items.itemnumber + FROM items + LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber + LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype + "; + if ($item) { - my $qitem = $dbh->quote($item); - # Look up the item by itemnumber - my $query = " - SELECT items.biblionumber, items.biblioitemnumber, itemtypes.notforloan - FROM items - LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber - LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype - WHERE itemnumber=$qitem - "; - $sth = $dbh->prepare($query); + $sth = $dbh->prepare("$select WHERE itemnumber = ?"); + $sth->execute($item); } else { - my $qbc = $dbh->quote($barcode); - # Look up the item by barcode - my $query = " - SELECT items.biblionumber, items.biblioitemnumber, itemtypes.notforloan - FROM items - LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber - LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype - WHERE items.biblioitemnumber = biblioitems.biblioitemnumber - AND biblioitems.itemtype = itemtypes.itemtype - AND barcode=$qbc - "; - $sth = $dbh->prepare($query); - - # FIXME - This function uses $item later on. Ought to set it here. + $sth = $dbh->prepare("$select WHERE barcode = ?"); + $sth->execute($barcode); } - $sth->execute; - my ( $biblio, $bibitem, $notforloan ) = $sth->fetchrow_array; - $sth->finish; + # note: we get the itemnumber because we might have started w/ just the barcode. Now we know for sure we have it. + my ( $biblio, $bibitem, $notforloan_per_itemtype, $notforloan_per_item, $itemnumber ) = $sth->fetchrow_array; + + return ( 0, 0 ) unless $itemnumber; # bail if we got nothing. # if item is not for loan it cannot be reserved either..... - return ( 0, 0 ) if $notforloan; + # execpt where items.notforloan < 0 : This indicates the item is holdable. + return ( 0, 0 ) if ( $notforloan_per_item > 0 ) or $notforloan_per_itemtype; - # get the reserves... # Find this item in the reserves - my @reserves = _Findgroupreserve( $bibitem, $biblio ); - my $count = scalar @reserves; + my @reserves = _Findgroupreserve( $bibitem, $biblio, $itemnumber ); # $priority and $highest are used to find the most important item # in the list returned by &_Findgroupreserve. (The lower $priority, # the more important the item.) # $highest is the most important item we've seen so far. - my $priority = 10000000; my $highest; - if ($count) { + if (scalar @reserves) { + my $priority = 10000000; foreach my $res (@reserves) { - # FIXME - $item might be undefined or empty: the caller - # might be searching by barcode. - if ( $res->{'itemnumber'} == $item ) { - # Found it - return ( "Waiting", $res ); - } - else { - # See if this item is more important than what we've got - # so far. - if ( $res->{'priority'} != 0 && $res->{'priority'} < $priority ) - { + if ( $res->{'itemnumber'} == $itemnumber && $res->{'priority'} == 0) { + return ( "Waiting", $res ); # Found it + } else { + # See if this item is more important than what we've got so far + if ( $res->{'priority'} && $res->{'priority'} < $priority ) { $priority = $res->{'priority'}; $highest = $res; } @@ -678,10 +875,9 @@ sub CheckReserves { } } - # If we get this far, then no exact match was found. Print the - # most important item on the list. I think this tells us who's - # next in line to get this book. - if ($highest) { # FIXME - $highest might be undefined + # If we get this far, then no exact match was found. + # We return the most important (i.e. next) reservation. + if ($highest) { $highest->{'itemnumber'} = $item; return ( "Reserved", $highest ); } @@ -690,6 +886,29 @@ sub CheckReserves { } } +=item CancelExpiredReserves + + CancelExpiredReserves(); + + Cancels all reserves with an expiration date from before today. + +=cut + +sub CancelExpiredReserves { + + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( " + SELECT * FROM reserves WHERE DATE(expirationdate) < DATE( CURDATE() ) + AND expirationdate IS NOT NULL + " ); + $sth->execute(); + + while ( my $res = $sth->fetchrow_hashref() ) { + CancelReserve( $res->{'biblionumber'}, '', $res->{'borrowernumber'} ); + } + +} + =item CancelReserve &CancelReserve($biblionumber, $itemnumber, $borrowernumber); @@ -725,6 +944,21 @@ sub CancelReserve { my $sth = $dbh->prepare($query); $sth->execute( $item, $borr ); $sth->finish; + $query = " + INSERT INTO old_reserves + SELECT * FROM reserves + WHERE itemnumber = ? + AND borrowernumber = ? + "; + $sth = $dbh->prepare($query); + $sth->execute( $item, $borr ); + $query = " + DELETE FROM reserves + WHERE itemnumber = ? + AND borrowernumber = ? + "; + $sth = $dbh->prepare($query); + $sth->execute( $item, $borr ); } else { # removing a reserve record.... @@ -736,7 +970,6 @@ sub CancelReserve { AND borrowernumber = ? AND cancellationdate IS NULL AND itemnumber IS NULL - AND (found <> 'F' OR found IS NULL) /; my $sth = $dbh->prepare($query); $sth->execute( $biblio, $borr ); @@ -749,8 +982,6 @@ sub CancelReserve { priority = 0 WHERE biblionumber = ? AND borrowernumber = ? - AND cancellationdate IS NULL - AND (found <> 'F' or found IS NULL) /; # update the database, removing the record... @@ -758,6 +989,23 @@ sub CancelReserve { $sth->execute( $biblio, $borr ); $sth->finish; + $query = qq/ + INSERT INTO old_reserves + SELECT * FROM reserves + WHERE biblionumber = ? + AND borrowernumber = ? + /; + $sth = $dbh->prepare($query); + $sth->execute( $biblio, $borr ); + + $query = qq/ + DELETE FROM reserves + WHERE biblionumber = ? + AND borrowernumber = ? + /; + $sth = $dbh->prepare($query); + $sth->execute( $biblio, $borr ); + # now fix the priority on the others.... _FixPriority( $priority, $biblio ); } @@ -765,7 +1013,36 @@ sub CancelReserve { =item ModReserve -&ModReserve($rank,$biblio,$borrower,$branch) +=over 4 + +ModReserve($rank, $biblio, $borrower, $branch[, $itemnumber]) + +=back + +Change a hold request's priority or cancel it. + +C<$rank> specifies the effect of the change. If C<$rank> +is 'W' or 'n', nothing happens. This corresponds to leaving a +request alone when changing its priority in the holds queue +for a bib. + +If C<$rank> is 'del', the hold request is cancelled. + +If C<$rank> is an integer greater than zero, the priority of +the request is set to that value. Since priority != 0 means +that the item is not waiting on the hold shelf, setting the +priority to a non-zero value also sets the request's found +status and waiting date to NULL. + +The optional C<$itemnumber> parameter is used only when +C<$rank> is a non-zero integer; if supplied, the itemnumber +of the hold request is set accordingly; if omitted, the itemnumber +is cleared. + +FIXME: Note that the forgoing can have the effect of causing +item-level hold requests to turn into title-level requests. This +will be fixed once reserves has separate columns for requested +itemnumber and supplying itemnumber. =cut @@ -781,21 +1058,33 @@ sub ModReserve { SET cancellationdate=now() WHERE biblionumber = ? AND borrowernumber = ? - AND cancellationdate is NULL - AND (found <> 'F' or found is NULL) /; my $sth = $dbh->prepare($query); $sth->execute( $biblio, $borrower ); $sth->finish; + $query = qq/ + INSERT INTO old_reserves + SELECT * + FROM reserves + WHERE biblionumber = ? + AND borrowernumber = ? + /; + $sth = $dbh->prepare($query); + $sth->execute( $biblio, $borrower ); + $query = qq/ + DELETE FROM reserves + WHERE biblionumber = ? + AND borrowernumber = ? + /; + $sth = $dbh->prepare($query); + $sth->execute( $biblio, $borrower ); } - else { + elsif ($rank =~ /^\d+/ and $rank > 0) { my $query = qq/ - UPDATE reserves SET priority = ? ,branchcode = ?, itemnumber = ?, found = NULL + UPDATE reserves SET priority = ? ,branchcode = ?, itemnumber = ?, found = NULL, waitingdate = NULL WHERE biblionumber = ? AND borrowernumber = ? - AND cancellationdate is NULL - AND (found <> 'F' or found is NULL) /; my $sth = $dbh->prepare($query); $sth->execute( $rank, $branch,$itemnumber, $biblio, $borrower); @@ -848,6 +1137,23 @@ sub ModReserveFill { $sth->execute( $biblionumber, $resdate, $borrowernumber ); $sth->finish; + # move to old_reserves + $query = "INSERT INTO old_reserves + SELECT * FROM reserves + WHERE biblionumber = ? + AND reservedate = ? + AND borrowernumber = ? + "; + $sth = $dbh->prepare($query); + $sth->execute( $biblionumber, $resdate, $borrowernumber ); + $query = "DELETE FROM reserves + WHERE biblionumber = ? + AND reservedate = ? + AND borrowernumber = ? + "; + $sth = $dbh->prepare($query); + $sth->execute( $biblionumber, $resdate, $borrowernumber ); + # now fix the priority on the others (if the priority wasn't # already sorted!).... unless ( $priority == 0 ) { @@ -880,7 +1186,10 @@ sub ModReserveStatus { "; my $sth_set = $dbh->prepare($query); $sth_set->execute( $newstatus, $itemnumber ); - $sth_set->finish; + + if ( C4::Context->preference("ReturnToShelvingCart") && $newstatus ) { + CartToShelf( $itemnumber ); + } } =item ModReserveAffect @@ -906,6 +1215,12 @@ sub ModReserveAffect { my $sth = $dbh->prepare("SELECT biblionumber FROM items WHERE itemnumber=?"); $sth->execute($itemnumber); my ($biblionumber) = $sth->fetchrow; + + # get request - need to find out if item is already + # waiting in order to not send duplicate hold filled notifications + my $request = GetReserveInfo($borrowernumber, $biblionumber); + my $already_on_shelf = ($request && $request->{found} eq 'W') ? 1 : 0; + # If we affect a reserve that has to be transfered, don't set to Waiting my $query; if ($transferToDo) { @@ -915,8 +1230,6 @@ sub ModReserveAffect { itemnumber = ? WHERE borrowernumber = ? AND biblionumber = ? - AND reserves.cancellationdate IS NULL - AND (reserves.found <> 'F' OR reserves.found IS NULL) "; } else { @@ -929,13 +1242,16 @@ sub ModReserveAffect { itemnumber = ? WHERE borrowernumber = ? AND biblionumber = ? - AND reserves.cancellationdate IS NULL - AND (reserves.found <> 'F' OR reserves.found IS NULL) "; } $sth = $dbh->prepare($query); $sth->execute( $itemnumber, $borrowernumber,$biblionumber); - $sth->finish; + _koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber ) if ( !$transferToDo && !$already_on_shelf ); + + if ( C4::Context->preference("ReturnToShelvingCart") ) { + CartToShelf( $itemnumber ); + } + return; } @@ -977,30 +1293,208 @@ sub ModReserveMinusPriority { my $query = " UPDATE reserves SET priority = 0 , itemnumber = ? - WHERE cancellationdate IS NULL - AND borrowernumber=? + WHERE borrowernumber=? AND biblionumber=? "; my $sth_upd = $dbh->prepare($query); $sth_upd->execute( $itemnumber, $borrowernumber, $biblionumber ); - $sth_upd->finish; # second step update all others reservs - $query = " - UPDATE reserves - SET priority = priority-1 - WHERE biblionumber = ? - AND priority > 0 - AND cancellationdate IS NULL - "; - $sth_upd = $dbh->prepare($query); - $sth_upd->execute( $biblionumber ); - $sth_upd->finish; - $sth_upd->finish; + _FixPriority($biblionumber, $borrowernumber, '0'); +} + +=item GetReserveInfo + +&GetReserveInfo($borrowernumber,$biblionumber); + + Get item and borrower details for a current hold. + Current implementation this query should have a single result. +=cut + +sub GetReserveInfo { + my ( $borrowernumber, $biblionumber ) = @_; + my $dbh = C4::Context->dbh; + my $strsth="SELECT + reservedate, + reservenotes, + reserves.borrowernumber, + reserves.biblionumber, + reserves.branchcode, + reserves.waitingdate, + notificationdate, + reminderdate, + priority, + found, + firstname, + surname, + phone, + email, + address, + address2, + cardnumber, + city, + zipcode, + biblio.title, + biblio.author, + items.holdingbranch, + items.itemcallnumber, + items.itemnumber, + items.location, + barcode, + notes + FROM reserves + LEFT JOIN items USING(itemnumber) + LEFT JOIN borrowers USING(borrowernumber) + LEFT JOIN biblio ON (reserves.biblionumber=biblio.biblionumber) + WHERE + reserves.borrowernumber=? + AND reserves.biblionumber=?"; + my $sth = $dbh->prepare($strsth); + $sth->execute($borrowernumber,$biblionumber); + + my $data = $sth->fetchrow_hashref; + return $data; + +} + +=item IsAvailableForItemLevelRequest + +=over 4 + +my $is_available = IsAvailableForItemLevelRequest($itemnumber); + +=back + +Checks whether a given item record is available for an +item-level hold request. An item is available if + +* it is not lost AND +* it is not damaged AND +* it is not withdrawn AND +* does not have a not for loan value > 0 + +Whether or not the item is currently on loan is +also checked - if the AllowOnShelfHolds system preference +is ON, an item can be requested even if it is currently +on loan to somebody else. If the system preference +is OFF, an item that is currently checked out cannot +be the target of an item-level hold request. + +Note that IsAvailableForItemLevelRequest() does not +check if the staff operator is authorized to place +a request on the item - in particular, +this routine does not check IndependantBranches +and canreservefromotherbranches. + +=cut + +sub IsAvailableForItemLevelRequest { + my $itemnumber = shift; + + my $item = GetItem($itemnumber); + + # must check the notforloan setting of the itemtype + # FIXME - a lot of places in the code do this + # or something similar - need to be + # consolidated + my $dbh = C4::Context->dbh; + my $notforloan_query; + if (C4::Context->preference('item-level_itypes')) { + $notforloan_query = "SELECT itemtypes.notforloan + FROM items + JOIN itemtypes ON (itemtypes.itemtype = items.itype) + WHERE itemnumber = ?"; + } else { + $notforloan_query = "SELECT itemtypes.notforloan + FROM items + JOIN biblioitems USING (biblioitemnumber) + JOIN itemtypes USING (itemtype) + WHERE itemnumber = ?"; + } + my $sth = $dbh->prepare($notforloan_query); + $sth->execute($itemnumber); + my $notforloan_per_itemtype = 0; + if (my ($notforloan) = $sth->fetchrow_array) { + $notforloan_per_itemtype = 1 if $notforloan; + } + + my $available_per_item = 1; + $available_per_item = 0 if $item->{itemlost} or + ( $item->{notforloan} > 0 ) or + ($item->{damaged} and not C4::Context->preference('AllowHoldsOnDamagedItems')) or + $item->{wthdrawn} or + $notforloan_per_itemtype; + + + if (C4::Context->preference('AllowOnShelfHolds')) { + return $available_per_item; + } else { + return ($available_per_item and ($item->{onloan} or GetReserveStatus($itemnumber) eq "W")); + } +} + +=item AlterPriority +AlterPriority( $where, $borrowernumber, $biblionumber, $reservedate ); + +This function changes a reserve's priority up, down, to the top, or to the bottom. +Input: $where is 'up', 'down', 'top' or 'bottom'. Biblionumber, Date reserve was placed + +=cut +sub AlterPriority { + my ( $where, $borrowernumber, $biblionumber ) = @_; + + my $dbh = C4::Context->dbh; + + ## Find this reserve + my $sth = $dbh->prepare('SELECT * FROM reserves WHERE biblionumber = ? AND borrowernumber = ? AND cancellationdate IS NULL'); + $sth->execute( $biblionumber, $borrowernumber ); + my $reserve = $sth->fetchrow_hashref(); + $sth->finish(); + + if ( $where eq 'up' || $where eq 'down' ) { + + my $priority = $reserve->{'priority'}; + $priority = $where eq 'up' ? $priority - 1 : $priority + 1; + _FixPriority( $biblionumber, $borrowernumber, $priority ) + + } elsif ( $where eq 'top' ) { + + _FixPriority( $biblionumber, $borrowernumber, '1' ) + + } elsif ( $where eq 'bottom' ) { + + _FixPriority( $biblionumber, $borrowernumber, '999999' ) + + } +} + +=item ToggleLowestPriority +ToggleLowestPriority( $borrowernumber, $biblionumber ); + +This function sets the lowestPriority field to true if is false, and false if it is true. +=cut + +sub ToggleLowestPriority { + my ( $borrowernumber, $biblionumber ) = @_; + + my $dbh = C4::Context->dbh; + + my $sth = $dbh->prepare( + "UPDATE reserves SET lowestPriority = NOT lowestPriority + WHERE biblionumber = ? + AND borrowernumber = ?" + ); + $sth->execute( + $biblionumber, + $borrowernumber, + ); + $sth->finish; + + _FixPriority( $biblionumber, $borrowernumber, '999999' ); } =item _FixPriority -&_FixPriority($biblio,$borrowernumber,$rank); +&_FixPriority($biblio,$borrowernumber,$rank,$ignoreSetLowestRank); Only used internally (so don't export it) Changed how this functions works # @@ -1012,7 +1506,7 @@ sub ModReserveMinusPriority { =cut sub _FixPriority { - my ( $biblio, $borrowernumber, $rank ) = @_; + my ( $biblio, $borrowernumber, $rank, $ignoreSetLowestRank ) = @_; my $dbh = C4::Context->dbh; if ( $rank eq "del" ) { CancelReserve( $biblio, undef, $borrowernumber ); @@ -1025,7 +1519,6 @@ sub _FixPriority { SET priority = 0 WHERE biblionumber = ? AND borrowernumber = ? - AND cancellationdate IS NULL AND found ='W' /; my $sth = $dbh->prepare($query); @@ -1037,13 +1530,13 @@ sub _FixPriority { # get whats left # FIXME adding a new security in returned elements for changing priority, # now, we don't care anymore any reservations with itemnumber linked (suppose a waiting reserve) + # This is wrong a waiting reserve has W set + # The assumption that having an itemnumber set means waiting is wrong and should be corrected any place it occurs my $query = qq/ SELECT borrowernumber, reservedate, constrainttype FROM reserves WHERE biblionumber = ? - AND cancellationdate IS NULL - AND itemnumber IS NULL - AND ((found <> 'F' and found <> 'W') or found is NULL) + AND ((found <> 'W') or found is NULL) ORDER BY priority ASC /; my $sth = $dbh->prepare($query); @@ -1089,18 +1582,26 @@ sub _FixPriority { ); $sth->finish; } + + $sth = $dbh->prepare( "SELECT borrowernumber FROM reserves WHERE lowestPriority = 1 ORDER BY priority" ); + $sth->execute(); + + unless ( $ignoreSetLowestRank ) { + while ( my $res = $sth->fetchrow_hashref() ) { + _FixPriority( $biblio, $res->{'borrowernumber'}, '999999', 1 ); + } + } } =item _Findgroupreserve - @results = &_Findgroupreserve($biblioitemnumber, $biblionumber); + @results = &_Findgroupreserve($biblioitemnumber, $biblionumber, $itemnumber); + +Looks for an item-specific match first, then for a title-level match, returning the +first match found. If neither, then we look for a 3rd kind of match based on +reserve constraints. -****** FIXME ****** -I don't know what this does, because I don't understand how reserve -constraints work. I think the idea is that you reserve a particular -biblio, and the constraint allows you to restrict it to a given -biblioitem (e.g., if you want to borrow the audio book edition of "The -Prophet", rather than the first available publication). +TODO: add more explanation about reserve constraints C<&_Findgroupreserve> returns : C<@results> is an array of references-to-hash whose keys are mostly @@ -1110,40 +1611,244 @@ C. =cut sub _Findgroupreserve { - my ( $bibitem, $biblio ) = @_; + my ( $bibitem, $biblio, $itemnumber ) = @_; my $dbh = C4::Context->dbh; + + # TODO: consolidate at least the SELECT portion of the first 2 queries to a common $select var. + # check for exact targetted match + my $item_level_target_query = qq/ + SELECT reserves.biblionumber AS biblionumber, + reserves.borrowernumber AS borrowernumber, + reserves.reservedate AS reservedate, + reserves.branchcode AS branchcode, + reserves.cancellationdate AS cancellationdate, + reserves.found AS found, + reserves.reservenotes AS reservenotes, + reserves.priority AS priority, + reserves.timestamp AS timestamp, + biblioitems.biblioitemnumber AS biblioitemnumber, + reserves.itemnumber AS itemnumber + FROM reserves + JOIN biblioitems USING (biblionumber) + JOIN hold_fill_targets USING (biblionumber, borrowernumber, itemnumber) + WHERE found IS NULL + AND priority > 0 + AND item_level_request = 1 + AND itemnumber = ? + AND reservedate <= CURRENT_DATE() + /; + my $sth = $dbh->prepare($item_level_target_query); + $sth->execute($itemnumber); + my @results; + if ( my $data = $sth->fetchrow_hashref ) { + push( @results, $data ); + } + return @results if @results; + + # check for title-level targetted match + my $title_level_target_query = qq/ + SELECT reserves.biblionumber AS biblionumber, + reserves.borrowernumber AS borrowernumber, + reserves.reservedate AS reservedate, + reserves.branchcode AS branchcode, + reserves.cancellationdate AS cancellationdate, + reserves.found AS found, + reserves.reservenotes AS reservenotes, + reserves.priority AS priority, + reserves.timestamp AS timestamp, + biblioitems.biblioitemnumber AS biblioitemnumber, + reserves.itemnumber AS itemnumber + FROM reserves + JOIN biblioitems USING (biblionumber) + JOIN hold_fill_targets USING (biblionumber, borrowernumber) + WHERE found IS NULL + AND priority > 0 + AND item_level_request = 0 + AND hold_fill_targets.itemnumber = ? + AND reservedate <= CURRENT_DATE() + /; + $sth = $dbh->prepare($title_level_target_query); + $sth->execute($itemnumber); + @results = (); + if ( my $data = $sth->fetchrow_hashref ) { + push( @results, $data ); + } + return @results if @results; + my $query = qq/ - SELECT reserves.biblionumber AS biblionumber, - reserves.borrowernumber AS borrowernumber, - reserves.reservedate AS reservedate, - reserves.branchcode AS branchcode, - reserves.cancellationdate AS cancellationdate, - reserves.found AS found, - reserves.reservenotes AS reservenotes, - reserves.priority AS priority, - reserves.timestamp AS timestamp, + SELECT reserves.biblionumber AS biblionumber, + reserves.borrowernumber AS borrowernumber, + reserves.reservedate AS reservedate, + reserves.branchcode AS branchcode, + reserves.cancellationdate AS cancellationdate, + reserves.found AS found, + reserves.reservenotes AS reservenotes, + reserves.priority AS priority, + reserves.timestamp AS timestamp, reserveconstraints.biblioitemnumber AS biblioitemnumber, - reserves.itemnumber AS itemnumber + reserves.itemnumber AS itemnumber FROM reserves LEFT JOIN reserveconstraints ON reserves.biblionumber = reserveconstraints.biblionumber WHERE reserves.biblionumber = ? AND ( ( reserveconstraints.biblioitemnumber = ? AND reserves.borrowernumber = reserveconstraints.borrowernumber - AND reserves.reservedate =reserveconstraints.reservedate ) + AND reserves.reservedate = reserveconstraints.reservedate ) OR reserves.constrainttype='a' ) - AND reserves.cancellationdate is NULL - AND (reserves.found <> 'F' or reserves.found is NULL) + AND (reserves.itemnumber IS NULL OR reserves.itemnumber = ?) + AND reserves.reservedate <= CURRENT_DATE() /; - my $sth = $dbh->prepare($query); - $sth->execute( $biblio, $bibitem ); - my @results; + $sth = $dbh->prepare($query); + $sth->execute( $biblio, $bibitem, $itemnumber ); + @results = (); while ( my $data = $sth->fetchrow_hashref ) { push( @results, $data ); } - $sth->finish; return @results; } +=item _koha_notify_reserve + +=over 4 + +_koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber ); + +=back + +Sends a notification to the patron that their hold has been filled (through +ModReserveAffect, _not_ ModReserveFill) + +=cut + +sub _koha_notify_reserve { + my ($itemnumber, $borrowernumber, $biblionumber) = @_; + + my $dbh = C4::Context->dbh; + my $borrower = C4::Members::GetMember( $borrowernumber ); + my $letter_code; + my $print_mode = 0; + my $messagingprefs; + if ( $borrower->{'email'} || $borrower->{'smsalertnumber'} ) { + $messagingprefs = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $borrowernumber, message_name => 'Hold Filled' } ); + + return if ( !defined( $messagingprefs->{'letter_code'} ) ); + $letter_code = $messagingprefs->{'letter_code'}; + } else { + $letter_code = 'HOLD_PRINT'; + $print_mode = 1; + } + + my $sth = $dbh->prepare(" + SELECT * + FROM reserves + WHERE borrowernumber = ? + AND biblionumber = ? + "); + $sth->execute( $borrowernumber, $biblionumber ); + my $reserve = $sth->fetchrow_hashref; + my $branch_details = GetBranchDetail( $reserve->{'branchcode'} ); + + my $admin_email_address = $branch_details->{'branchemail'} || C4::Context->preference('KohaAdminEmailAddress'); + + my $letter = getletter( 'reserves', $letter_code ); + die "Could not find a letter called '$letter_code' in the 'reserves' module" unless( $letter ); + + C4::Letters::parseletter( $letter, 'branches', $reserve->{'branchcode'} ); + C4::Letters::parseletter( $letter, 'borrowers', $borrowernumber ); + C4::Letters::parseletter( $letter, 'biblio', $biblionumber ); + C4::Letters::parseletter( $letter, 'reserves', $borrowernumber, $biblionumber ); + + if ( $reserve->{'itemnumber'} ) { + C4::Letters::parseletter( $letter, 'items', $reserve->{'itemnumber'} ); + } + my $today = C4::Dates->new()->output(); + $letter->{'title'} =~ s/<>/$today/g; + $letter->{'content'} =~ s/<>/$today/g; + $letter->{'content'} =~ s/<<[a-z0-9_]+\.[a-z0-9]+>>//g; #remove any stragglers + + if ( $print_mode ) { + C4::Letters::EnqueueLetter( { + letter => $letter, + borrowernumber => $borrowernumber, + message_transport_type => 'print', + } ); + + return; + } + + if ( grep { $_ eq 'email' } @{$messagingprefs->{transports}} ) { + # aka, 'email' in ->{'transports'} + C4::Letters::EnqueueLetter( + { letter => $letter, + borrowernumber => $borrowernumber, + message_transport_type => 'email', + from_address => $admin_email_address, + } + ); + } + + if ( grep { $_ eq 'sms' } @{$messagingprefs->{transports}} ) { + C4::Letters::EnqueueLetter( + { letter => $letter, + borrowernumber => $borrowernumber, + message_transport_type => 'sms', + } + ); + } +} + +=item _ShiftPriorityByDateAndPriority + +=over 4 + +$new_priority = _ShiftPriorityByDateAndPriority( $biblionumber, $reservedate, $priority ); + +=back + +This increments the priority of all reserves after the one + with either the lowest date after C<$reservedate> + or the lowest priority after C<$priority>. + +It effectively makes room for a new reserve to be inserted with a certain + priority, which is returned. + +This is most useful when the reservedate can be set by the user. It allows + the new reserve to be placed before other reserves that have a later + reservedate. Since priority also is set by the form in reserves/request.pl + the sub accounts for that too. + +=cut + +sub _ShiftPriorityByDateAndPriority { + my ( $biblio, $resdate, $new_priority ) = @_; + + my $dbh = C4::Context->dbh; + my $query = "SELECT priority FROM reserves WHERE biblionumber = ? AND ( reservedate > ? OR priority > ? ) ORDER BY priority ASC LIMIT 1"; + my $sth = $dbh->prepare( $query ); + $sth->execute( $biblio, $resdate, $new_priority ); + my $min_priority = $sth->fetchrow; + # if no such matches are found, $new_priority remains as original value + $new_priority = $min_priority if ( $min_priority ); + + # Shift the priority up by one; works in conjunction with the next SQL statement + $query = "UPDATE reserves + SET priority = priority+1 + WHERE biblionumber = ? + AND borrowernumber = ? + AND reservedate = ? + AND found IS NULL"; + my $sth_update = $dbh->prepare( $query ); + + # Select all reserves for the biblio with priority greater than $new_priority, and order greatest to least + $query = "SELECT borrowernumber, reservedate FROM reserves WHERE priority >= ? AND biblionumber = ? ORDER BY priority DESC"; + $sth = $dbh->prepare( $query ); + $sth->execute( $new_priority, $biblio ); + while ( my $row = $sth->fetchrow_hashref ) { + $sth_update->execute( $biblio, $row->{borrowernumber}, $row->{reservedate} ); + } + + return $new_priority; # so the caller knows what priority they wind up receiving +} + =back =head1 AUTHOR @@ -1152,3 +1857,4 @@ Koha Developement team =cut +1;