X-Git-Url: http://koha-dev.rot13.org:8081/gitweb/?a=blobdiff_plain;f=C4%2FReserves.pm;h=2dd2688fcdc454dc75bd2a2d2539aa5547fc9d32;hb=2c470899b3f0191b9597d713e895b9240fe1d137;hp=20292deb46f05ebf099c716d4a80f9377c94d3d1;hpb=9f42b8ad49fb17c4cbea4825639de7c4212f681c;p=koha_gimpoz diff --git a/C4/Reserves.pm b/C4/Reserves.pm old mode 100755 new mode 100644 index 20292deb46..2dd2688fcd --- a/C4/Reserves.pm +++ b/C4/Reserves.pm @@ -1,9 +1,8 @@ -# -*- tab-width: 8 -*- -# NOTE: This file uses standard 8-character tabs - package C4::Reserves; # Copyright 2000-2002 Katipo Communications +# 2006 SAN Ouest Provence +# 2007 BibLibre Paul POULAIN # # This file is part of Koha. # @@ -16,24 +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. -# $Id$ 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; -use vars qw($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 = do { my @v = '$Revision$' =~ /\d+/g; shift(@v) . "." . join( "_", map { sprintf "%03d", $_ } @v ); }; +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); =head1 NAME @@ -45,284 +50,203 @@ C4::Reserves - Koha functions for dealing with reservation. =head1 DESCRIPTION -this modules provides somes functions to deal with reservations. +This modules provides somes functions to deal with reservations. + + Reserves are stored in reserves table. + The following columns contains important values : + - priority >0 : then the reserve is at 1st stage, and not yet affected to any item. + =0 : then the reserve is being dealed + - found : NULL : means the patron requested the 1st available, and we haven't choosen the item + T(ransit) : the reserve is linked to an item but is in transit to the pickup branch + W(aiting) : the reserve is linked to an item, is at the pickup branch, and is waiting on the hold shelf + F(inished) : the reserve has been completed, and is done + - itemnumber : empty : the reserve is still unaffected to an item + filled: the reserve is attached to an item + The complete workflow is : + ==== 1st use case ==== + patron request a document, 1st available : P >0, F=NULL, I=NULL + a library having it run "transfertodo", and clic on the list + if there is no transfer to do, the reserve waiting + patron can pick it up P =0, F=W, I=filled + if there is a transfer to do, write in branchtransfer P =0, F=T, I=filled + The pickup library recieve the book, it check in P =0, F=W, I=filled + The patron borrow the book P =0, F=F, I=filled + + ==== 2nd use case ==== + patron requests a document, a given item, + If pickup is holding branch P =0, F=W, I=filled + If transfer needed, write in branchtransfer P =0, F=T, I=filled + The pickup library receive the book, it checks it in P =0, F=W, I=filled + The patron borrow the book P =0, F=F, I=filled =head1 FUNCTIONS -=over 2 - =cut -@ISA = qw(Exporter); - -@EXPORT = qw( - &FindReserves - &CheckReserves - &GetWaitingReserves - &CancelReserve - &CalcReserveFee - &FillReserve - &ReserveWaiting - &CreateReserve - &UpdateReserve - &GetReserveTitle - &GetReservations - &SetWaitingStatus - &GlobalCancel - &MinusPriority - &OtherReserves - &GetFirstReserveDateFromItem - &CountReservesFromBorrower - &FixPriority - &FindReservesInQueue - GetReservesForBranch - GetReservesToBranch -); - -# make all your functions, whether exported or not; - -=item GlobalCancel - -($messages,$nextreservinfo) = &GlobalCancel($itemnumber,$borrowernumber); - - New op dev for the circulation based on item, global is a function to cancel reserv,check other reserves, and transfer document if it's necessary - -=cut - -sub GlobalCancel { - my $messages; - my $nextreservinfo; - my ( $itemnumber, $borrowernumber ) = @_; - - #step 1 : cancel the reservation - my $CancelReserve = CancelReserve( undef, $itemnumber, $borrowernumber ); - - #step 2 launch the subroutine of the others reserves - ( $messages, $nextreservinfo ) = OtherReserves($itemnumber); - - return ( $messages, $nextreservinfo ); -} +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 + &GetReserveInfo + &GetReserveStatus + + &GetOtherReserves + + &ModReserveFill + &ModReserveAffect + &ModReserve + &ModReserveStatus + &ModReserveCancelAll + &ModReserveMinusPriority + + &CheckReserves + &CanBookBeReserved + &CanItemBeReserved + &CancelReserve + &CancelExpiredReserves -=item OtherReserves + &IsAvailableForItemLevelRequest + + &AlterPriority + &ToggleLowestPriority + ); +} -($messages,$nextreservinfo)=$OtherReserves(itemnumber); +=head2 AddReserve -Check queued list of this document and check if this document must be transfered + AddReserve($branch,$borrowernumber,$biblionumber,$constraint,$bibitems,$priority,$resdate,$expdate,$notes,$title,$checkitem,$found) =cut -#' -sub OtherReserves { - my ($itemnumber) = @_; - my $messages; - my $nextreservinfo; - my ( $restype, $checkreserves ) = CheckReserves($itemnumber); - if ($checkreserves) { - my $iteminfo = GetItem($itemnumber); - if ( $iteminfo->{'holdingbranch'} ne $checkreserves->{'branchcode'} ) { - $messages->{'transfert'} = $checkreserves->{'branchcode'}; - #minus priorities of others reservs - MinusPriority( - $itemnumber, - $checkreserves->{'borrowernumber'}, - $iteminfo->{'biblionumber'} - ); - - #launch the subroutine dotransfer - C4::Circulation::dotransfer( - $itemnumber, - $iteminfo->{'holdingbranch'}, - $checkreserves->{'branchcode'} - ), - ; - } - - #step 2b : case of a reservation on the same branch, set the waiting status - else { - $messages->{'waiting'} = 1; - MinusPriority( - $itemnumber, - $checkreserves->{'borrowernumber'}, - $iteminfo->{'biblionumber'} - ); - SetWaitingStatus($itemnumber); - } - - $nextreservinfo = $checkreserves->{'borrowernumber'}; +sub AddReserve { + my ( + $branch, $borrowernumber, $biblionumber, + $constraint, $bibitems, $priority, $resdate, $expdate, $notes, + $title, $checkitem, $found + ) = @_; + my $fee = + GetReserveFee($borrowernumber, $biblionumber, $constraint, + $bibitems ); + my $dbh = C4::Context->dbh; + my $const = lc substr( $constraint, 0, 1 ); + $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' } - - return ( $messages, $nextreservinfo ); -} - -=item MinusPriority - -&MinusPriority($itemnumber,$borrowernumber,$biblionumber) - -Reduce the values of queuded list - -=cut - -#' -sub MinusPriority { - my ( $itemnumber, $borrowernumber, $biblionumber ) = @_; - - #first step update the value of the first person on reserv - my $dbh = C4::Context->dbh; - my $query = " - UPDATE reserves - SET priority = 0 , itemnumber = ? - WHERE cancellationdate IS NULL - AND 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 = " - SELECT priority,borrowernumber,biblionumber,reservedate - FROM reserves - WHERE priority !='0' - AND biblionumber = ? - AND cancellationdate IS NULL - "; - my $sth_oth = $dbh->prepare($query); - $sth_oth->execute($biblionumber); - while ( my ( $priority, $borrowernumber, $biblionumber, $reservedate ) = - $sth_oth->fetchrow_array ) - { - $priority--; - $query = " - UPDATE reserves - SET priority = ? - WHERE biblionumber = ? - AND borrowernumber = ? - AND reservedate = ? - "; - my $sth_upd_oth = $dbh->prepare($query); - $sth_upd_oth->execute( $priority, $biblionumber, $borrowernumber, - $reservedate ); - $sth_upd_oth->finish; + if ( C4::Context->preference( 'AllowHoldDateInFuture' ) ) { + # Make room in reserves for this before those of a later reserve date + $priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority ); } - $sth_oth->finish; -} - -=item SetWaitingStatus - -&SetWaitingStatus($itemnumber); - -we check if we have a reserves with itemnumber (New op system of reserves), if we found one, we update the status of the reservation when we have : 'priority' = 0, and we have an itemnumber - -=cut - -sub SetWaitingStatus { - - #first : check if we have a reservation for this item . - my ($itemnumber) = @_; - my $dbh = C4::Context->dbh; - my $query = " - SELECT priority,borrowernumber - FROM reserves - WHERE itemnumber=? - AND cancellationdate IS NULL - AND found IS NULL AND priority='0' - "; - my $sth_find = $dbh->prepare($query); - $sth_find->execute($itemnumber); - my ( $priority, $borrowernumber ) = $sth_find->fetchrow_array; - $sth_find->finish; - return unless $borrowernumber; - -# step 2 : if we have a borrowernumber, we update the value found to 'W' to notify the borrower - $query = " - UPDATE reserves - SET found='W',waitingdate = now() - WHERE borrowernumber=? - AND itemnumber=? - AND found IS NULL - "; - my $sth_set = $dbh->prepare($query); - $sth_set->execute( $borrowernumber, $itemnumber ); - $sth_set->finish; -} + my $waitingdate; -=item GetReservations + # If the reserv had the waiting status, we had the value of the resdate + if ( $found eq 'W' ) { + $waitingdate = $resdate; + } -@borrowerreserv=&GetReservations($itemnumber,$borrowernumber); + #eval { + # updates take place here + if ( $fee > 0 ) { + my $nextacctno = &getnextacctno( $borrowernumber ); + my $query = qq/ + INSERT INTO accountlines + (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding) + VALUES + (?,?,now(),?,?,'Res',?) + /; + my $usth = $dbh->prepare($query); + $usth->execute( $borrowernumber, $nextacctno, $fee, + "Reserve Charge - $title", $fee ); + } -this function get the list of reservation for an C<$itemnumber> or C<$borrowernumber> -given on input arg. You should give $itemnumber OR $borrowernumber but not both. + #if ($const eq 'a'){ + my $query = qq/ + INSERT INTO reserves + (borrowernumber,biblionumber,reservedate,branchcode,constrainttype, + priority,reservenotes,itemnumber,found,waitingdate,expirationdate) + VALUES + (?,?,?,?,?, + ?,?,?,?,?,?) + /; + my $sth = $dbh->prepare($query); + $sth->execute( + $borrowernumber, $biblionumber, $resdate, $branch, + $const, $priority, $notes, $checkitem, + $found, $waitingdate, $expdate + ); -=cut + # 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 $branchcode = $borrower->{branchcode}; + my $branch_details = C4::Branch::GetBranchDetail($branchcode); + my $admin_email_address =$branch_details->{'branchemail'} || 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, + } + ); + -sub GetReservations { - my ( $itemnumber, $borrowernumber ) = @_; - if ($itemnumber) { - my $dbh = C4::Context->dbh; - my $query = " - SELECT reservedate,borrowernumber - FROM reserves - WHERE itemnumber=? - AND cancellationdate IS NULL - AND (found <> 'F' OR found IS NULL) - "; - my $sth_res = $dbh->prepare($query); - $sth_res->execute($itemnumber); - my ( $reservedate, $borrowernumber ) = $sth_res->fetchrow_array; - return ( $reservedate, $borrowernumber ); } - if ($borrowernumber) { - my $dbh = C4::Context->dbh; - my $query = " - SELECT * - FROM reserves - WHERE borrowernumber=? - AND cancellationdate IS NULL - AND (found != 'F' or found is null) - ORDER BY reservedate - "; - my $sth_find = $dbh->prepare($query); - $sth_find->execute($borrowernumber); - my @borrowerreserv; - while ( my $data = $sth_find->fetchrow_hashref ) { - push @borrowerreserv, $data; - } - return @borrowerreserv; + + #} + ($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 + (?,?,?,?) + /; + $sth = $dbh->prepare($query); # keep prepare outside the loop! + foreach (@$bibitems) { + $sth->execute($borrowernumber, $biblionumber, $resdate, $_); } + + return; # FIXME: why not have a useful return value? } -=item FindReserves - - $results = &FindReserves($biblionumber, $borrowernumber); - -Looks books up in the reserves. C<$biblionumber> is the biblionumber -of the book to look up. C<$borrowernumber> is the borrower number of a -patron whose books to look up. - -Either C<$biblionumber> or C<$borrowernumber> may be the empty string, -but not both. If both are specified, C<&FindReserves> looks up the -given book for the given patron. If only C<$biblionumber> is -specified, C<&FindReserves> looks up that book for all patrons. If -only C<$borrowernumber> is specified, C<&FindReserves> looks up all of -that patron's reserves. If neither is specified, C<&FindReserves> -barfs. +=head2 GetReservesFromBiblionumber -For each book thus found, C<&FindReserves> checks the reserve -constraints and does something I don't understand. + ($count, $title_reserves) = &GetReserves($biblionumber); -C<&FindReserves> returns a two-element array: - -C<$results> is a reference to an array of references of hashes. Each hash -has for keys a list of column from reserves table (see details in function). +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 FindReserves { - my ( $biblionumber, $bor ) = @_; - my $dbh = C4::Context->dbh; - my @bind; +sub GetReservesFromBiblionumber { + my ($biblionumber) = shift or return (0, []); + my ($all_dates) = shift; + my $dbh = C4::Context->dbh; # Find the desired items in the reserves my $query = " @@ -334,68 +258,50 @@ sub FindReserves { reservedate, constrainttype, found, - itemnumber - FROM reserves - WHERE cancellationdate IS NULL - AND (found <> \'F\' OR found IS NULL) - "; - - if ( $biblionumber ne '' ) { - $query .= ' - AND biblionumber = ? - '; - push @bind, $biblionumber; - } - - if ( $bor ne '' ) { - $query .= ' - AND borrowernumber = ? - '; - push @bind, $bor; + itemnumber, + reservenotes, + expirationdate, + lowestPriority + FROM reserves + WHERE biblionumber = ? "; + unless ( $all_dates ) { + $query .= "AND reservedate <= CURRENT_DATE()"; } - - $query .= ' - ORDER BY priority - '; + $query .= "ORDER BY priority"; my $sth = $dbh->prepare($query); - $sth->execute(@bind); + $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. @@ -405,22 +311,184 @@ sub FindReserves { } push @results, $data; } - $sth->finish; - return ( $#results + 1, \@results ); } +=head2 GetReservesFromItemnumber + + ( $reservedate, $borrowernumber, $branchcode ) = GetReservesFromItemnumber($itemnumber); + +TODO :: Description here + +=cut + +sub GetReservesFromItemnumber { + my ( $itemnumber, $all_dates ) = @_; + my $dbh = C4::Context->dbh; + my $query = " + SELECT reservedate,borrowernumber,branchcode + FROM reserves + WHERE itemnumber=? + "; + 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; + return ( $reservedate, $borrowernumber, $branchcode ); +} + +=head2 GetReservesFromBorrowernumber + + $borrowerreserv = GetReservesFromBorrowernumber($borrowernumber,$tatus); + +TODO :: Descritpion + +=cut + +sub GetReservesFromBorrowernumber { + my ( $borrowernumber, $status ) = @_; + my $dbh = C4::Context->dbh; + my $sth; + if ($status) { + $sth = $dbh->prepare(" + SELECT * + FROM reserves + WHERE borrowernumber=? + AND found =? + ORDER BY reservedate + "); + $sth->execute($borrowernumber,$status); + } else { + $sth = $dbh->prepare(" + SELECT * + FROM reserves + WHERE borrowernumber=? + ORDER BY reservedate + "); + $sth->execute($borrowernumber); + } + my $data = $sth->fetchall_arrayref({}); + return @$data; +} #------------------------------------------------------------------------------------- +=head2 CanBookBeReserved + + $error = &CanBookBeReserved($borrowernumber, $biblionumber) + +=cut + +sub CanBookBeReserved{ + my ($borrowernumber, $biblionumber) = @_; + + my @items = GetItemsInfo($biblionumber); + foreach my $item (@items){ + return 1 if CanItemBeReserved($borrowernumber, $item->{itemnumber}); + } + return 0; +} -=item CountReservesFromBorrower +=head2 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; + } +} +#-------------------------------------------------------------------------------- +=head2 GetReserveCount -$number = &CountReservesFromBorrower($borrowernumber); + $number = &GetReserveCount($borrowernumber); this function returns the number of reservation for a borrower given on input arg. =cut -sub CountReservesFromBorrower { +sub GetReserveCount { my ($borrowernumber) = @_; my $dbh = C4::Context->dbh; @@ -429,62 +497,246 @@ sub CountReservesFromBorrower { 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}; } -#------------------------------------------------------------------------------------- - -=item GetFirstReserveDateFromItem +=head2 GetOtherReserves -$date = GetFirstReserveDateFromItem($itemnumber) + ($messages,$nextreservinfo)=$GetOtherReserves(itemnumber); -this function returns the first date a item has been reserved. +Check queued list of this document and check if this document must be transfered =cut -sub GetFirstReserveDateFromItem { +sub GetOtherReserves { my ($itemnumber) = @_; + my $messages; + my $nextreservinfo; + my ( $restype, $checkreserves ) = CheckReserves($itemnumber); + if ($checkreserves) { + my $iteminfo = GetItem($itemnumber); + if ( $iteminfo->{'holdingbranch'} ne $checkreserves->{'branchcode'} ) { + $messages->{'transfert'} = $checkreserves->{'branchcode'}; + #minus priorities of others reservs + ModReserveMinusPriority( + $itemnumber, + $checkreserves->{'borrowernumber'}, + $iteminfo->{'biblionumber'} + ); - my $dbh = C4::Context->dbh; + #launch the subroutine dotransfer + C4::Items::ModItemTransfer( + $itemnumber, + $iteminfo->{'holdingbranch'}, + $checkreserves->{'branchcode'} + ), + ; + } - my $query = ' - SELECT reservedate, - borrowernumber, - branchcode - FROM reserves - WHERE itemnumber = ? - AND cancellationdate IS NULL - AND (found != \'F\' OR found IS NULL) - '; - my $sth = $dbh->prepare($query); - $sth->execute($itemnumber); - my $row = $sth->fetchrow_hashref; + #step 2b : case of a reservation on the same branch, set the waiting status + else { + $messages->{'waiting'} = 1; + ModReserveMinusPriority( + $itemnumber, + $checkreserves->{'borrowernumber'}, + $iteminfo->{'biblionumber'} + ); + ModReserveStatus($itemnumber,'W'); + } + + $nextreservinfo = $checkreserves->{'borrowernumber'}; + } - return ($row->{reservedate},$row->{borrowernumber},$row->{branchcode}); + return ( $messages, $nextreservinfo ); } -#------------------------------------------------------------------------------------- +=head2 GetReserveFee -=item CheckReserves + $fee = GetReserveFee($borrowernumber,$biblionumber,$constraint,$biblionumber); - ($status, $reserve) = &CheckReserves($itemnumber, $barcode); +Calculate the fee for a reserve -Find a book in the reserves. +=cut + +sub GetReserveFee { + my ($borrowernumber, $biblionumber, $constraint, $bibitems ) = @_; + + #check for issues; + my $dbh = C4::Context->dbh; + my $const = lc substr( $constraint, 0, 1 ); + my $query = qq/ + SELECT * FROM borrowers + LEFT JOIN categories ON borrowers.categorycode = categories.categorycode + WHERE borrowernumber = ? + /; + my $sth = $dbh->prepare($query); + $sth->execute($borrowernumber); + my $data = $sth->fetchrow_hashref; + $sth->finish(); + my $fee = $data->{'reservefee'}; + my $cntitems = @- > $bibitems; + + if ( $fee > 0 ) { + + # check for items on issue + # first find biblioitem records + my @biblioitems; + my $sth1 = $dbh->prepare( + "SELECT * FROM biblio LEFT JOIN biblioitems on biblio.biblionumber = biblioitems.biblionumber + WHERE (biblio.biblionumber = ?)" + ); + $sth1->execute($biblionumber); + while ( my $data1 = $sth1->fetchrow_hashref ) { + if ( $const eq "a" ) { + push @biblioitems, $data1; + } + else { + my $found = 0; + my $x = 0; + while ( $x < $cntitems ) { + if ( @$bibitems->{'biblioitemnumber'} == + $data->{'biblioitemnumber'} ) + { + $found = 1; + } + $x++; + } + if ( $const eq 'o' ) { + if ( $found == 1 ) { + push @biblioitems, $data1; + } + } + else { + if ( $found == 0 ) { + push @biblioitems, $data1; + } + } + } + } + $sth1->finish; + my $cntitemsfound = @biblioitems; + my $issues = 0; + my $x = 0; + my $allissued = 1; + while ( $x < $cntitemsfound ) { + my $bitdata = $biblioitems[$x]; + my $sth2 = $dbh->prepare( + "SELECT * FROM items + WHERE biblioitemnumber = ?" + ); + $sth2->execute( $bitdata->{'biblioitemnumber'} ); + while ( my $itdata = $sth2->fetchrow_hashref ) { + my $sth3 = $dbh->prepare( + "SELECT * FROM issues + WHERE itemnumber = ?" + ); + $sth3->execute( $itdata->{'itemnumber'} ); + if ( my $isdata = $sth3->fetchrow_hashref ) { + } + else { + $allissued = 0; + } + } + $x++; + } + if ( $allissued == 0 ) { + my $rsth = + $dbh->prepare("SELECT * FROM reserves WHERE biblionumber = ?"); + $rsth->execute($biblionumber); + if ( my $rdata = $rsth->fetchrow_hashref ) { + } + else { + $fee = 0; + } + } + } + return $fee; +} + +=head2 GetReservesToBranch + + @transreserv = GetReservesToBranch( $frombranch ); + +Get reserve list for a given branch + +=cut + +sub GetReservesToBranch { + my ( $frombranch ) = @_; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( + "SELECT borrowernumber,reservedate,itemnumber,timestamp + FROM reserves + WHERE priority='0' + AND branchcode=?" + ); + $sth->execute( $frombranch ); + my @transreserv; + my $i = 0; + while ( my $data = $sth->fetchrow_hashref ) { + $transreserv[$i] = $data; + $i++; + } + return (@transreserv); +} + +=head2 GetReservesForBranch + + @transreserv = GetReservesForBranch($frombranch); + +=cut + +sub GetReservesForBranch { + my ($frombranch) = @_; + my $dbh = C4::Context->dbh; + my $query = "SELECT borrowernumber,reservedate,itemnumber,waitingdate + FROM reserves + WHERE priority='0' + AND found='W' "; + if ($frombranch){ + $query .= " AND branchcode=? "; + } + $query .= "ORDER BY waitingdate" ; + my $sth = $dbh->prepare($query); + if ($frombranch){ + $sth->execute($frombranch); + } + else { + $sth->execute(); + } + my @transreserv; + my $i = 0; + while ( my $data = $sth->fetchrow_hashref ) { + $transreserv[$i] = $data; + $i++; + } + 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; +} + +=head2 CheckReserves -C<$itemnumber> is the book's item number. C<$barcode> is its barcode. -Either one, but not both, may be false. If both are specified, -C<&CheckReserves> uses C<$itemnumber>. + ($status, $reserve) = &CheckReserves($itemnumber); + ($status, $reserve) = &CheckReserves(undef, $barcode); -$itemnubmer can be false, in which case uses the barcode. (Never uses -both. $itemnumber gets priority). +Find a book in the reserves. + +C<$itemnumber> is the book's item number. As I understand it, C<&CheckReserves> looks for the given item in the reserves. If it is found, that's a match, and C<$status> is set to @@ -504,68 +756,54 @@ table in the Koha database. =cut -#' 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, biblioitems, itemtypes - WHERE items.biblioitemnumber = biblioitems.biblioitemnumber - AND biblioitems.itemtype = itemtypes.itemtype - AND 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, biblioitems, itemtypes - 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, + # 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; } @@ -573,10 +811,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 ); } @@ -585,9 +822,30 @@ sub CheckReserves { } } -#------------------------------------------------------------------------------------- +=head2 CancelExpiredReserves + + CancelExpiredReserves(); + +Cancels all reserves with an expiration date from before today. + +=cut -=item CancelReserve +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'} ); + } + +} + +=head2 CancelReserve &CancelReserve($biblionumber, $itemnumber, $borrowernumber); @@ -605,11 +863,10 @@ priorities of the other people who are waiting on the book. =cut -#' sub CancelReserve { my ( $biblio, $item, $borr ) = @_; my $dbh = C4::Context->dbh; - if ( ( $item and $borr ) and ( not $biblio ) ) { + if ( $item and $borr ) { # removing a waiting reserve record.... # update the database... my $query = " @@ -623,8 +880,23 @@ 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 ); } - if ( ( $biblio and $borr ) and ( not $item ) ) { + else { # removing a reserve record.... # get the prioritiy on this record.... my $priority; @@ -634,7 +906,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 ); @@ -647,8 +918,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... @@ -656,96 +925,536 @@ 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 ); + _FixPriority( $biblio, $borr ); } } -#------------------------------------------------------------------------------------- - -=item FillReserve - - &FillReserve($reserve); - -Fill a reserve. If I understand this correctly, this means that the -reserved book has been found and given to the patron who reserved it. - -C<$reserve> specifies the reserve to fill. It is a reference-to-hash -whose keys are fields from the reserves table in the Koha database. - -=cut - -#' -sub FillReserve { - my ($res) = @_; - my $dbh = C4::Context->dbh; - # fill in a reserve record.... - my $qbiblio = $res->{'biblionumber'}; - my $borr = $res->{'borrowernumber'}; - my $resdate = $res->{'reservedate'}; +=head2 ModReserve - # get the priority on this record.... - my $priority; - my $query = "SELECT priority - FROM reserves - WHERE biblionumber = ? - AND borrowernumber = ? - AND reservedate = ?"; - my $sth = $dbh->prepare($query); - $sth->execute( $qbiblio, $borr, $resdate ); - ($priority) = $sth->fetchrow_array; - $sth->finish; + ModReserve($rank, $biblio, $borrower, $branch[, $itemnumber]) - # update the database... - $query = "UPDATE reserves - SET found = 'F', - priority = 0 - WHERE biblionumber = ? - AND reservedate = ? - AND borrowernumber = ? - "; - $sth = $dbh->prepare($query); - $sth->execute( $qbiblio, $resdate, $borr ); - $sth->finish; +Change a hold request's priority or cancel it. - # now fix the priority on the others (if the priority wasn't - # already sorted!).... - unless ( $priority == 0 ) { - FixPriority( $priority, $qbiblio ); - } -} +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. -=item FixPriority +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. -&FixPriority($biblio,$borrowernumber,$rank); +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. - Only used internally (so don't export it) - Changed how this functions works # - Now just gets an array of reserves in the rank order and updates them with - the array index (+1 as array starts from 0) - and if $rank is supplied will splice item from the array and splice it back in again - in new priority rank +B 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 +=cut -sub FixPriority { - my ( $biblio, $borrowernumber, $rank ) = @_; - my $dbh = C4::Context->dbh; - if ( $rank eq "del" ) { - CancelReserve( $biblio, undef, $borrowernumber ); +sub ModReserve { + #subroutine to update a reserve + my ( $rank, $biblio, $borrower, $branch , $itemnumber) = @_; + return if $rank eq "W"; + return if $rank eq "n"; + my $dbh = C4::Context->dbh; + if ( $rank eq "del" ) { + my $query = qq/ + UPDATE reserves + SET cancellationdate=now() + WHERE biblionumber = ? + AND borrowernumber = ? + /; + 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 ); + + } + elsif ($rank =~ /^\d+/ and $rank > 0) { + my $query = qq/ + UPDATE reserves SET priority = ? ,branchcode = ?, itemnumber = ?, found = NULL, waitingdate = NULL + WHERE biblionumber = ? + AND borrowernumber = ? + /; + my $sth = $dbh->prepare($query); + $sth->execute( $rank, $branch,$itemnumber, $biblio, $borrower); + $sth->finish; + _FixPriority( $biblio, $borrower, $rank); + } +} + +=head2 ModReserveFill + + &ModReserveFill($reserve); + +Fill a reserve. If I understand this correctly, this means that the +reserved book has been found and given to the patron who reserved it. + +C<$reserve> specifies the reserve to fill. It is a reference-to-hash +whose keys are fields from the reserves table in the Koha database. + +=cut + +sub ModReserveFill { + my ($res) = @_; + my $dbh = C4::Context->dbh; + # fill in a reserve record.... + my $biblionumber = $res->{'biblionumber'}; + my $borrowernumber = $res->{'borrowernumber'}; + my $resdate = $res->{'reservedate'}; + + # get the priority on this record.... + my $priority; + my $query = "SELECT priority + FROM reserves + WHERE biblionumber = ? + AND borrowernumber = ? + AND reservedate = ?"; + my $sth = $dbh->prepare($query); + $sth->execute( $biblionumber, $borrowernumber, $resdate ); + ($priority) = $sth->fetchrow_array; + $sth->finish; + + # update the database... + $query = "UPDATE reserves + SET found = 'F', + priority = 0 + WHERE biblionumber = ? + AND reservedate = ? + AND borrowernumber = ? + "; + $sth = $dbh->prepare($query); + $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 ) { + _FixPriority( $biblionumber, $borrowernumber ); + } +} + +=head2 ModReserveStatus + + &ModReserveStatus($itemnumber, $newstatus); + +Update the reserve status for the active (priority=0) reserve. + +$itemnumber is the itemnumber the reserve is on + +$newstatus is the new status. + +=cut + +sub ModReserveStatus { + + #first : check if we have a reservation for this item . + my ($itemnumber, $newstatus) = @_; + my $dbh = C4::Context->dbh; + my $query = " UPDATE reserves + SET found=?,waitingdate = now() + WHERE itemnumber=? + AND found IS NULL + AND priority = 0 + "; + my $sth_set = $dbh->prepare($query); + $sth_set->execute( $newstatus, $itemnumber ); + + if ( C4::Context->preference("ReturnToShelvingCart") && $newstatus ) { + CartToShelf( $itemnumber ); + } +} + +=head2 ModReserveAffect + + &ModReserveAffect($itemnumber,$borrowernumber,$diffBranchSend); + +This function affect an item and a status for a given reserve +The itemnumber parameter is used to find the biblionumber. +with the biblionumber & the borrowernumber, we can affect the itemnumber +to the correct reserve. + +if $transferToDo is not set, then the status is set to "Waiting" as well. +otherwise, a transfer is on the way, and the end of the transfer will +take care of the waiting status + +=cut + +sub ModReserveAffect { + my ( $itemnumber, $borrowernumber,$transferToDo ) = @_; + my $dbh = C4::Context->dbh; + + # we want to attach $itemnumber to $borrowernumber, find the biblionumber + # attached to $itemnumber + 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) { + $query = " + UPDATE reserves + SET priority = 0, + itemnumber = ?, + found = 'T' + WHERE borrowernumber = ? + AND biblionumber = ? + "; + } + else { + # affect the reserve to Waiting as well. + $query = " + UPDATE reserves + SET priority = 0, + found = 'W', + waitingdate=now(), + itemnumber = ? + WHERE borrowernumber = ? + AND biblionumber = ? + "; + } + $sth = $dbh->prepare($query); + $sth->execute( $itemnumber, $borrowernumber,$biblionumber); + _koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber ) if ( !$transferToDo && !$already_on_shelf ); + + if ( C4::Context->preference("ReturnToShelvingCart") ) { + CartToShelf( $itemnumber ); + } + + return; +} + +=head2 ModReserveCancelAll + + ($messages,$nextreservinfo) = &ModReserveCancelAll($itemnumber,$borrowernumber); + +function to cancel reserv,check other reserves, and transfer document if it's necessary + +=cut + +sub ModReserveCancelAll { + my $messages; + my $nextreservinfo; + my ( $itemnumber, $borrowernumber ) = @_; + + #step 1 : cancel the reservation + my $CancelReserve = CancelReserve( undef, $itemnumber, $borrowernumber ); + + #step 2 launch the subroutine of the others reserves + ( $messages, $nextreservinfo ) = GetOtherReserves($itemnumber); + + return ( $messages, $nextreservinfo ); +} + +=head2 ModReserveMinusPriority + + &ModReserveMinusPriority($itemnumber,$borrowernumber,$biblionumber) + +Reduce the values of queuded list + +=cut + +sub ModReserveMinusPriority { + my ( $itemnumber, $borrowernumber, $biblionumber ) = @_; + + #first step update the value of the first person on reserv + my $dbh = C4::Context->dbh; + my $query = " + UPDATE reserves + SET priority = 0 , itemnumber = ? + WHERE borrowernumber=? + AND biblionumber=? + "; + my $sth_upd = $dbh->prepare($query); + $sth_upd->execute( $itemnumber, $borrowernumber, $biblionumber ); + # second step update all others reservs + _FixPriority($biblionumber, $borrowernumber, '0'); +} + +=head2 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; + +} + +=head2 IsAvailableForItemLevelRequest + + my $is_available = IsAvailableForItemLevelRequest($itemnumber); + +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")); + } +} + +=head2 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' ) + + } +} + +=head2 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' ); +} + +=head2 _FixPriority + + &_FixPriority($biblio,$borrowernumber,$rank,$ignoreSetLowestRank); + +Only used internally (so don't export it) +Changed how this functions works # +Now just gets an array of reserves in the rank order and updates them with +the array index (+1 as array starts from 0) +and if $rank is supplied will splice item from the array and splice it back in again +in new priority rank + +=cut + +sub _FixPriority { + my ( $biblio, $borrowernumber, $rank, $ignoreSetLowestRank ) = @_; + my $dbh = C4::Context->dbh; + if ( $rank eq "del" ) { + CancelReserve( $biblio, undef, $borrowernumber ); } if ( $rank eq "W" || $rank eq "0" ) { - # make sure priority for waiting items is 0 + # make sure priority for waiting or in-transit items is 0 my $query = qq/ UPDATE reserves SET priority = 0 WHERE biblionumber = ? AND borrowernumber = ? - AND cancellationdate IS NULL - AND found ='W' + AND found IN ('W', 'T') /; my $sth = $dbh->prepare($query); $sth->execute( $biblio, $borrowernumber ); @@ -756,13 +1465,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' AND found <> 'T') or found is NULL) ORDER BY priority ASC /; my $sth = $dbh->prepare($query); @@ -808,645 +1517,270 @@ 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 ); + } + } } -#------------------------------------------------------------------------------------- +=head2 _Findgroupreserve -=item ReserveWaiting + @results = &_Findgroupreserve($biblioitemnumber, $biblionumber, $itemnumber); -branchcode = &ReserveWaiting($item,$borr); -this function set FOUND to 'W' for Waiting into the database. +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. -=cut +TODO: add more explanation about reserve constraints -sub ReserveWaiting { - my ( $item, $borr,$diffBranchSend ) = @_; - my $dbh = C4::Context->dbh; - - # get priority and biblionumber.... - my $query = qq/ - SELECT reserves.priority as priority, - reserves.biblionumber as biblionumber, - reserves.branchcode as branchcode, - reserves.timestamp as timestamp - FROM reserves,items - WHERE reserves.biblionumber = items.biblionumber - AND items.itemnumber = ? - AND reserves.borrowernumber = ? - AND reserves.cancellationdate IS NULL - AND (reserves.found <> 'F' OR reserves.found IS NULL) - /; - my $sth = $dbh->prepare($query); - $sth->execute( $item, $borr ); - my $data = $sth->fetchrow_hashref; - $sth->finish; - my $biblio = $data->{'biblionumber'}; - my $timestamp = $data->{'timestamp'}; - - # update reserves record.... - if ($diffBranchSend) { - $query = " - UPDATE reserves - SET priority = 0, - itemnumber = ? - WHERE borrowernumber = ? - AND biblionumber = ? - AND timestamp = ? - "; - } - else { - $query = " - UPDATE reserves - SET priority = 0, - found = 'W', - waitingdate=now(), - itemnumber = ? - WHERE borrowernumber = ? - AND biblionumber = ? - AND timestamp = ? - "; - } - $sth = $dbh->prepare($query); - $sth->execute( $item, $borr, $biblio, $timestamp ); - $sth->finish; - - # now fix up the remaining priorities.... - FixPriority( $data->{'priority'}, $biblio ); - my $branchcode = $data->{'branchcode'}; - return $branchcode; -} - -#------------------------------------------------------------------------------------- - -=item GetWaitingReserves - -\@itemswaiting=GetWaitingReserves($borr); - -this funtion fetch the list of waiting reserves from database. +C<&_Findgroupreserve> returns : +C<@results> is an array of references-to-hash whose keys are mostly +fields from the reserves table of the Koha database, plus +C. =cut -sub GetWaitingReserves { - my ($borr) = @_; - my $dbh = C4::Context->dbh; - my @itemswaiting; - my $query = " - SELECT * +sub _Findgroupreserve { + 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 - WHERE borrowernumber = ? - AND reserves.found = 'W' - AND cancellationdate IS NULL - "; - my $sth = $dbh->prepare($query); - $sth->execute($borr); - while ( my $data = $sth->fetchrow_hashref ) { - push( @itemswaiting, $data ); + 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 ); } - $sth->finish; - return \@itemswaiting; -} - -#------------------------------------------------------------------------------------- - -=item Findgroupreserve - - @results = &Findgroupreserve($biblioitemnumber, $biblionumber); - -****** 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). - -C<&Findgroupreserve> returns : -C<@results> is an array of references-to-hash whose keys are mostly -fields from the reserves table of the Koha database, plus -C. - -=cut + 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; -#' -sub Findgroupreserve { - my ( $bibitem, $biblio ) = @_; - my $dbh = C4::Context->dbh; 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.waitingdate AS waitingdate, + 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 CreateReserve +=head2 _koha_notify_reserve -CreateReserve($branch,$borrowernumber,$biblionumber,$constraint,$bibitems,$priority,$notes,$title,$checkitem,$found) + _koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber ); -FIXME - A somewhat different version of this function appears in -C4::Reserves. Pick one and stick with it. +Sends a notification to the patron that their hold has been filled (through +ModReserveAffect, _not_ ModReserveFill) =cut -sub CreateReserve { - my ( - $branch, $borrowernumber, $biblionumber, - $constraint, $bibitems, $priority, $notes, - $title, $checkitem, $found - ) = @_; - my $fee; - if ( $library_name =~ /Horowhenua/ ) { - $fee = - CalcHLTReserveFee($borrowernumber, $biblionumber, $constraint, - $bibitems ); - } - else { - $fee = - CalcReserveFee($borrowernumber, $biblionumber, $constraint, - $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]; - my $waitingdate; - - # If the reserv had the waiting status, we had the value of the resdate - if ( $found eq 'W' ) { - $waitingdate = $resdate; - } +sub _koha_notify_reserve { + my ($itemnumber, $borrowernumber, $biblionumber) = @_; - #eval { - # updates take place here - if ( $fee > 0 ) { - my $nextacctno = &getnextacctno( $borrowernumber ); - my $query = qq/ - INSERT INTO accountlines - (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding) - VALUES - (?,?,now(),?,?,'Res',?) - /; - 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) - VALUES - (?,?,?,?,?, - ?,?,?,?,?) - /; - my $sth = $dbh->prepare($query); - $sth->execute( - $borrowernumber, $biblionumber, $resdate, $branch, - $const, $priority, $notes, $checkitem, - $found, $waitingdate - ); - $sth->finish; - - #} - 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 - (?,?,?,?) - /; - my $sth = $dbh->prepare(""); - $sth->execute( $borrowernumber, $biblionumber, $resdate, - $biblioitem ); - $sth->finish; - $i++; - } - } - return; -} - -# FIXME - A functionally identical version of this function appears in -# C4::Reserves. Pick one and stick with it. -# XXX - Internal use only -# FIXME - opac-reserves.pl need to use it, temporarily put into @EXPORT - -sub CalcReserveFee { - my ($borrowernumber, $biblionumber, $constraint, $bibitems ) = @_; - - #check for issues; - my $dbh = C4::Context->dbh; - my $const = lc substr( $constraint, 0, 1 ); - my $query = qq/ - SELECT * FROM borrowers,categories - WHERE borrowernumber = ? - AND borrowers.categorycode = categories.categorycode - /; - my $sth = $dbh->prepare($query); - $sth->execute($borrowernumber); - my $data = $sth->fetchrow_hashref; - $sth->finish(); - my $fee = $data->{'reservefee'}; - my $cntitems = @- > $bibitems; - - if ( $fee > 0 ) { - - # check for items on issue - # first find biblioitem records - my @biblioitems; - my $sth1 = $dbh->prepare( - "SELECT * FROM biblio,biblioitems - WHERE (biblio.biblionumber = ?) - AND (biblio.biblionumber = biblioitems.biblionumber)" - ); - $sth1->execute($biblionumber); - while ( my $data1 = $sth1->fetchrow_hashref ) { - if ( $const eq "a" ) { - push @biblioitems, $data1; - } - else { - my $found = 0; - my $x = 0; - while ( $x < $cntitems ) { - if ( @$bibitems->{'biblioitemnumber'} == - $data->{'biblioitemnumber'} ) - { - $found = 1; - } - $x++; - } - if ( $const eq 'o' ) { - if ( $found == 1 ) { - push @biblioitems, $data1; - } - } - else { - if ( $found == 0 ) { - push @biblioitems, $data1; - } - } - } - } - $sth1->finish; - my $cntitemsfound = @biblioitems; - my $issues = 0; - my $x = 0; - my $allissued = 1; - while ( $x < $cntitemsfound ) { - my $bitdata = $biblioitems[$x]; - my $sth2 = $dbh->prepare( - "SELECT * FROM items - WHERE biblioitemnumber = ?" - ); - $sth2->execute( $bitdata->{'biblioitemnumber'} ); - while ( my $itdata = $sth2->fetchrow_hashref ) { - my $sth3 = $dbh->prepare( - "SELECT * FROM issues - WHERE itemnumber = ? - AND returndate IS NULL" - ); - $sth3->execute( $itdata->{'itemnumber'} ); - if ( my $isdata = $sth3->fetchrow_hashref ) { - } - else { - $allissued = 0; - } - } - $x++; - } - if ( $allissued == 0 ) { - my $rsth = - $dbh->prepare("SELECT * FROM reserves WHERE biblionumber = ?"); - $rsth->execute($biblionumber); - if ( my $rdata = $rsth->fetchrow_hashref ) { - } - else { - $fee = 0; - } - } - } - - # print "fee $fee"; - return $fee; -} - -# The following are junior and young adult item types that should not incur a -# reserve charge. -# -# Juniors: BJC, BJCN, BJF, BJK, BJM, BJN, BJP, BJSF, BJSN, DJ, DJP, FJ, JVID, -# VJ, VJP, PJ, TJ, TJP, VJ, VJP. -# -# Young adults: BYF, BYN, BYP, DY, DYP, PY, PYP, TY, TYP, VY, VYP. -# -# All other item types should incur a reserve charge. -sub CalcHLTReserveFee { - my ($borrowernumber, $biblionumber, $constraint, $bibitems ) = @_; my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare( - "SELECT * FROM borrowers,categories - WHERE (borrowernumber = ?) - AND (borrowers.categorycode = categories.categorycode)" - ); - $sth->execute($borrowernumber); - my $data = $sth->fetchrow_hashref; - $sth->finish(); - my $fee = $data->{'reservefee'}; - - my $matchno; - my @nocharge = - qw/BJC BJCN BJF BJK BJM BJN BJP BJSF BJSN DJ DJP FJ NJ CJ VJ VJP PJ TJ TJP BYF BYN BYP DY DYP PY PYP TY TYP VY VYP/; - $sth = $dbh->prepare( - "SELECT * FROM biblio,biblioitems - WHERE (biblio.biblionumber = ?) - AND (biblio.biblionumber = biblioitems.biblionumber)" - ); - $sth->execute($biblionumber); - $data = $sth->fetchrow_hashref; - my $itemtype = $data->{'itemtype'}; - for ( my $i = 0 ; $i < @nocharge ; $i++ ) { - if ( $itemtype eq $nocharge[$i] ) { - $matchno++; - last; - } + my $borrower = C4::Members::GetMember(borrowernumber => $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; } - if ( $matchno > 0 ) { - $fee = 0; - } - return $fee; -} + 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'} ); -=item GetNextAccountNumber + my $admin_email_address = $branch_details->{'branchemail'} || C4::Context->preference('KohaAdminEmailAddress'); -GetNextAccountNumber() + my $letter = getletter( 'reserves', $letter_code ); + die "Could not find a letter called '$letter_code' in the 'reserves' module" unless( $letter ); -=cut + 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 ); -sub GetNextAccountNumber { - my ($borrowernumber, $dbh ) = @_; - my $nextaccntno = 1; - my $sth = $dbh->prepare( - "select * from accountlines - where (borrowernumber = ?) - order by accountno desc" - ); - $sth->execute($borrowernumber); - if ( my $accdata = $sth->fetchrow_hashref ) { - $nextaccntno = $accdata->{'accountno'} + 1; + if ( $reserve->{'itemnumber'} ) { + C4::Letters::parseletter( $letter, 'items', $reserve->{'itemnumber'} ); } - $sth->finish; - return ($nextaccntno); -} - -#------------------------------------------------------------------------------------- - -=item UpdateReserve - -&UpdateReserve($rank,$biblio,$borrower,$branch) - -=cut - -sub UpdateReserve { - #subroutine to update a reserve - my ( $rank, $biblio, $borrower, $branch , $itemnumber) = @_; - return if $rank eq "W"; - return if $rank eq "n"; - my $dbh = C4::Context->dbh; - if ( $rank eq "del" ) { - my $query = qq/ - UPDATE reserves - 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; + 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; } - else { - my $query = qq/ - UPDATE reserves SET priority = ? ,branchcode = ?, itemnumber = ?, found = 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); - $sth->finish; - FixPriority( $biblio, $borrower, $rank); - } -} - -=item GetReserveTitle - -$data = GetReserveTitle($biblio,$bor,$date,$timestamp); - -=cut - -sub GetReserveTitle { - my ( $biblio, $bor, $date, $timestamp ) = @_; - my $dbh = C4::Context->dbh; - my $query = qq/ - SELECT * - FROM reserveconstraints,biblioitems - WHERE reserveconstraints.biblioitemnumber=biblioitems.biblioitemnumber - AND reserveconstraints.biblionumber=? - AND reserveconstraints.borrowernumber = ? - AND reserveconstraints.reservedate=? - AND reserveconstraints.timestamp=? - /; - my $sth = $dbh->prepare($query); - $sth->execute( $biblio, $bor, $date, $timestamp ); - my $data = $sth->fetchrow_hashref; - $sth->finish; - return $data; -} - -=item FindReservesInQueue - - $results = &FindReservesInQueue($biblionumber); - -Simple variant of FindReserves, exept the result is now displaying only the queue list of reservations with the same biblionumber (At this time only displayed in request.pl) -C<&FindReservesInQueue> returns a two-element array: - -C<$results> is a reference to an array of references of hashes. Each hash -has for keys a list of column from reserves table (see details in function). - -=cut - -#' - -sub FindReservesInQueue { - my ($biblionumber) = @_; - my $dbh = C4::Context->dbh; - - # Find the desired items in the reserves - my $query = qq/ - SELECT branchcode, - timestamp AS rtimestamp, - priority, - biblionumber, - borrowernumber, - reservedate, - constrainttype, - found, - itemnumber - FROM reserves - WHERE cancellationdate IS NULL - AND biblionumber = ? - AND (found <> \'F\' OR found IS NULL) - AND priority <> \'0\' - 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' ) { - $query = ' - SELECT biblioitemnumber - FROM reserveconstraints - WHERE biblionumber = ? - AND borrowernumber = ? - AND reservedate = ? - '; - my $csth = $dbh->prepare($query); - $csth->execute( $data->{biblionumber}, $data->{borrowernumber}, - $data->{reservedate}, ); - - my @bibitemno; - while ( my $bibitemnos = $csth->fetchrow_array ) { - push( @bibitemno, $bibitemnos ); + 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, } - my $count = @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++; - } - 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. - foreach my $key ( keys %$bdata ) { - $data->{$key} = $bdata->{$key}; + if ( grep { $_ eq 'sms' } @{$messagingprefs->{transports}} ) { + C4::Letters::EnqueueLetter( + { letter => $letter, + borrowernumber => $borrowernumber, + message_transport_type => 'sms', } - } - push @results, $data; + ); } - $sth->finish; - - return ( $#results + 1, \@results ); } +=head2 _ShiftPriorityByDateAndPriority -=head2 GetReservesToBranch + $new_priority = _ShiftPriorityByDateAndPriority( $biblionumber, $reservedate, $priority ); -@transreserv = GetReservesToBranch( $frombranch ); +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>. -Get reserve list for a given branch +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 GetReservesToBranch { - my ( $frombranch ) = @_; +sub _ShiftPriorityByDateAndPriority { + my ( $biblio, $resdate, $new_priority ) = @_; + my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare( - "SELECT borrowernumber,reservedate,itemnumber,timestamp - FROM reserves - WHERE priority='0' AND cancellationdate is null - AND branchcode=? - AND found IS NULL " - ); - $sth->execute( $frombranch ); - my @transreserv; - my $i = 0; - while ( my $data = $sth->fetchrow_hashref ) { - $transreserv[$i] = $data; - $i++; + 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} ); } - $sth->finish; - return (@transreserv); -} - -=head2 GetReservesForBranch -@transreserv = GetReservesForBranch($frombranch); - -=cut - -sub GetReservesForBranch { - my ($frombranch) = @_; - my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare( " - SELECT borrowernumber,reservedate,itemnumber,waitingdate - FROM reserves - WHERE priority='0' - AND cancellationdate IS NULL - AND found='W' - AND branchcode=? - ORDER BY waitingdate" ); - $sth->execute($frombranch); - my @transreserv; - my $i = 0; - while ( my $data = $sth->fetchrow_hashref ) { - $transreserv[$i] = $data; - $i++; - } - $sth->finish; - return (@transreserv); + return $new_priority; # so the caller knows what priority they wind up receiving } -=back - =head1 AUTHOR -Koha Developement team +Koha Development Team =cut +1;