+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 );