Merge remote branch 'kc/new/bug_3326' into kcmaster
[koha.git] / C4 / Reserves.pm
index b10d6b4..87e2325 100644 (file)
@@ -2,7 +2,7 @@ package C4::Reserves;
 
 # Copyright 2000-2002 Katipo Communications
 #           2006 SAN Ouest Provence
 
 # Copyright 2000-2002 Katipo Communications
 #           2006 SAN Ouest Provence
-#           2007 BibLibre Paul POULAIN
+#           2007-2010 BibLibre Paul POULAIN
 #
 # This file is part of Koha.
 #
 #
 # This file is part of Koha.
 #
@@ -15,13 +15,13 @@ 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.
 #
 # 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;
 
 
 use strict;
-# use warnings;  # FIXME: someday
+#use warnings; FIXME - Bug 2505
 use C4::Context;
 use C4::Biblio;
 use C4::Members;
 use C4::Context;
 use C4::Biblio;
 use C4::Members;
@@ -32,7 +32,7 @@ use C4::Accounts;
 
 # for _koha_notify_reserve
 use C4::Members::Messaging;
 
 # for _koha_notify_reserve
 use C4::Members::Messaging;
-use C4::Members qw( GetMember );
+use C4::Members qw();
 use C4::Letters;
 use C4::Branch qw( GetBranchDetail );
 use C4::Dates qw( format_date_in_iso );
 use C4::Letters;
 use C4::Branch qw( GetBranchDetail );
 use C4::Dates qw( format_date_in_iso );
@@ -50,14 +50,15 @@ C4::Reserves - Koha functions for dealing with reservation.
 
 =head1 DESCRIPTION
 
 
 =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
   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
-            W(aiting)  : the reserve has an itemnumber affected, and is on the way
+            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
             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
@@ -67,20 +68,18 @@ C4::Reserves - Koha functions for dealing with reservation.
   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 
   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=NULL, 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
            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=NULL, I=filled
-        The pickup library recieve the book, it checks it in      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
   The patron borrow the book                                      P =0, F=F,    I=filled
-  
-=head1 FUNCTIONS
 
 
-=over 2
+=head1 FUNCTIONS
 
 =cut
 
 
 =cut
 
@@ -100,7 +99,8 @@ BEGIN {
         &GetReserveCount
         &GetReserveFee
                &GetReserveInfo
         &GetReserveCount
         &GetReserveFee
                &GetReserveInfo
-    
+        &GetReserveStatus
+        
         &GetOtherReserves
         
         &ModReserveFill
         &GetOtherReserves
         
         &ModReserveFill
@@ -123,7 +123,7 @@ BEGIN {
     );
 }    
 
     );
 }    
 
-=item AddReserve
+=head2 AddReserve
 
     AddReserve($branch,$borrowernumber,$biblionumber,$constraint,$bibitems,$priority,$resdate,$expdate,$notes,$title,$checkitem,$found)
 
 
     AddReserve($branch,$borrowernumber,$biblionumber,$constraint,$bibitems,$priority,$resdate,$expdate,$notes,$title,$checkitem,$found)
 
@@ -142,7 +142,11 @@ sub AddReserve {
     my $const   = lc substr( $constraint, 0, 1 );
     $resdate = format_date_in_iso( $resdate ) if ( $resdate );
     $resdate = C4::Dates->today( 'iso' ) unless ( $resdate );
     my $const   = lc substr( $constraint, 0, 1 );
     $resdate = format_date_in_iso( $resdate ) if ( $resdate );
     $resdate = C4::Dates->today( 'iso' ) unless ( $resdate );
-    $expdate = format_date_in_iso( $expdate ) if ( $expdate );
+    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 );
     if ( C4::Context->preference( 'AllowHoldDateInFuture' ) ) {
        # Make room in reserves for this before those of a later reserve date
        $priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority );
@@ -190,7 +194,9 @@ sub AddReserve {
         my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber);
         my $biblio   = GetBiblioData($biblionumber);
         my $letter = C4::Letters::getletter( 'reserves', 'HOLDPLACED');
         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 $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 %keys = (%$borrower, %$biblio);
         foreach my $key (keys %keys) {
@@ -228,9 +234,9 @@ sub AddReserve {
     return;     # FIXME: why not have a useful return value?
 }
 
     return;     # FIXME: why not have a useful return value?
 }
 
-=item GetReservesFromBiblionumber
+=head2 GetReservesFromBiblionumber
 
 
-($count, $title_reserves) = &GetReserves($biblionumber);
+  ($count, $title_reserves) = &GetReserves($biblionumber);
 
 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>.
 
 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>.
@@ -308,11 +314,11 @@ sub GetReservesFromBiblionumber {
     return ( $#results + 1, \@results );
 }
 
     return ( $#results + 1, \@results );
 }
 
-=item GetReservesFromItemnumber
+=head2 GetReservesFromItemnumber
 
  ( $reservedate, $borrowernumber, $branchcode ) = GetReservesFromItemnumber($itemnumber);
 
 
  ( $reservedate, $borrowernumber, $branchcode ) = GetReservesFromItemnumber($itemnumber);
 
-   TODO :: Description here
+TODO :: Description here
 
 =cut
 
 
 =cut
 
@@ -333,12 +339,12 @@ sub GetReservesFromItemnumber {
     return ( $reservedate, $borrowernumber, $branchcode );
 }
 
     return ( $reservedate, $borrowernumber, $branchcode );
 }
 
-=item GetReservesFromBorrowernumber
+=head2 GetReservesFromBorrowernumber
 
     $borrowerreserv = GetReservesFromBorrowernumber($borrowernumber,$tatus);
 
     $borrowerreserv = GetReservesFromBorrowernumber($borrowernumber,$tatus);
-    
-    TODO :: Descritpion
-    
+
+TODO :: Descritpion
+
 =cut
 
 sub GetReservesFromBorrowernumber {
 =cut
 
 sub GetReservesFromBorrowernumber {
@@ -367,92 +373,27 @@ sub GetReservesFromBorrowernumber {
     return @$data;
 }
 #-------------------------------------------------------------------------------------
     return @$data;
 }
 #-------------------------------------------------------------------------------------
-=item CanBookBeReserved
+=head2 CanBookBeReserved
 
 
-$error = &CanBookBeReserved($borrowernumber, $biblionumber)
+  $error = &CanBookBeReserved($borrowernumber, $biblionumber)
 
 =cut
 
 sub CanBookBeReserved{
     my ($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 = '*'";
+    my @items = GetItemsInfo($biblionumber);
+    foreach my $item (@items){
+        return 1 if CanItemBeReserved($borrowernumber, $item->{itemnumber});
     }
     }
-    
-    $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;
-    }
-    
+    return 0;
 }
 
 }
 
-=item CanItemBeReserved
+=head2 CanItemBeReserved
 
 
-$error = &CanItemBeReserved($borrowernumber, $itemnumber)
+  $error = &CanItemBeReserved($borrowernumber, $itemnumber)
 
 
-this function return 1 if an item can be issued by this borrower.
+This function return 1 if an item can be issued by this borrower.
 
 =cut
 
 
 =cut
 
@@ -539,9 +480,9 @@ sub CanItemBeReserved{
     }
 }
 #--------------------------------------------------------------------------------
     }
 }
 #--------------------------------------------------------------------------------
-=item GetReserveCount
+=head2 GetReserveCount
 
 
-$number = &GetReserveCount($borrowernumber);
+  $number = &GetReserveCount($borrowernumber);
 
 this function returns the number of reservation for a borrower given on input arg.
 
 
 this function returns the number of reservation for a borrower given on input arg.
 
@@ -563,9 +504,9 @@ sub GetReserveCount {
     return $row->{counter};
 }
 
     return $row->{counter};
 }
 
-=item GetOtherReserves
+=head2 GetOtherReserves
 
 
-($messages,$nextreservinfo)=$GetOtherReserves(itemnumber);
+  ($messages,$nextreservinfo)=$GetOtherReserves(itemnumber);
 
 Check queued list of this document and check if this document must be  transfered
 
 
 Check queued list of this document and check if this document must be  transfered
 
@@ -613,9 +554,9 @@ sub GetOtherReserves {
     return ( $messages, $nextreservinfo );
 }
 
     return ( $messages, $nextreservinfo );
 }
 
-=item GetReserveFee
+=head2 GetReserveFee
 
 
-$fee = GetReserveFee($borrowernumber,$biblionumber,$constraint,$biblionumber);
+  $fee = GetReserveFee($borrowernumber,$biblionumber,$constraint,$biblionumber);
 
 Calculate the fee for a reserve
 
 
 Calculate the fee for a reserve
 
@@ -716,9 +657,9 @@ sub GetReserveFee {
     return $fee;
 }
 
     return $fee;
 }
 
-=item GetReservesToBranch
+=head2 GetReservesToBranch
 
 
-@transreserv = GetReservesToBranch( $frombranch );
+  @transreserv = GetReservesToBranch( $frombranch );
 
 Get reserve list for a given branch
 
 
 Get reserve list for a given branch
 
@@ -743,9 +684,9 @@ sub GetReservesToBranch {
     return (@transreserv);
 }
 
     return (@transreserv);
 }
 
-=item GetReservesForBranch
+=head2 GetReservesForBranch
 
 
-@transreserv = GetReservesForBranch($frombranch);
+  @transreserv = GetReservesForBranch($frombranch);
 
 =cut
 
 
 =cut
 
@@ -776,7 +717,19 @@ sub GetReservesForBranch {
     return (@transreserv);
 }
 
     return (@transreserv);
 }
 
-=item CheckReserves
+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
 
   ($status, $reserve) = &CheckReserves($itemnumber);
   ($status, $reserve) = &CheckReserves(undef, $barcode);
 
   ($status, $reserve) = &CheckReserves($itemnumber);
   ($status, $reserve) = &CheckReserves(undef, $barcode);
@@ -851,6 +804,12 @@ sub CheckReserves {
             } else {
                 # See if this item is more important than what we've got so far
                 if ( $res->{'priority'} && $res->{'priority'} < $priority ) {
             } else {
                 # See if this item is more important than what we've got so far
                 if ( $res->{'priority'} && $res->{'priority'} < $priority ) {
+                    my $borrowerinfo=C4::Members::GetMemberDetails($res->{'borrowernumber'});
+                    my $iteminfo=C4::Items::GetItem($itemnumber);
+                    my $branch=C4::Circulation::_GetCircControlBranch($iteminfo,$borrowerinfo);
+                    my $branchitemrule = C4::Circulation::GetBranchItemRule($branch,$iteminfo->{'itype'});
+                    next if ($branchitemrule->{'holdallowed'} == 0);
+                    next if (($branchitemrule->{'holdallowed'} == 1) && ($branch ne $borrowerinfo->{'branchcode'}));
                     $priority = $res->{'priority'};
                     $highest  = $res;
                 }
                     $priority = $res->{'priority'};
                     $highest  = $res;
                 }
@@ -869,12 +828,12 @@ sub CheckReserves {
     }
 }
 
     }
 }
 
-=item CancelExpiredReserves
+=head2 CancelExpiredReserves
 
   CancelExpiredReserves();
 
   CancelExpiredReserves();
-  
-  Cancels all reserves with an expiration date from before today.
-  
+
+Cancels all reserves with an expiration date from before today.
+
 =cut
 
 sub CancelExpiredReserves {
 =cut
 
 sub CancelExpiredReserves {
@@ -892,7 +851,7 @@ sub CancelExpiredReserves {
   
 }
 
   
 }
 
-=item CancelReserve
+=head2 CancelReserve
 
   &CancelReserve($biblionumber, $itemnumber, $borrowernumber);
 
 
   &CancelReserve($biblionumber, $itemnumber, $borrowernumber);
 
@@ -990,17 +949,13 @@ sub CancelReserve {
         $sth->execute( $biblio, $borr );
 
         # now fix the priority on the others....
         $sth->execute( $biblio, $borr );
 
         # now fix the priority on the others....
-        _FixPriority( $priority, $biblio );
+        _FixPriority( $biblio, $borr );
     }
 }
 
     }
 }
 
-=item ModReserve
-
-=over 4
+=head2 ModReserve
 
 
-ModReserve($rank, $biblio, $borrower, $branch[, $itemnumber])
-
-=back
+  ModReserve($rank, $biblio, $borrower, $branch[, $itemnumber])
 
 Change a hold request's priority or cancel it.
 
 
 Change a hold request's priority or cancel it.
 
@@ -1022,7 +977,7 @@ C<$rank> is a non-zero integer; if supplied, the itemnumber
 of the hold request is set accordingly; if omitted, the itemnumber
 is cleared.
 
 of the hold request is set accordingly; if omitted, the itemnumber
 is cleared.
 
-FIXME: Note that the forgoing can have the effect of causing
+B<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.
 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.
@@ -1076,7 +1031,7 @@ sub ModReserve {
     }
 }
 
     }
 }
 
-=item ModReserveFill
+=head2 ModReserveFill
 
   &ModReserveFill($reserve);
 
 
   &ModReserveFill($reserve);
 
@@ -1140,13 +1095,13 @@ sub ModReserveFill {
     # now fix the priority on the others (if the priority wasn't
     # already sorted!)....
     unless ( $priority == 0 ) {
     # now fix the priority on the others (if the priority wasn't
     # already sorted!)....
     unless ( $priority == 0 ) {
-        _FixPriority( $priority, $biblionumber );
+        _FixPriority( $biblionumber, $borrowernumber );
     }
 }
 
     }
 }
 
-=item ModReserveStatus
+=head2 ModReserveStatus
 
 
-&ModReserveStatus($itemnumber, $newstatus);
+  &ModReserveStatus($itemnumber, $newstatus);
 
 Update the reserve status for the active (priority=0) reserve.
 
 
 Update the reserve status for the active (priority=0) reserve.
 
@@ -1175,9 +1130,9 @@ sub ModReserveStatus {
     }
 }
 
     }
 }
 
-=item ModReserveAffect
+=head2 ModReserveAffect
 
 
-&ModReserveAffect($itemnumber,$borrowernumber,$diffBranchSend);
+  &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.
 
 This function affect an item and a status for a given reserve
 The itemnumber parameter is used to find the biblionumber.
@@ -1187,6 +1142,7 @@ 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
 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 {
 =cut
 
 sub ModReserveAffect {
@@ -1210,7 +1166,8 @@ sub ModReserveAffect {
     $query = "
         UPDATE reserves
         SET    priority = 0,
     $query = "
         UPDATE reserves
         SET    priority = 0,
-               itemnumber = ?
+               itemnumber = ?,
+               found = 'T'
         WHERE borrowernumber = ?
           AND biblionumber = ?
     ";
         WHERE borrowernumber = ?
           AND biblionumber = ?
     ";
@@ -1238,11 +1195,11 @@ sub ModReserveAffect {
     return;
 }
 
     return;
 }
 
-=item ModReserveCancelAll
+=head2 ModReserveCancelAll
 
 
-($messages,$nextreservinfo) = &ModReserveCancelAll($itemnumber,$borrowernumber);
+  ($messages,$nextreservinfo) = &ModReserveCancelAll($itemnumber,$borrowernumber);
 
 
-    function to cancel reserv,check other reserves, and transfer document if it's necessary
+function to cancel reserv,check other reserves, and transfer document if it's necessary
 
 =cut
 
 
 =cut
 
@@ -1260,9 +1217,9 @@ sub ModReserveCancelAll {
     return ( $messages, $nextreservinfo );
 }
 
     return ( $messages, $nextreservinfo );
 }
 
-=item ModReserveMinusPriority
+=head2 ModReserveMinusPriority
 
 
-&ModReserveMinusPriority($itemnumber,$borrowernumber,$biblionumber)
+  &ModReserveMinusPriority($itemnumber,$borrowernumber,$biblionumber)
 
 Reduce the values of queuded list     
 
 
 Reduce the values of queuded list     
 
@@ -1285,34 +1242,53 @@ sub ModReserveMinusPriority {
     _FixPriority($biblionumber, $borrowernumber, '0');
 }
 
     _FixPriority($biblionumber, $borrowernumber, '0');
 }
 
-=item GetReserveInfo
+=head2 GetReserveInfo
 
 
-&GetReserveInfo($borrowernumber,$biblionumber);
+  &GetReserveInfo($borrowernumber,$biblionumber);
+
+Get item and borrower details for a current hold.
+Current implementation this query should have a single result.
 
 
- 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;
 =cut
 
 sub GetReserveInfo {
        my ( $borrowernumber, $biblionumber ) = @_;
     my $dbh = C4::Context->dbh;
-       my $strsth="SELECT reservedate, reservenotes, reserves.borrowernumber,
-                               reserves.biblionumber, reserves.branchcode,
-                               notificationdate, reminderdate, priority, found,
-                               firstname, surname, phone, 
-                               email, address, address2,
-                               cardnumber, city, zipcode,
-                               biblio.title, biblio.author,
-                               items.holdingbranch, items.itemcallnumber, items.itemnumber, 
-                               barcode, notes
-                       FROM reserves left join items 
-                               ON items.itemnumber=reserves.itemnumber , 
-                               borrowers, biblio 
+       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 
                        WHERE 
-                               reserves.borrowernumber=?  &&
-                               reserves.biblionumber=? && 
-                               reserves.borrowernumber=borrowers.borrowernumber && 
-                               reserves.biblionumber=biblio.biblionumber ";
+                               reserves.borrowernumber=?
+                               AND reserves.biblionumber=?";
        my $sth = $dbh->prepare($strsth); 
        $sth->execute($borrowernumber,$biblionumber);
 
        my $sth = $dbh->prepare($strsth); 
        $sth->execute($borrowernumber,$biblionumber);
 
@@ -1321,13 +1297,9 @@ sub GetReserveInfo {
 
 }
 
 
 }
 
-=item IsAvailableForItemLevelRequest
-
-=over 4
+=head2 IsAvailableForItemLevelRequest
 
 
-my $is_available = IsAvailableForItemLevelRequest($itemnumber);
-
-=back
+  my $is_available = IsAvailableForItemLevelRequest($itemnumber);
 
 Checks whether a given item record is available for an
 item-level hold request.  An item is available if
 
 Checks whether a given item record is available for an
 item-level hold request.  An item is available if
@@ -1393,17 +1365,19 @@ sub IsAvailableForItemLevelRequest {
     if (C4::Context->preference('AllowOnShelfHolds')) {
         return $available_per_item;
     } else {
     if (C4::Context->preference('AllowOnShelfHolds')) {
         return $available_per_item;
     } else {
-        return ($available_per_item and $item->{onloan}); 
+        return ($available_per_item and ($item->{onloan} or GetReserveStatus($itemnumber) eq "W")); 
     }
 }
 
     }
 }
 
-=item AlterPriority
-AlterPriority( $where, $borrowernumber, $biblionumber, $reservedate );
+=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
 
 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 ) = @_;
 
 sub AlterPriority {
     my ( $where, $borrowernumber, $biblionumber ) = @_;
 
@@ -1432,10 +1406,12 @@ sub AlterPriority {
     }
 }
 
     }
 }
 
-=item ToggleLowestPriority
-ToggleLowestPriority( $borrowernumber, $biblionumber );
+=head2 ToggleLowestPriority
+
+  ToggleLowestPriority( $borrowernumber, $biblionumber );
 
 This function sets the lowestPriority field to true if is false, and false if it is true.
 
 This function sets the lowestPriority field to true if is false, and false if it is true.
+
 =cut
 
 sub ToggleLowestPriority {
 =cut
 
 sub ToggleLowestPriority {
@@ -1457,16 +1433,16 @@ sub ToggleLowestPriority {
     _FixPriority( $biblionumber, $borrowernumber, '999999' );
 }
 
     _FixPriority( $biblionumber, $borrowernumber, '999999' );
 }
 
-=item _FixPriority
+=head2 _FixPriority
 
 
-&_FixPriority($biblio,$borrowernumber,$rank,$ignoreSetLowestRank);
+  &_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
+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 
 
 
 =cut 
 
@@ -1478,13 +1454,13 @@ sub _FixPriority {
      }
     if ( $rank eq "W" || $rank eq "0" ) {
 
      }
     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 = ?
         my $query = qq/
             UPDATE reserves
             SET    priority = 0
             WHERE biblionumber = ?
               AND borrowernumber = ?
-              AND found ='W'
+              AND found IN ('W', 'T')
         /;
         my $sth = $dbh->prepare($query);
         $sth->execute( $biblio, $borrowernumber );
         /;
         my $sth = $dbh->prepare($query);
         $sth->execute( $biblio, $borrowernumber );
@@ -1501,7 +1477,7 @@ sub _FixPriority {
         SELECT borrowernumber, reservedate, constrainttype
         FROM   reserves
         WHERE  biblionumber   = ?
         SELECT borrowernumber, reservedate, constrainttype
         FROM   reserves
         WHERE  biblionumber   = ?
-          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);
         ORDER BY priority ASC
     /;
     my $sth = $dbh->prepare($query);
@@ -1558,7 +1534,7 @@ sub _FixPriority {
     }
 }
 
     }
 }
 
-=item _Findgroupreserve
+=head2 _Findgroupreserve
 
   @results = &_Findgroupreserve($biblioitemnumber, $biblionumber, $itemnumber);
 
 
   @results = &_Findgroupreserve($biblioitemnumber, $biblionumber, $itemnumber);
 
@@ -1644,6 +1620,7 @@ sub _Findgroupreserve {
         SELECT reserves.biblionumber               AS biblionumber,
                reserves.borrowernumber             AS borrowernumber,
                reserves.reservedate                AS reservedate,
         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.branchcode                 AS branchcode,
                reserves.cancellationdate           AS cancellationdate,
                reserves.found                      AS found,
@@ -1671,13 +1648,9 @@ sub _Findgroupreserve {
     return @results;
 }
 
     return @results;
 }
 
-=item _koha_notify_reserve
-
-=over 4
-
-_koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber );
+=head2 _koha_notify_reserve
 
 
-=back
+  _koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber );
 
 Sends a notification to the patron that their hold has been filled (through
 ModReserveAffect, _not_ ModReserveFill)
 
 Sends a notification to the patron that their hold has been filled (through
 ModReserveAffect, _not_ ModReserveFill)
@@ -1688,9 +1661,19 @@ sub _koha_notify_reserve {
     my ($itemnumber, $borrowernumber, $biblionumber) = @_;
 
     my $dbh = C4::Context->dbh;
     my ($itemnumber, $borrowernumber, $biblionumber) = @_;
 
     my $dbh = C4::Context->dbh;
-    my $messagingprefs = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $borrowernumber, message_name => 'Hold Filled' } );
-
-    return if ( !defined( $messagingprefs->{'letter_code'} ) );
+    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;
+    }
 
     my $sth = $dbh->prepare("
         SELECT *
 
     my $sth = $dbh->prepare("
         SELECT *
@@ -1704,19 +1687,33 @@ sub _koha_notify_reserve {
 
     my $admin_email_address = $branch_details->{'branchemail'} || C4::Context->preference('KohaAdminEmailAddress');
 
 
     my $admin_email_address = $branch_details->{'branchemail'} || C4::Context->preference('KohaAdminEmailAddress');
 
-    my $letter = getletter( 'reserves', $messagingprefs->{'letter_code'} );
+    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, 'branches', $reserve->{'branchcode'} );
-    C4::Letters::parseletter( $letter, 'borrowers', $reserve->{'borrowernumber'} );
-    C4::Letters::parseletter( $letter, 'biblio', $reserve->{'biblionumber'} );
-    C4::Letters::parseletter( $letter, 'reserves', $reserve->{'borrowernumber'}, $reserve->{'biblionumber'} );
+    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'} );
     }
 
     if ( $reserve->{'itemnumber'} ) {
         C4::Letters::parseletter( $letter, 'items', $reserve->{'itemnumber'} );
     }
+    my $today = C4::Dates->new()->output();
+    $letter->{'title'} =~ s/<<today>>/$today/g;
+    $letter->{'content'} =~ s/<<today>>/$today/g;
     $letter->{'content'} =~ s/<<[a-z0-9_]+\.[a-z0-9]+>>//g; #remove any stragglers
 
     $letter->{'content'} =~ s/<<[a-z0-9_]+\.[a-z0-9]+>>//g; #remove any stragglers
 
-    if ( -1 !=  firstidx { $_ eq 'email' } @{$messagingprefs->{transports}} ) {
+    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,
         # aka, 'email' in ->{'transports'}
         C4::Letters::EnqueueLetter(
             {   letter                 => $letter,
@@ -1727,7 +1724,7 @@ sub _koha_notify_reserve {
         );
     }
 
         );
     }
 
-    if ( -1 != firstidx { $_ eq 'sms' } @{$messagingprefs->{transports}} ) {
+    if ( grep { $_ eq 'sms' } @{$messagingprefs->{transports}} ) {
         C4::Letters::EnqueueLetter(
             {   letter                 => $letter,
                 borrowernumber         => $borrowernumber,
         C4::Letters::EnqueueLetter(
             {   letter                 => $letter,
                 borrowernumber         => $borrowernumber,
@@ -1737,25 +1734,21 @@ sub _koha_notify_reserve {
     }
 }
 
     }
 }
 
-=item _ShiftPriorityByDateAndPriority
-
-=over 4
-
-$new_priority = _ShiftPriorityByDateAndPriority( $biblionumber, $reservedate, $priority );
+=head2 _ShiftPriorityByDateAndPriority
 
 
-=back
+  $new_priority = _ShiftPriorityByDateAndPriority( $biblionumber, $reservedate, $priority );
 
 This increments the priority of all reserves after the one
 
 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>.
+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
 
 It effectively makes room for a new reserve to be inserted with a certain
- priority, which is returned.
+priority, which is returned.
 
 This is most useful when the reservedate can be set by the user.  It allows
 
 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.
+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
 
 
 =cut
 
@@ -1763,39 +1756,36 @@ sub _ShiftPriorityByDateAndPriority {
     my ( $biblio, $resdate, $new_priority ) = @_;
 
     my $dbh = C4::Context->dbh;
     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";
+    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 $sth = $dbh->prepare( $query );
     $sth->execute( $biblio, $resdate, $new_priority );
-    my ( $min_priority ) = $sth->fetchrow;
-    $sth->finish;  # $sth might have more data.
+    my $min_priority = $sth->fetchrow;
+    # if no such matches are found, $new_priority remains as original value
     $new_priority = $min_priority if ( $min_priority );
     $new_priority = $min_priority if ( $min_priority );
-    my $updated_priority = $new_priority + 1;
 
 
-    $query = "
- UPDATE reserves
-    SET priority = ?
-  WHERE biblionumber = ?
-    AND borrowernumber = ?
-    AND reservedate = ?
-    AND found IS NULL";
+    # 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 );
 
     my $sth_update = $dbh->prepare( $query );
 
-    $query = "SELECT * FROM reserves WHERE priority >= ?";
+    # 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 = $dbh->prepare( $query );
-    $sth->execute( $new_priority );
+    $sth->execute( $new_priority, $biblio );
     while ( my $row = $sth->fetchrow_hashref ) {
     while ( my $row = $sth->fetchrow_hashref ) {
-       $sth_update->execute( $updated_priority, $biblio, $row->{borrowernumber}, $row->{reservedate} );
-       $updated_priority++;
+       $sth_update->execute( $biblio, $row->{borrowernumber}, $row->{reservedate} );
     }
 
     }
 
-    return $new_priority;  # so the caller knows what priority they end up at
+    return $new_priority;  # so the caller knows what priority they wind up receiving
 }
 
 }
 
-=back
-
 =head1 AUTHOR
 
 =head1 AUTHOR
 
-Koha Developement team <info@koha.org>
+Koha Development Team <http://koha-community.org/>
 
 =cut
 
 
 =cut