| Filename | /mnt/catalyst/koha/C4/Reserves.pm |
| Statements | Executed 36 statements in 18.7ms |
| Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
|---|---|---|---|---|---|
| 1 | 1 | 1 | 13.3ms | 233ms | C4::Reserves::BEGIN@27 |
| 1 | 1 | 1 | 10.0ms | 28.7ms | C4::Reserves::BEGIN@29 |
| 1 | 1 | 1 | 5.26ms | 55.9ms | C4::Reserves::BEGIN@36 |
| 1 | 1 | 1 | 4.78ms | 5.63ms | C4::Reserves::BEGIN@31 |
| 1 | 1 | 1 | 3.63ms | 4.25ms | C4::Reserves::BEGIN@34 |
| 1 | 1 | 1 | 420µs | 432µs | C4::Reserves::BEGIN@24 |
| 1 | 1 | 1 | 12µs | 12µs | C4::Reserves::BEGIN@28 |
| 1 | 1 | 1 | 11µs | 11µs | C4::Reserves::BEGIN@89 |
| 1 | 1 | 1 | 11µs | 11µs | C4::Reserves::BEGIN@30 |
| 1 | 1 | 1 | 9µs | 30µs | C4::Reserves::BEGIN@37 |
| 1 | 1 | 1 | 9µs | 27µs | C4::Reserves::BEGIN@42 |
| 1 | 1 | 1 | 9µs | 26µs | C4::Reserves::BEGIN@38 |
| 1 | 1 | 1 | 8µs | 32µs | C4::Reserves::BEGIN@40 |
| 1 | 1 | 1 | 7µs | 9µs | C4::Reserves::BEGIN@26 |
| 1 | 1 | 1 | 6µs | 50µs | C4::Reserves::BEGIN@44 |
| 1 | 1 | 1 | 4µs | 4µs | C4::Reserves::BEGIN@35 |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::AddReserve |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::AlterPriority |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::AutoUnsuspendReserves |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::CalculatePriority |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::CanBookBeReserved |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::CanItemBeReserved |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::CanReserveBeCanceledFromOpac |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::CancelExpiredReserves |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::CancelReserve |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::CheckReserves |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::GetOtherReserves |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::GetReserve |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::GetReserveCount |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::GetReserveFee |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::GetReserveId |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::GetReserveInfo |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::GetReserveStatus |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::GetReservesControlBranch |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::GetReservesForBranch |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::GetReservesFromBiblionumber |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::GetReservesFromBorrowernumber |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::GetReservesFromItemnumber |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::GetReservesToBranch |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::IsAvailableForItemLevelRequest |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::MergeHolds |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::ModReserve |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::ModReserveAffect |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::ModReserveCancelAll |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::ModReserveFill |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::ModReserveMinusPriority |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::ModReserveStatus |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::MoveReserve |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::ReserveSlip |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::RevertWaitingStatus |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::SuspendAll |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::ToggleLowestPriority |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::ToggleSuspend |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::_Findgroupreserve |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::_FixPriority |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::_ShiftPriorityByDateAndPriority |
| 0 | 0 | 0 | 0s | 0s | C4::Reserves::_koha_notify_reserve |
| Line | State ments |
Time on line |
Calls | Time in subs |
Code |
|---|---|---|---|---|---|
| 1 | package C4::Reserves; | ||||
| 2 | |||||
| 3 | # Copyright 2000-2002 Katipo Communications | ||||
| 4 | # 2006 SAN Ouest Provence | ||||
| 5 | # 2007-2010 BibLibre Paul POULAIN | ||||
| 6 | # 2011 Catalyst IT | ||||
| 7 | # | ||||
| 8 | # This file is part of Koha. | ||||
| 9 | # | ||||
| 10 | # Koha is free software; you can redistribute it and/or modify it under the | ||||
| 11 | # terms of the GNU General Public License as published by the Free Software | ||||
| 12 | # Foundation; either version 2 of the License, or (at your option) any later | ||||
| 13 | # version. | ||||
| 14 | # | ||||
| 15 | # Koha is distributed in the hope that it will be useful, but WITHOUT ANY | ||||
| 16 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||||
| 17 | # A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||||
| 18 | # | ||||
| 19 | # You should have received a copy of the GNU General Public License along | ||||
| 20 | # with Koha; if not, write to the Free Software Foundation, Inc., | ||||
| 21 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| 22 | |||||
| 23 | |||||
| 24 | 2 | 27µs | 2 | 443µs | # spent 432µs (420+12) within C4::Reserves::BEGIN@24 which was called:
# once (420µs+12µs) by C4::Circulation::BEGIN@27 at line 24 # spent 432µs making 1 call to C4::Reserves::BEGIN@24
# spent 12µs making 1 call to strict::import |
| 25 | #use warnings; FIXME - Bug 2505 | ||||
| 26 | 2 | 19µs | 2 | 11µs | # spent 9µs (7+2) within C4::Reserves::BEGIN@26 which was called:
# once (7µs+2µs) by C4::Circulation::BEGIN@27 at line 26 # spent 9µs making 1 call to C4::Reserves::BEGIN@26
# spent 2µs making 1 call to C4::Context::import |
| 27 | 2 | 2.03ms | 2 | 234ms | # spent 233ms (13.3+220) within C4::Reserves::BEGIN@27 which was called:
# once (13.3ms+220ms) by C4::Circulation::BEGIN@27 at line 27 # spent 233ms making 1 call to C4::Reserves::BEGIN@27
# spent 329µs making 1 call to Exporter::import |
| 28 | 2 | 28µs | 1 | 12µs | # spent 12µs within C4::Reserves::BEGIN@28 which was called:
# once (12µs+0s) by C4::Circulation::BEGIN@27 at line 28 # spent 12µs making 1 call to C4::Reserves::BEGIN@28 |
| 29 | 2 | 3.06ms | 2 | 28.8ms | # spent 28.7ms (10.0+18.7) within C4::Reserves::BEGIN@29 which was called:
# once (10.0ms+18.7ms) by C4::Circulation::BEGIN@27 at line 29 # spent 28.7ms making 1 call to C4::Reserves::BEGIN@29
# spent 109µs making 1 call to Exporter::import |
| 30 | 2 | 27µs | 1 | 11µs | # spent 11µs within C4::Reserves::BEGIN@30 which was called:
# once (11µs+0s) by C4::Circulation::BEGIN@27 at line 30 # spent 11µs making 1 call to C4::Reserves::BEGIN@30 |
| 31 | 2 | 2.69ms | 2 | 5.76ms | # spent 5.63ms (4.78+852µs) within C4::Reserves::BEGIN@31 which was called:
# once (4.78ms+852µs) by C4::Circulation::BEGIN@27 at line 31 # spent 5.63ms making 1 call to C4::Reserves::BEGIN@31
# spent 135µs making 1 call to Exporter::import |
| 32 | |||||
| 33 | # for _koha_notify_reserve | ||||
| 34 | 2 | 3.35ms | 1 | 4.25ms | # spent 4.25ms (3.63+624µs) within C4::Reserves::BEGIN@34 which was called:
# once (3.63ms+624µs) by C4::Circulation::BEGIN@27 at line 34 # spent 4.25ms making 1 call to C4::Reserves::BEGIN@34 |
| 35 | 2 | 20µs | 1 | 4µs | # spent 4µs within C4::Reserves::BEGIN@35 which was called:
# once (4µs+0s) by C4::Circulation::BEGIN@27 at line 35 # spent 4µs making 1 call to C4::Reserves::BEGIN@35 |
| 36 | 2 | 1.95ms | 2 | 56.0ms | # spent 55.9ms (5.26+50.6) within C4::Reserves::BEGIN@36 which was called:
# once (5.26ms+50.6ms) by C4::Circulation::BEGIN@27 at line 36 # spent 55.9ms making 1 call to C4::Reserves::BEGIN@36
# spent 114µs making 1 call to Exporter::import |
| 37 | 2 | 25µs | 2 | 50µs | # spent 30µs (9+21) within C4::Reserves::BEGIN@37 which was called:
# once (9µs+21µs) by C4::Circulation::BEGIN@27 at line 37 # spent 30µs making 1 call to C4::Reserves::BEGIN@37
# spent 20µs making 1 call to Exporter::import |
| 38 | 2 | 25µs | 2 | 44µs | # spent 26µs (9+18) within C4::Reserves::BEGIN@38 which was called:
# once (9µs+18µs) by C4::Circulation::BEGIN@27 at line 38 # spent 26µs making 1 call to C4::Reserves::BEGIN@38
# spent 18µs making 1 call to Exporter::import |
| 39 | |||||
| 40 | 2 | 22µs | 2 | 55µs | # spent 32µs (8+24) within C4::Reserves::BEGIN@40 which was called:
# once (8µs+24µs) by C4::Circulation::BEGIN@27 at line 40 # spent 32µs making 1 call to C4::Reserves::BEGIN@40
# spent 24µs making 1 call to Exporter::import |
| 41 | |||||
| 42 | 2 | 27µs | 2 | 44µs | # spent 27µs (9+18) within C4::Reserves::BEGIN@42 which was called:
# once (9µs+18µs) by C4::Circulation::BEGIN@27 at line 42 # spent 27µs making 1 call to C4::Reserves::BEGIN@42
# spent 18µs making 1 call to Exporter::import |
| 43 | |||||
| 44 | 2 | 91µs | 2 | 94µs | # spent 50µs (6+44) within C4::Reserves::BEGIN@44 which was called:
# once (6µs+44µs) by C4::Circulation::BEGIN@27 at line 44 # spent 50µs making 1 call to C4::Reserves::BEGIN@44
# spent 44µs making 1 call to vars::import |
| 45 | |||||
| 46 | =head1 NAME | ||||
| 47 | |||||
| 48 | C4::Reserves - Koha functions for dealing with reservation. | ||||
| 49 | |||||
| 50 | =head1 SYNOPSIS | ||||
| 51 | |||||
| 52 | use C4::Reserves; | ||||
| 53 | |||||
| 54 | =head1 DESCRIPTION | ||||
| 55 | |||||
| 56 | This modules provides somes functions to deal with reservations. | ||||
| 57 | |||||
| 58 | Reserves are stored in reserves table. | ||||
| 59 | The following columns contains important values : | ||||
| 60 | - priority >0 : then the reserve is at 1st stage, and not yet affected to any item. | ||||
| 61 | =0 : then the reserve is being dealed | ||||
| 62 | - found : NULL : means the patron requested the 1st available, and we haven't choosen the item | ||||
| 63 | T(ransit) : the reserve is linked to an item but is in transit to the pickup branch | ||||
| 64 | W(aiting) : the reserve is linked to an item, is at the pickup branch, and is waiting on the hold shelf | ||||
| 65 | F(inished) : the reserve has been completed, and is done | ||||
| 66 | - itemnumber : empty : the reserve is still unaffected to an item | ||||
| 67 | filled: the reserve is attached to an item | ||||
| 68 | The complete workflow is : | ||||
| 69 | ==== 1st use case ==== | ||||
| 70 | patron request a document, 1st available : P >0, F=NULL, I=NULL | ||||
| 71 | a library having it run "transfertodo", and clic on the list | ||||
| 72 | if there is no transfer to do, the reserve waiting | ||||
| 73 | patron can pick it up P =0, F=W, I=filled | ||||
| 74 | if there is a transfer to do, write in branchtransfer P =0, F=T, I=filled | ||||
| 75 | The pickup library recieve the book, it check in P =0, F=W, I=filled | ||||
| 76 | The patron borrow the book P =0, F=F, I=filled | ||||
| 77 | |||||
| 78 | ==== 2nd use case ==== | ||||
| 79 | patron requests a document, a given item, | ||||
| 80 | If pickup is holding branch P =0, F=W, I=filled | ||||
| 81 | If transfer needed, write in branchtransfer P =0, F=T, I=filled | ||||
| 82 | The pickup library receive the book, it checks it in P =0, F=W, I=filled | ||||
| 83 | The patron borrow the book P =0, F=F, I=filled | ||||
| 84 | |||||
| 85 | =head1 FUNCTIONS | ||||
| 86 | |||||
| 87 | =cut | ||||
| 88 | |||||
| 89 | # spent 11µs within C4::Reserves::BEGIN@89 which was called:
# once (11µs+0s) by C4::Circulation::BEGIN@27 at line 139 | ||||
| 90 | # set the version for version checking | ||||
| 91 | 1 | 900ns | $VERSION = 3.07.00.049; | ||
| 92 | 1 | 500ns | require Exporter; | ||
| 93 | 1 | 5µs | @ISA = qw(Exporter); | ||
| 94 | 1 | 2µs | @EXPORT = qw( | ||
| 95 | &AddReserve | ||||
| 96 | |||||
| 97 | &GetReserve | ||||
| 98 | &GetReservesFromItemnumber | ||||
| 99 | &GetReservesFromBiblionumber | ||||
| 100 | &GetReservesFromBorrowernumber | ||||
| 101 | &GetReservesForBranch | ||||
| 102 | &GetReservesToBranch | ||||
| 103 | &GetReserveCount | ||||
| 104 | &GetReserveFee | ||||
| 105 | &GetReserveInfo | ||||
| 106 | &GetReserveStatus | ||||
| 107 | |||||
| 108 | &GetOtherReserves | ||||
| 109 | |||||
| 110 | &ModReserveFill | ||||
| 111 | &ModReserveAffect | ||||
| 112 | &ModReserve | ||||
| 113 | &ModReserveStatus | ||||
| 114 | &ModReserveCancelAll | ||||
| 115 | &ModReserveMinusPriority | ||||
| 116 | &MoveReserve | ||||
| 117 | |||||
| 118 | &CheckReserves | ||||
| 119 | &CanBookBeReserved | ||||
| 120 | &CanItemBeReserved | ||||
| 121 | &CanReserveBeCanceledFromOpac | ||||
| 122 | &CancelReserve | ||||
| 123 | &CancelExpiredReserves | ||||
| 124 | |||||
| 125 | &AutoUnsuspendReserves | ||||
| 126 | |||||
| 127 | &IsAvailableForItemLevelRequest | ||||
| 128 | |||||
| 129 | &AlterPriority | ||||
| 130 | &ToggleLowestPriority | ||||
| 131 | |||||
| 132 | &ReserveSlip | ||||
| 133 | &ToggleSuspend | ||||
| 134 | &SuspendAll | ||||
| 135 | |||||
| 136 | &GetReservesControlBranch | ||||
| 137 | ); | ||||
| 138 | 1 | 4µs | @EXPORT_OK = qw( MergeHolds ); | ||
| 139 | 1 | 5.31ms | 1 | 11µs | } # spent 11µs making 1 call to C4::Reserves::BEGIN@89 |
| 140 | |||||
| 141 | =head2 AddReserve | ||||
| 142 | |||||
| 143 | AddReserve($branch,$borrowernumber,$biblionumber,$constraint,$bibitems,$priority,$resdate,$expdate,$notes,$title,$checkitem,$found) | ||||
| 144 | |||||
| 145 | =cut | ||||
| 146 | |||||
| 147 | sub AddReserve { | ||||
| 148 | my ( | ||||
| 149 | $branch, $borrowernumber, $biblionumber, | ||||
| 150 | $constraint, $bibitems, $priority, $resdate, $expdate, $notes, | ||||
| 151 | $title, $checkitem, $found | ||||
| 152 | ) = @_; | ||||
| 153 | my $fee = | ||||
| 154 | GetReserveFee($borrowernumber, $biblionumber, $constraint, | ||||
| 155 | $bibitems ); | ||||
| 156 | my $dbh = C4::Context->dbh; | ||||
| 157 | my $const = lc substr( $constraint, 0, 1 ); | ||||
| 158 | $resdate = format_date_in_iso( $resdate ) if ( $resdate ); | ||||
| 159 | $resdate = C4::Dates->today( 'iso' ) unless ( $resdate ); | ||||
| 160 | if ($expdate) { | ||||
| 161 | $expdate = format_date_in_iso( $expdate ); | ||||
| 162 | } else { | ||||
| 163 | undef $expdate; # make reserves.expirationdate default to null rather than '0000-00-00' | ||||
| 164 | } | ||||
| 165 | if ( C4::Context->preference( 'AllowHoldDateInFuture' ) ) { | ||||
| 166 | # Make room in reserves for this before those of a later reserve date | ||||
| 167 | $priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority ); | ||||
| 168 | } | ||||
| 169 | my $waitingdate; | ||||
| 170 | |||||
| 171 | # If the reserv had the waiting status, we had the value of the resdate | ||||
| 172 | if ( $found eq 'W' ) { | ||||
| 173 | $waitingdate = $resdate; | ||||
| 174 | } | ||||
| 175 | |||||
| 176 | #eval { | ||||
| 177 | # updates take place here | ||||
| 178 | if ( $fee > 0 ) { | ||||
| 179 | my $nextacctno = &getnextacctno( $borrowernumber ); | ||||
| 180 | my $query = qq/ | ||||
| 181 | INSERT INTO accountlines | ||||
| 182 | (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding) | ||||
| 183 | VALUES | ||||
| 184 | (?,?,now(),?,?,'Res',?) | ||||
| 185 | /; | ||||
| 186 | my $usth = $dbh->prepare($query); | ||||
| 187 | $usth->execute( $borrowernumber, $nextacctno, $fee, | ||||
| 188 | "Reserve Charge - $title", $fee ); | ||||
| 189 | } | ||||
| 190 | |||||
| 191 | #if ($const eq 'a'){ | ||||
| 192 | my $query = qq/ | ||||
| 193 | INSERT INTO reserves | ||||
| 194 | (borrowernumber,biblionumber,reservedate,branchcode,constrainttype, | ||||
| 195 | priority,reservenotes,itemnumber,found,waitingdate,expirationdate) | ||||
| 196 | VALUES | ||||
| 197 | (?,?,?,?,?, | ||||
| 198 | ?,?,?,?,?,?) | ||||
| 199 | /; | ||||
| 200 | my $sth = $dbh->prepare($query); | ||||
| 201 | $sth->execute( | ||||
| 202 | $borrowernumber, $biblionumber, $resdate, $branch, | ||||
| 203 | $const, $priority, $notes, $checkitem, | ||||
| 204 | $found, $waitingdate, $expdate | ||||
| 205 | ); | ||||
| 206 | |||||
| 207 | # Send e-mail to librarian if syspref is active | ||||
| 208 | if(C4::Context->preference("emailLibrarianWhenHoldIsPlaced")){ | ||||
| 209 | my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber); | ||||
| 210 | my $branch_details = C4::Branch::GetBranchDetail($borrower->{branchcode}); | ||||
| 211 | if ( my $letter = C4::Letters::GetPreparedLetter ( | ||||
| 212 | module => 'reserves', | ||||
| 213 | letter_code => 'HOLDPLACED', | ||||
| 214 | branchcode => $branch, | ||||
| 215 | tables => { | ||||
| 216 | 'branches' => $branch_details, | ||||
| 217 | 'borrowers' => $borrower, | ||||
| 218 | 'biblio' => $biblionumber, | ||||
| 219 | 'items' => $checkitem, | ||||
| 220 | }, | ||||
| 221 | ) ) { | ||||
| 222 | |||||
| 223 | my $admin_email_address =$branch_details->{'branchemail'} || C4::Context->preference('KohaAdminEmailAddress'); | ||||
| 224 | |||||
| 225 | C4::Letters::EnqueueLetter( | ||||
| 226 | { letter => $letter, | ||||
| 227 | borrowernumber => $borrowernumber, | ||||
| 228 | message_transport_type => 'email', | ||||
| 229 | from_address => $admin_email_address, | ||||
| 230 | to_address => $admin_email_address, | ||||
| 231 | } | ||||
| 232 | ); | ||||
| 233 | } | ||||
| 234 | } | ||||
| 235 | |||||
| 236 | #} | ||||
| 237 | ($const eq "o" || $const eq "e") or return; # FIXME: why not have a useful return value? | ||||
| 238 | $query = qq/ | ||||
| 239 | INSERT INTO reserveconstraints | ||||
| 240 | (borrowernumber,biblionumber,reservedate,biblioitemnumber) | ||||
| 241 | VALUES | ||||
| 242 | (?,?,?,?) | ||||
| 243 | /; | ||||
| 244 | $sth = $dbh->prepare($query); # keep prepare outside the loop! | ||||
| 245 | foreach (@$bibitems) { | ||||
| 246 | $sth->execute($borrowernumber, $biblionumber, $resdate, $_); | ||||
| 247 | } | ||||
| 248 | |||||
| 249 | return; # FIXME: why not have a useful return value? | ||||
| 250 | } | ||||
| 251 | |||||
| 252 | =head2 GetReserve | ||||
| 253 | |||||
| 254 | $res = GetReserve( $reserve_id ); | ||||
| 255 | |||||
| 256 | Return the current reserve. | ||||
| 257 | |||||
| 258 | =cut | ||||
| 259 | |||||
| 260 | sub GetReserve { | ||||
| 261 | my ($reserve_id) = @_; | ||||
| 262 | |||||
| 263 | my $dbh = C4::Context->dbh; | ||||
| 264 | my $query = "SELECT * FROM reserves WHERE reserve_id = ?"; | ||||
| 265 | my $sth = $dbh->prepare( $query ); | ||||
| 266 | $sth->execute( $reserve_id ); | ||||
| 267 | return $sth->fetchrow_hashref(); | ||||
| 268 | } | ||||
| 269 | |||||
| 270 | =head2 GetReservesFromBiblionumber | ||||
| 271 | |||||
| 272 | my $reserves = GetReservesFromBiblionumber({ | ||||
| 273 | biblionumber => $biblionumber, | ||||
| 274 | [ itemnumber => $itemnumber, ] | ||||
| 275 | [ all_dates => 1|0 ] | ||||
| 276 | }); | ||||
| 277 | |||||
| 278 | This function gets the list of reservations for one C<$biblionumber>, | ||||
| 279 | returning an arrayref pointing to the reserves for C<$biblionumber>. | ||||
| 280 | |||||
| 281 | By default, only reserves whose start date falls before the current | ||||
| 282 | time are returned. To return all reserves, including future ones, | ||||
| 283 | the C<all_dates> parameter can be included and set to a true value. | ||||
| 284 | |||||
| 285 | If the C<itemnumber> parameter is supplied, reserves must be targeted | ||||
| 286 | to that item or not targeted to any item at all; otherwise, they | ||||
| 287 | are excluded from the list. | ||||
| 288 | |||||
| 289 | =cut | ||||
| 290 | |||||
| 291 | sub GetReservesFromBiblionumber { | ||||
| 292 | my ( $params ) = @_; | ||||
| 293 | my $biblionumber = $params->{biblionumber} or return []; | ||||
| 294 | my $itemnumber = $params->{itemnumber}; | ||||
| 295 | my $all_dates = $params->{all_dates} // 0; | ||||
| 296 | my $dbh = C4::Context->dbh; | ||||
| 297 | |||||
| 298 | # Find the desired items in the reserves | ||||
| 299 | my @params; | ||||
| 300 | my $query = " | ||||
| 301 | SELECT reserve_id, | ||||
| 302 | branchcode, | ||||
| 303 | timestamp AS rtimestamp, | ||||
| 304 | priority, | ||||
| 305 | biblionumber, | ||||
| 306 | borrowernumber, | ||||
| 307 | reservedate, | ||||
| 308 | constrainttype, | ||||
| 309 | found, | ||||
| 310 | itemnumber, | ||||
| 311 | reservenotes, | ||||
| 312 | expirationdate, | ||||
| 313 | lowestPriority, | ||||
| 314 | suspend, | ||||
| 315 | suspend_until | ||||
| 316 | FROM reserves | ||||
| 317 | WHERE biblionumber = ? "; | ||||
| 318 | push( @params, $biblionumber ); | ||||
| 319 | unless ( $all_dates ) { | ||||
| 320 | $query .= " AND reservedate <= CAST(NOW() AS DATE) "; | ||||
| 321 | } | ||||
| 322 | if ( $itemnumber ) { | ||||
| 323 | $query .= " AND ( itemnumber IS NULL OR itemnumber = ? )"; | ||||
| 324 | push( @params, $itemnumber ); | ||||
| 325 | } | ||||
| 326 | $query .= "ORDER BY priority"; | ||||
| 327 | my $sth = $dbh->prepare($query); | ||||
| 328 | $sth->execute( @params ); | ||||
| 329 | my @results; | ||||
| 330 | my $i = 0; | ||||
| 331 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
| 332 | |||||
| 333 | # FIXME - What is this doing? How do constraints work? | ||||
| 334 | if ($data->{constrainttype} eq 'o') { | ||||
| 335 | $query = ' | ||||
| 336 | SELECT biblioitemnumber | ||||
| 337 | FROM reserveconstraints | ||||
| 338 | WHERE biblionumber = ? | ||||
| 339 | AND borrowernumber = ? | ||||
| 340 | AND reservedate = ? | ||||
| 341 | '; | ||||
| 342 | my $csth = $dbh->prepare($query); | ||||
| 343 | $csth->execute($data->{biblionumber}, $data->{borrowernumber}, $data->{reservedate}); | ||||
| 344 | my @bibitemno; | ||||
| 345 | while ( my $bibitemnos = $csth->fetchrow_array ) { | ||||
| 346 | push( @bibitemno, $bibitemnos ); # FIXME: inefficient: use fetchall_arrayref | ||||
| 347 | } | ||||
| 348 | my $count = scalar @bibitemno; | ||||
| 349 | |||||
| 350 | # if we have two or more different specific itemtypes | ||||
| 351 | # reserved by same person on same day | ||||
| 352 | my $bdata; | ||||
| 353 | if ( $count > 1 ) { | ||||
| 354 | $bdata = GetBiblioItemData( $bibitemno[$i] ); # FIXME: This doesn't make sense. | ||||
| 355 | $i++; # $i can increase each pass, but the next @bibitemno might be smaller? | ||||
| 356 | } | ||||
| 357 | else { | ||||
| 358 | # Look up the book we just found. | ||||
| 359 | $bdata = GetBiblioItemData( $bibitemno[0] ); | ||||
| 360 | } | ||||
| 361 | # Add the results of this latest search to the current | ||||
| 362 | # results. | ||||
| 363 | # FIXME - An 'each' would probably be more efficient. | ||||
| 364 | foreach my $key ( keys %$bdata ) { | ||||
| 365 | $data->{$key} = $bdata->{$key}; | ||||
| 366 | } | ||||
| 367 | } | ||||
| 368 | push @results, $data; | ||||
| 369 | } | ||||
| 370 | return \@results; | ||||
| 371 | } | ||||
| 372 | |||||
| 373 | =head2 GetReservesFromItemnumber | ||||
| 374 | |||||
| 375 | ( $reservedate, $borrowernumber, $branchcode, $reserve_id, $waitingdate ) = GetReservesFromItemnumber($itemnumber); | ||||
| 376 | |||||
| 377 | Get the first reserve for a specific item number (based on priority). Returns the abovementioned values for that reserve. | ||||
| 378 | |||||
| 379 | The routine does not look at future reserves (read: item level holds), but DOES include future waits (a confirmed future hold). | ||||
| 380 | |||||
| 381 | =cut | ||||
| 382 | |||||
| 383 | sub GetReservesFromItemnumber { | ||||
| 384 | my ( $itemnumber ) = @_; | ||||
| 385 | my $dbh = C4::Context->dbh; | ||||
| 386 | my $query = " | ||||
| 387 | SELECT reservedate,borrowernumber,branchcode,reserve_id,waitingdate | ||||
| 388 | FROM reserves | ||||
| 389 | WHERE itemnumber=? AND ( reservedate <= CAST(now() AS date) OR | ||||
| 390 | waitingdate IS NOT NULL ) | ||||
| 391 | ORDER BY priority | ||||
| 392 | "; | ||||
| 393 | my $sth_res = $dbh->prepare($query); | ||||
| 394 | $sth_res->execute($itemnumber); | ||||
| 395 | my ( $reservedate, $borrowernumber,$branchcode, $reserve_id, $wait ) = $sth_res->fetchrow_array; | ||||
| 396 | return ( $reservedate, $borrowernumber, $branchcode, $reserve_id, $wait ); | ||||
| 397 | } | ||||
| 398 | |||||
| 399 | =head2 GetReservesFromBorrowernumber | ||||
| 400 | |||||
| 401 | $borrowerreserv = GetReservesFromBorrowernumber($borrowernumber,$tatus); | ||||
| 402 | |||||
| 403 | TODO :: Descritpion | ||||
| 404 | |||||
| 405 | =cut | ||||
| 406 | |||||
| 407 | sub GetReservesFromBorrowernumber { | ||||
| 408 | my ( $borrowernumber, $status ) = @_; | ||||
| 409 | my $dbh = C4::Context->dbh; | ||||
| 410 | my $sth; | ||||
| 411 | if ($status) { | ||||
| 412 | $sth = $dbh->prepare(" | ||||
| 413 | SELECT * | ||||
| 414 | FROM reserves | ||||
| 415 | WHERE borrowernumber=? | ||||
| 416 | AND found =? | ||||
| 417 | ORDER BY reservedate | ||||
| 418 | "); | ||||
| 419 | $sth->execute($borrowernumber,$status); | ||||
| 420 | } else { | ||||
| 421 | $sth = $dbh->prepare(" | ||||
| 422 | SELECT * | ||||
| 423 | FROM reserves | ||||
| 424 | WHERE borrowernumber=? | ||||
| 425 | ORDER BY reservedate | ||||
| 426 | "); | ||||
| 427 | $sth->execute($borrowernumber); | ||||
| 428 | } | ||||
| 429 | my $data = $sth->fetchall_arrayref({}); | ||||
| 430 | return @$data; | ||||
| 431 | } | ||||
| 432 | #------------------------------------------------------------------------------------- | ||||
| 433 | =head2 CanBookBeReserved | ||||
| 434 | |||||
| 435 | $error = &CanBookBeReserved($borrowernumber, $biblionumber) | ||||
| 436 | |||||
| 437 | =cut | ||||
| 438 | |||||
| 439 | sub CanBookBeReserved{ | ||||
| 440 | my ($borrowernumber, $biblionumber) = @_; | ||||
| 441 | |||||
| 442 | my $items = GetItemnumbersForBiblio($biblionumber); | ||||
| 443 | #get items linked via host records | ||||
| 444 | my @hostitems = get_hostitemnumbers_of($biblionumber); | ||||
| 445 | if (@hostitems){ | ||||
| 446 | push (@$items,@hostitems); | ||||
| 447 | } | ||||
| 448 | |||||
| 449 | foreach my $item (@$items){ | ||||
| 450 | return 1 if CanItemBeReserved($borrowernumber, $item); | ||||
| 451 | } | ||||
| 452 | return 0; | ||||
| 453 | } | ||||
| 454 | |||||
| 455 | =head2 CanItemBeReserved | ||||
| 456 | |||||
| 457 | $error = &CanItemBeReserved($borrowernumber, $itemnumber) | ||||
| 458 | |||||
| 459 | This function return 1 if an item can be issued by this borrower. | ||||
| 460 | |||||
| 461 | =cut | ||||
| 462 | |||||
| 463 | sub CanItemBeReserved{ | ||||
| 464 | my ($borrowernumber, $itemnumber) = @_; | ||||
| 465 | |||||
| 466 | my $dbh = C4::Context->dbh; | ||||
| 467 | my $ruleitemtype; # itemtype of the matching issuing rule | ||||
| 468 | my $allowedreserves = 0; | ||||
| 469 | |||||
| 470 | # we retrieve borrowers and items informations # | ||||
| 471 | # item->{itype} will come for biblioitems if necessery | ||||
| 472 | my $item = GetItem($itemnumber); | ||||
| 473 | |||||
| 474 | # If an item is damaged and we don't allow holds on damaged items, we can stop right here | ||||
| 475 | return 0 if ( $item->{damaged} && !C4::Context->preference('AllowHoldsOnDamagedItems') ); | ||||
| 476 | |||||
| 477 | my $borrower = C4::Members::GetMember('borrowernumber'=>$borrowernumber); | ||||
| 478 | |||||
| 479 | my $controlbranch = C4::Context->preference('ReservesControlBranch'); | ||||
| 480 | my $itemtypefield = C4::Context->preference('item-level_itypes') ? "itype" : "itemtype"; | ||||
| 481 | |||||
| 482 | # we retrieve user rights on this itemtype and branchcode | ||||
| 483 | my $sth = $dbh->prepare("SELECT categorycode, itemtype, branchcode, reservesallowed | ||||
| 484 | FROM issuingrules | ||||
| 485 | WHERE (categorycode in (?,'*') ) | ||||
| 486 | AND (itemtype IN (?,'*')) | ||||
| 487 | AND (branchcode IN (?,'*')) | ||||
| 488 | ORDER BY | ||||
| 489 | categorycode DESC, | ||||
| 490 | itemtype DESC, | ||||
| 491 | branchcode DESC;" | ||||
| 492 | ); | ||||
| 493 | |||||
| 494 | my $querycount ="SELECT | ||||
| 495 | count(*) as count | ||||
| 496 | FROM reserves | ||||
| 497 | LEFT JOIN items USING (itemnumber) | ||||
| 498 | LEFT JOIN biblioitems ON (reserves.biblionumber=biblioitems.biblionumber) | ||||
| 499 | LEFT JOIN borrowers USING (borrowernumber) | ||||
| 500 | WHERE borrowernumber = ? | ||||
| 501 | "; | ||||
| 502 | |||||
| 503 | |||||
| 504 | my $branchcode = ""; | ||||
| 505 | my $branchfield = "reserves.branchcode"; | ||||
| 506 | |||||
| 507 | if( $controlbranch eq "ItemHomeLibrary" ){ | ||||
| 508 | $branchfield = "items.homebranch"; | ||||
| 509 | $branchcode = $item->{homebranch}; | ||||
| 510 | }elsif( $controlbranch eq "PatronLibrary" ){ | ||||
| 511 | $branchfield = "borrowers.branchcode"; | ||||
| 512 | $branchcode = $borrower->{branchcode}; | ||||
| 513 | } | ||||
| 514 | |||||
| 515 | # we retrieve rights | ||||
| 516 | $sth->execute($borrower->{'categorycode'}, $item->{'itype'}, $branchcode); | ||||
| 517 | if(my $rights = $sth->fetchrow_hashref()){ | ||||
| 518 | $ruleitemtype = $rights->{itemtype}; | ||||
| 519 | $allowedreserves = $rights->{reservesallowed}; | ||||
| 520 | }else{ | ||||
| 521 | $ruleitemtype = '*'; | ||||
| 522 | } | ||||
| 523 | |||||
| 524 | # we retrieve count | ||||
| 525 | |||||
| 526 | $querycount .= "AND $branchfield = ?"; | ||||
| 527 | |||||
| 528 | $querycount .= " AND $itemtypefield = ?" if ($ruleitemtype ne "*"); | ||||
| 529 | my $sthcount = $dbh->prepare($querycount); | ||||
| 530 | |||||
| 531 | if($ruleitemtype eq "*"){ | ||||
| 532 | $sthcount->execute($borrowernumber, $branchcode); | ||||
| 533 | }else{ | ||||
| 534 | $sthcount->execute($borrowernumber, $branchcode, $ruleitemtype); | ||||
| 535 | } | ||||
| 536 | |||||
| 537 | my $reservecount = "0"; | ||||
| 538 | if(my $rowcount = $sthcount->fetchrow_hashref()){ | ||||
| 539 | $reservecount = $rowcount->{count}; | ||||
| 540 | } | ||||
| 541 | |||||
| 542 | # we check if it's ok or not | ||||
| 543 | if( $reservecount >= $allowedreserves ){ | ||||
| 544 | return 0; | ||||
| 545 | } | ||||
| 546 | |||||
| 547 | # If reservecount is ok, we check item branch if IndependentBranches is ON | ||||
| 548 | # and canreservefromotherbranches is OFF | ||||
| 549 | if ( C4::Context->preference('IndependentBranches') | ||||
| 550 | and !C4::Context->preference('canreservefromotherbranches') ) | ||||
| 551 | { | ||||
| 552 | my $itembranch = $item->{homebranch}; | ||||
| 553 | if ($itembranch ne $borrower->{branchcode}) { | ||||
| 554 | return 0; | ||||
| 555 | } | ||||
| 556 | } | ||||
| 557 | |||||
| 558 | return 1; | ||||
| 559 | } | ||||
| 560 | |||||
| 561 | =head2 CanReserveBeCanceledFromOpac | ||||
| 562 | |||||
| 563 | $number = CanReserveBeCanceledFromOpac($reserve_id, $borrowernumber); | ||||
| 564 | |||||
| 565 | returns 1 if reserve can be cancelled by user from OPAC. | ||||
| 566 | First check if reserve belongs to user, next checks if reserve is not in | ||||
| 567 | transfer or waiting status | ||||
| 568 | |||||
| 569 | =cut | ||||
| 570 | |||||
| 571 | sub CanReserveBeCanceledFromOpac { | ||||
| 572 | my ($reserve_id, $borrowernumber) = @_; | ||||
| 573 | |||||
| 574 | return unless $reserve_id and $borrowernumber; | ||||
| 575 | my $reserve = GetReserve($reserve_id); | ||||
| 576 | |||||
| 577 | return 0 unless $reserve->{borrowernumber} == $borrowernumber; | ||||
| 578 | return 0 if ( $reserve->{found} eq 'W' ) or ( $reserve->{found} eq 'T' ); | ||||
| 579 | |||||
| 580 | return 1; | ||||
| 581 | |||||
| 582 | } | ||||
| 583 | |||||
| 584 | #-------------------------------------------------------------------------------- | ||||
| 585 | =head2 GetReserveCount | ||||
| 586 | |||||
| 587 | $number = &GetReserveCount($borrowernumber); | ||||
| 588 | |||||
| 589 | this function returns the number of reservation for a borrower given on input arg. | ||||
| 590 | |||||
| 591 | =cut | ||||
| 592 | |||||
| 593 | sub GetReserveCount { | ||||
| 594 | my ($borrowernumber) = @_; | ||||
| 595 | |||||
| 596 | my $dbh = C4::Context->dbh; | ||||
| 597 | |||||
| 598 | my $query = " | ||||
| 599 | SELECT COUNT(*) AS counter | ||||
| 600 | FROM reserves | ||||
| 601 | WHERE borrowernumber = ? | ||||
| 602 | "; | ||||
| 603 | my $sth = $dbh->prepare($query); | ||||
| 604 | $sth->execute($borrowernumber); | ||||
| 605 | my $row = $sth->fetchrow_hashref; | ||||
| 606 | return $row->{counter}; | ||||
| 607 | } | ||||
| 608 | |||||
| 609 | =head2 GetOtherReserves | ||||
| 610 | |||||
| 611 | ($messages,$nextreservinfo)=$GetOtherReserves(itemnumber); | ||||
| 612 | |||||
| 613 | Check queued list of this document and check if this document must be transfered | ||||
| 614 | |||||
| 615 | =cut | ||||
| 616 | |||||
| 617 | sub GetOtherReserves { | ||||
| 618 | my ($itemnumber) = @_; | ||||
| 619 | my $messages; | ||||
| 620 | my $nextreservinfo; | ||||
| 621 | my ( undef, $checkreserves, undef ) = CheckReserves($itemnumber); | ||||
| 622 | if ($checkreserves) { | ||||
| 623 | my $iteminfo = GetItem($itemnumber); | ||||
| 624 | if ( $iteminfo->{'holdingbranch'} ne $checkreserves->{'branchcode'} ) { | ||||
| 625 | $messages->{'transfert'} = $checkreserves->{'branchcode'}; | ||||
| 626 | #minus priorities of others reservs | ||||
| 627 | ModReserveMinusPriority( | ||||
| 628 | $itemnumber, | ||||
| 629 | $checkreserves->{'reserve_id'}, | ||||
| 630 | ); | ||||
| 631 | |||||
| 632 | #launch the subroutine dotransfer | ||||
| 633 | C4::Items::ModItemTransfer( | ||||
| 634 | $itemnumber, | ||||
| 635 | $iteminfo->{'holdingbranch'}, | ||||
| 636 | $checkreserves->{'branchcode'} | ||||
| 637 | ), | ||||
| 638 | ; | ||||
| 639 | } | ||||
| 640 | |||||
| 641 | #step 2b : case of a reservation on the same branch, set the waiting status | ||||
| 642 | else { | ||||
| 643 | $messages->{'waiting'} = 1; | ||||
| 644 | ModReserveMinusPriority( | ||||
| 645 | $itemnumber, | ||||
| 646 | $checkreserves->{'reserve_id'}, | ||||
| 647 | ); | ||||
| 648 | ModReserveStatus($itemnumber,'W'); | ||||
| 649 | } | ||||
| 650 | |||||
| 651 | $nextreservinfo = $checkreserves->{'borrowernumber'}; | ||||
| 652 | } | ||||
| 653 | |||||
| 654 | return ( $messages, $nextreservinfo ); | ||||
| 655 | } | ||||
| 656 | |||||
| 657 | =head2 GetReserveFee | ||||
| 658 | |||||
| 659 | $fee = GetReserveFee($borrowernumber,$biblionumber,$constraint,$biblionumber); | ||||
| 660 | |||||
| 661 | Calculate the fee for a reserve | ||||
| 662 | |||||
| 663 | =cut | ||||
| 664 | |||||
| 665 | sub GetReserveFee { | ||||
| 666 | my ($borrowernumber, $biblionumber, $constraint, $bibitems ) = @_; | ||||
| 667 | |||||
| 668 | #check for issues; | ||||
| 669 | my $dbh = C4::Context->dbh; | ||||
| 670 | my $const = lc substr( $constraint, 0, 1 ); | ||||
| 671 | my $query = qq/ | ||||
| 672 | SELECT * FROM borrowers | ||||
| 673 | LEFT JOIN categories ON borrowers.categorycode = categories.categorycode | ||||
| 674 | WHERE borrowernumber = ? | ||||
| 675 | /; | ||||
| 676 | my $sth = $dbh->prepare($query); | ||||
| 677 | $sth->execute($borrowernumber); | ||||
| 678 | my $data = $sth->fetchrow_hashref; | ||||
| 679 | my $fee = $data->{'reservefee'}; | ||||
| 680 | my $cntitems = @- > $bibitems; | ||||
| 681 | |||||
| 682 | if ( $fee > 0 ) { | ||||
| 683 | |||||
| 684 | # check for items on issue | ||||
| 685 | # first find biblioitem records | ||||
| 686 | my @biblioitems; | ||||
| 687 | my $sth1 = $dbh->prepare( | ||||
| 688 | "SELECT * FROM biblio LEFT JOIN biblioitems on biblio.biblionumber = biblioitems.biblionumber | ||||
| 689 | WHERE (biblio.biblionumber = ?)" | ||||
| 690 | ); | ||||
| 691 | $sth1->execute($biblionumber); | ||||
| 692 | while ( my $data1 = $sth1->fetchrow_hashref ) { | ||||
| 693 | if ( $const eq "a" ) { | ||||
| 694 | push @biblioitems, $data1; | ||||
| 695 | } | ||||
| 696 | else { | ||||
| 697 | my $found = 0; | ||||
| 698 | my $x = 0; | ||||
| 699 | while ( $x < $cntitems ) { | ||||
| 700 | if ( @$bibitems->{'biblioitemnumber'} == | ||||
| 701 | $data->{'biblioitemnumber'} ) | ||||
| 702 | { | ||||
| 703 | $found = 1; | ||||
| 704 | } | ||||
| 705 | $x++; | ||||
| 706 | } | ||||
| 707 | if ( $const eq 'o' ) { | ||||
| 708 | if ( $found == 1 ) { | ||||
| 709 | push @biblioitems, $data1; | ||||
| 710 | } | ||||
| 711 | } | ||||
| 712 | else { | ||||
| 713 | if ( $found == 0 ) { | ||||
| 714 | push @biblioitems, $data1; | ||||
| 715 | } | ||||
| 716 | } | ||||
| 717 | } | ||||
| 718 | } | ||||
| 719 | my $cntitemsfound = @biblioitems; | ||||
| 720 | my $issues = 0; | ||||
| 721 | my $x = 0; | ||||
| 722 | my $allissued = 1; | ||||
| 723 | while ( $x < $cntitemsfound ) { | ||||
| 724 | my $bitdata = $biblioitems[$x]; | ||||
| 725 | my $sth2 = $dbh->prepare( | ||||
| 726 | "SELECT * FROM items | ||||
| 727 | WHERE biblioitemnumber = ?" | ||||
| 728 | ); | ||||
| 729 | $sth2->execute( $bitdata->{'biblioitemnumber'} ); | ||||
| 730 | while ( my $itdata = $sth2->fetchrow_hashref ) { | ||||
| 731 | my $sth3 = $dbh->prepare( | ||||
| 732 | "SELECT * FROM issues | ||||
| 733 | WHERE itemnumber = ?" | ||||
| 734 | ); | ||||
| 735 | $sth3->execute( $itdata->{'itemnumber'} ); | ||||
| 736 | if ( my $isdata = $sth3->fetchrow_hashref ) { | ||||
| 737 | } | ||||
| 738 | else { | ||||
| 739 | $allissued = 0; | ||||
| 740 | } | ||||
| 741 | } | ||||
| 742 | $x++; | ||||
| 743 | } | ||||
| 744 | if ( $allissued == 0 ) { | ||||
| 745 | my $rsth = | ||||
| 746 | $dbh->prepare("SELECT * FROM reserves WHERE biblionumber = ?"); | ||||
| 747 | $rsth->execute($biblionumber); | ||||
| 748 | if ( my $rdata = $rsth->fetchrow_hashref ) { | ||||
| 749 | } | ||||
| 750 | else { | ||||
| 751 | $fee = 0; | ||||
| 752 | } | ||||
| 753 | } | ||||
| 754 | } | ||||
| 755 | return $fee; | ||||
| 756 | } | ||||
| 757 | |||||
| 758 | =head2 GetReservesToBranch | ||||
| 759 | |||||
| 760 | @transreserv = GetReservesToBranch( $frombranch ); | ||||
| 761 | |||||
| 762 | Get reserve list for a given branch | ||||
| 763 | |||||
| 764 | =cut | ||||
| 765 | |||||
| 766 | sub GetReservesToBranch { | ||||
| 767 | my ( $frombranch ) = @_; | ||||
| 768 | my $dbh = C4::Context->dbh; | ||||
| 769 | my $sth = $dbh->prepare( | ||||
| 770 | "SELECT reserve_id,borrowernumber,reservedate,itemnumber,timestamp | ||||
| 771 | FROM reserves | ||||
| 772 | WHERE priority='0' | ||||
| 773 | AND branchcode=?" | ||||
| 774 | ); | ||||
| 775 | $sth->execute( $frombranch ); | ||||
| 776 | my @transreserv; | ||||
| 777 | my $i = 0; | ||||
| 778 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
| 779 | $transreserv[$i] = $data; | ||||
| 780 | $i++; | ||||
| 781 | } | ||||
| 782 | return (@transreserv); | ||||
| 783 | } | ||||
| 784 | |||||
| 785 | =head2 GetReservesForBranch | ||||
| 786 | |||||
| 787 | @transreserv = GetReservesForBranch($frombranch); | ||||
| 788 | |||||
| 789 | =cut | ||||
| 790 | |||||
| 791 | sub GetReservesForBranch { | ||||
| 792 | my ($frombranch) = @_; | ||||
| 793 | my $dbh = C4::Context->dbh; | ||||
| 794 | |||||
| 795 | my $query = " | ||||
| 796 | SELECT reserve_id,borrowernumber,reservedate,itemnumber,waitingdate | ||||
| 797 | FROM reserves | ||||
| 798 | WHERE priority='0' | ||||
| 799 | AND found='W' | ||||
| 800 | "; | ||||
| 801 | $query .= " AND branchcode=? " if ( $frombranch ); | ||||
| 802 | $query .= "ORDER BY waitingdate" ; | ||||
| 803 | |||||
| 804 | my $sth = $dbh->prepare($query); | ||||
| 805 | if ($frombranch){ | ||||
| 806 | $sth->execute($frombranch); | ||||
| 807 | } else { | ||||
| 808 | $sth->execute(); | ||||
| 809 | } | ||||
| 810 | |||||
| 811 | my @transreserv; | ||||
| 812 | my $i = 0; | ||||
| 813 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
| 814 | $transreserv[$i] = $data; | ||||
| 815 | $i++; | ||||
| 816 | } | ||||
| 817 | return (@transreserv); | ||||
| 818 | } | ||||
| 819 | |||||
| 820 | =head2 GetReserveStatus | ||||
| 821 | |||||
| 822 | $reservestatus = GetReserveStatus($itemnumber, $biblionumber); | ||||
| 823 | |||||
| 824 | Take an itemnumber or a biblionumber and return the status of the reserve places on it. | ||||
| 825 | If several reserves exist, the reserve with the lower priority is given. | ||||
| 826 | |||||
| 827 | =cut | ||||
| 828 | |||||
| 829 | ## FIXME: I don't think this does what it thinks it does. | ||||
| 830 | ## It only ever checks the first reserve result, even though | ||||
| 831 | ## multiple reserves for that bib can have the itemnumber set | ||||
| 832 | ## the sub is only used once in the codebase. | ||||
| 833 | sub GetReserveStatus { | ||||
| 834 | my ($itemnumber, $biblionumber) = @_; | ||||
| 835 | |||||
| 836 | my $dbh = C4::Context->dbh; | ||||
| 837 | |||||
| 838 | my ($sth, $found, $priority); | ||||
| 839 | if ( $itemnumber ) { | ||||
| 840 | $sth = $dbh->prepare("SELECT found, priority FROM reserves WHERE itemnumber = ? order by priority LIMIT 1"); | ||||
| 841 | $sth->execute($itemnumber); | ||||
| 842 | ($found, $priority) = $sth->fetchrow_array; | ||||
| 843 | } | ||||
| 844 | |||||
| 845 | if ( $biblionumber and not defined $found and not defined $priority ) { | ||||
| 846 | $sth = $dbh->prepare("SELECT found, priority FROM reserves WHERE biblionumber = ? order by priority LIMIT 1"); | ||||
| 847 | $sth->execute($biblionumber); | ||||
| 848 | ($found, $priority) = $sth->fetchrow_array; | ||||
| 849 | } | ||||
| 850 | |||||
| 851 | if(defined $found) { | ||||
| 852 | return 'Waiting' if $found eq 'W' and $priority == 0; | ||||
| 853 | return 'Finished' if $found eq 'F'; | ||||
| 854 | } | ||||
| 855 | |||||
| 856 | return 'Reserved' if $priority > 0; | ||||
| 857 | |||||
| 858 | return ''; # empty string here will remove need for checking undef, or less log lines | ||||
| 859 | } | ||||
| 860 | |||||
| 861 | =head2 CheckReserves | ||||
| 862 | |||||
| 863 | ($status, $reserve, $all_reserves) = &CheckReserves($itemnumber); | ||||
| 864 | ($status, $reserve, $all_reserves) = &CheckReserves(undef, $barcode); | ||||
| 865 | ($status, $reserve, $all_reserves) = &CheckReserves($itemnumber,undef,$lookahead); | ||||
| 866 | |||||
| 867 | Find a book in the reserves. | ||||
| 868 | |||||
| 869 | C<$itemnumber> is the book's item number. | ||||
| 870 | C<$lookahead> is the number of days to look in advance for future reserves. | ||||
| 871 | |||||
| 872 | As I understand it, C<&CheckReserves> looks for the given item in the | ||||
| 873 | reserves. If it is found, that's a match, and C<$status> is set to | ||||
| 874 | C<Waiting>. | ||||
| 875 | |||||
| 876 | Otherwise, it finds the most important item in the reserves with the | ||||
| 877 | same biblio number as this book (I'm not clear on this) and returns it | ||||
| 878 | with C<$status> set to C<Reserved>. | ||||
| 879 | |||||
| 880 | C<&CheckReserves> returns a two-element list: | ||||
| 881 | |||||
| 882 | C<$status> is either C<Waiting>, C<Reserved> (see above), or 0. | ||||
| 883 | |||||
| 884 | C<$reserve> is the reserve item that matched. It is a | ||||
| 885 | reference-to-hash whose keys are mostly the fields of the reserves | ||||
| 886 | table in the Koha database. | ||||
| 887 | |||||
| 888 | =cut | ||||
| 889 | |||||
| 890 | sub CheckReserves { | ||||
| 891 | my ( $item, $barcode, $lookahead_days) = @_; | ||||
| 892 | my $dbh = C4::Context->dbh; | ||||
| 893 | my $sth; | ||||
| 894 | my $select; | ||||
| 895 | if (C4::Context->preference('item-level_itypes')){ | ||||
| 896 | $select = " | ||||
| 897 | SELECT items.biblionumber, | ||||
| 898 | items.biblioitemnumber, | ||||
| 899 | itemtypes.notforloan, | ||||
| 900 | items.notforloan AS itemnotforloan, | ||||
| 901 | items.itemnumber, | ||||
| 902 | items.damaged | ||||
| 903 | FROM items | ||||
| 904 | LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber | ||||
| 905 | LEFT JOIN itemtypes ON items.itype = itemtypes.itemtype | ||||
| 906 | "; | ||||
| 907 | } | ||||
| 908 | else { | ||||
| 909 | $select = " | ||||
| 910 | SELECT items.biblionumber, | ||||
| 911 | items.biblioitemnumber, | ||||
| 912 | itemtypes.notforloan, | ||||
| 913 | items.notforloan AS itemnotforloan, | ||||
| 914 | items.itemnumber, | ||||
| 915 | items.damaged | ||||
| 916 | FROM items | ||||
| 917 | LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber | ||||
| 918 | LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype | ||||
| 919 | "; | ||||
| 920 | } | ||||
| 921 | |||||
| 922 | if ($item) { | ||||
| 923 | $sth = $dbh->prepare("$select WHERE itemnumber = ?"); | ||||
| 924 | $sth->execute($item); | ||||
| 925 | } | ||||
| 926 | else { | ||||
| 927 | $sth = $dbh->prepare("$select WHERE barcode = ?"); | ||||
| 928 | $sth->execute($barcode); | ||||
| 929 | } | ||||
| 930 | # note: we get the itemnumber because we might have started w/ just the barcode. Now we know for sure we have it. | ||||
| 931 | my ( $biblio, $bibitem, $notforloan_per_itemtype, $notforloan_per_item, $itemnumber, $damaged ) = $sth->fetchrow_array; | ||||
| 932 | |||||
| 933 | return if ( $damaged && !C4::Context->preference('AllowHoldsOnDamagedItems') ); | ||||
| 934 | |||||
| 935 | return unless $itemnumber; # bail if we got nothing. | ||||
| 936 | |||||
| 937 | # if item is not for loan it cannot be reserved either..... | ||||
| 938 | # execpt where items.notforloan < 0 : This indicates the item is holdable. | ||||
| 939 | return if ( $notforloan_per_item > 0 ) or $notforloan_per_itemtype; | ||||
| 940 | |||||
| 941 | # Find this item in the reserves | ||||
| 942 | my @reserves = _Findgroupreserve( $bibitem, $biblio, $itemnumber, $lookahead_days); | ||||
| 943 | |||||
| 944 | # $priority and $highest are used to find the most important item | ||||
| 945 | # in the list returned by &_Findgroupreserve. (The lower $priority, | ||||
| 946 | # the more important the item.) | ||||
| 947 | # $highest is the most important item we've seen so far. | ||||
| 948 | my $highest; | ||||
| 949 | if (scalar @reserves) { | ||||
| 950 | my $priority = 10000000; | ||||
| 951 | foreach my $res (@reserves) { | ||||
| 952 | if ( $res->{'itemnumber'} == $itemnumber && $res->{'priority'} == 0) { | ||||
| 953 | return ( "Waiting", $res, \@reserves ); # Found it | ||||
| 954 | } else { | ||||
| 955 | # See if this item is more important than what we've got so far | ||||
| 956 | if ( $res->{'priority'} && $res->{'priority'} < $priority ) { | ||||
| 957 | my $borrowerinfo=C4::Members::GetMember(borrowernumber => $res->{'borrowernumber'}); | ||||
| 958 | my $iteminfo=C4::Items::GetItem($itemnumber); | ||||
| 959 | my $branch = GetReservesControlBranch( $iteminfo, $borrowerinfo ); | ||||
| 960 | my $branchitemrule = C4::Circulation::GetBranchItemRule($branch,$iteminfo->{'itype'}); | ||||
| 961 | next if ($branchitemrule->{'holdallowed'} == 0); | ||||
| 962 | next if (($branchitemrule->{'holdallowed'} == 1) && ($branch ne $borrowerinfo->{'branchcode'})); | ||||
| 963 | $priority = $res->{'priority'}; | ||||
| 964 | $highest = $res; | ||||
| 965 | } | ||||
| 966 | } | ||||
| 967 | } | ||||
| 968 | } | ||||
| 969 | |||||
| 970 | # If we get this far, then no exact match was found. | ||||
| 971 | # We return the most important (i.e. next) reservation. | ||||
| 972 | if ($highest) { | ||||
| 973 | $highest->{'itemnumber'} = $item; | ||||
| 974 | return ( "Reserved", $highest, \@reserves ); | ||||
| 975 | } | ||||
| 976 | |||||
| 977 | return ( '' ); | ||||
| 978 | } | ||||
| 979 | |||||
| 980 | =head2 CancelExpiredReserves | ||||
| 981 | |||||
| 982 | CancelExpiredReserves(); | ||||
| 983 | |||||
| 984 | Cancels all reserves with an expiration date from before today. | ||||
| 985 | |||||
| 986 | =cut | ||||
| 987 | |||||
| 988 | sub CancelExpiredReserves { | ||||
| 989 | |||||
| 990 | # Cancel reserves that have passed their expiration date. | ||||
| 991 | my $dbh = C4::Context->dbh; | ||||
| 992 | my $sth = $dbh->prepare( " | ||||
| 993 | SELECT * FROM reserves WHERE DATE(expirationdate) < DATE( CURDATE() ) | ||||
| 994 | AND expirationdate IS NOT NULL | ||||
| 995 | AND found IS NULL | ||||
| 996 | " ); | ||||
| 997 | $sth->execute(); | ||||
| 998 | |||||
| 999 | while ( my $res = $sth->fetchrow_hashref() ) { | ||||
| 1000 | CancelReserve({ reserve_id => $res->{'reserve_id'} }); | ||||
| 1001 | } | ||||
| 1002 | |||||
| 1003 | # Cancel reserves that have been waiting too long | ||||
| 1004 | if ( C4::Context->preference("ExpireReservesMaxPickUpDelay") ) { | ||||
| 1005 | my $max_pickup_delay = C4::Context->preference("ReservesMaxPickUpDelay"); | ||||
| 1006 | my $charge = C4::Context->preference("ExpireReservesMaxPickUpDelayCharge"); | ||||
| 1007 | |||||
| 1008 | my $query = "SELECT * FROM reserves WHERE TO_DAYS( NOW() ) - TO_DAYS( waitingdate ) > ? AND found = 'W' AND priority = 0"; | ||||
| 1009 | $sth = $dbh->prepare( $query ); | ||||
| 1010 | $sth->execute( $max_pickup_delay ); | ||||
| 1011 | |||||
| 1012 | while (my $res = $sth->fetchrow_hashref ) { | ||||
| 1013 | if ( $charge ) { | ||||
| 1014 | manualinvoice($res->{'borrowernumber'}, $res->{'itemnumber'}, 'Hold waiting too long', 'F', $charge); | ||||
| 1015 | } | ||||
| 1016 | |||||
| 1017 | CancelReserve({ reserve_id => $res->{'reserve_id'} }); | ||||
| 1018 | } | ||||
| 1019 | } | ||||
| 1020 | |||||
| 1021 | } | ||||
| 1022 | |||||
| 1023 | =head2 AutoUnsuspendReserves | ||||
| 1024 | |||||
| 1025 | AutoUnsuspendReserves(); | ||||
| 1026 | |||||
| 1027 | Unsuspends all suspended reserves with a suspend_until date from before today. | ||||
| 1028 | |||||
| 1029 | =cut | ||||
| 1030 | |||||
| 1031 | sub AutoUnsuspendReserves { | ||||
| 1032 | |||||
| 1033 | my $dbh = C4::Context->dbh; | ||||
| 1034 | |||||
| 1035 | my $query = "UPDATE reserves SET suspend = 0, suspend_until = NULL WHERE DATE( suspend_until ) < DATE( CURDATE() )"; | ||||
| 1036 | my $sth = $dbh->prepare( $query ); | ||||
| 1037 | $sth->execute(); | ||||
| 1038 | |||||
| 1039 | } | ||||
| 1040 | |||||
| 1041 | =head2 CancelReserve | ||||
| 1042 | |||||
| 1043 | CancelReserve({ reserve_id => $reserve_id, [ biblionumber => $biblionumber, borrowernumber => $borrrowernumber, itemnumber => $itemnumber ] }); | ||||
| 1044 | |||||
| 1045 | Cancels a reserve. | ||||
| 1046 | |||||
| 1047 | =cut | ||||
| 1048 | |||||
| 1049 | sub CancelReserve { | ||||
| 1050 | my ( $params ) = @_; | ||||
| 1051 | |||||
| 1052 | my $reserve_id = $params->{'reserve_id'}; | ||||
| 1053 | $reserve_id = GetReserveId( $params ) unless ( $reserve_id ); | ||||
| 1054 | |||||
| 1055 | return unless ( $reserve_id ); | ||||
| 1056 | |||||
| 1057 | my $dbh = C4::Context->dbh; | ||||
| 1058 | |||||
| 1059 | my $reserve = GetReserve( $reserve_id ); | ||||
| 1060 | |||||
| 1061 | my $query = " | ||||
| 1062 | UPDATE reserves | ||||
| 1063 | SET cancellationdate = now(), | ||||
| 1064 | found = Null, | ||||
| 1065 | priority = 0 | ||||
| 1066 | WHERE reserve_id = ? | ||||
| 1067 | "; | ||||
| 1068 | my $sth = $dbh->prepare($query); | ||||
| 1069 | $sth->execute( $reserve_id ); | ||||
| 1070 | |||||
| 1071 | $query = " | ||||
| 1072 | INSERT INTO old_reserves | ||||
| 1073 | SELECT * FROM reserves | ||||
| 1074 | WHERE reserve_id = ? | ||||
| 1075 | "; | ||||
| 1076 | $sth = $dbh->prepare($query); | ||||
| 1077 | $sth->execute( $reserve_id ); | ||||
| 1078 | |||||
| 1079 | $query = " | ||||
| 1080 | DELETE FROM reserves | ||||
| 1081 | WHERE reserve_id = ? | ||||
| 1082 | "; | ||||
| 1083 | $sth = $dbh->prepare($query); | ||||
| 1084 | $sth->execute( $reserve_id ); | ||||
| 1085 | |||||
| 1086 | # now fix the priority on the others.... | ||||
| 1087 | _FixPriority({ biblionumber => $reserve->{biblionumber} }); | ||||
| 1088 | } | ||||
| 1089 | |||||
| 1090 | =head2 ModReserve | ||||
| 1091 | |||||
| 1092 | ModReserve({ rank => $rank, | ||||
| 1093 | reserve_id => $reserve_id, | ||||
| 1094 | branchcode => $branchcode | ||||
| 1095 | [, itemnumber => $itemnumber ] | ||||
| 1096 | [, biblionumber => $biblionumber, $borrowernumber => $borrowernumber ] | ||||
| 1097 | }); | ||||
| 1098 | |||||
| 1099 | Change a hold request's priority or cancel it. | ||||
| 1100 | |||||
| 1101 | C<$rank> specifies the effect of the change. If C<$rank> | ||||
| 1102 | is 'W' or 'n', nothing happens. This corresponds to leaving a | ||||
| 1103 | request alone when changing its priority in the holds queue | ||||
| 1104 | for a bib. | ||||
| 1105 | |||||
| 1106 | If C<$rank> is 'del', the hold request is cancelled. | ||||
| 1107 | |||||
| 1108 | If C<$rank> is an integer greater than zero, the priority of | ||||
| 1109 | the request is set to that value. Since priority != 0 means | ||||
| 1110 | that the item is not waiting on the hold shelf, setting the | ||||
| 1111 | priority to a non-zero value also sets the request's found | ||||
| 1112 | status and waiting date to NULL. | ||||
| 1113 | |||||
| 1114 | The optional C<$itemnumber> parameter is used only when | ||||
| 1115 | C<$rank> is a non-zero integer; if supplied, the itemnumber | ||||
| 1116 | of the hold request is set accordingly; if omitted, the itemnumber | ||||
| 1117 | is cleared. | ||||
| 1118 | |||||
| 1119 | B<FIXME:> Note that the forgoing can have the effect of causing | ||||
| 1120 | item-level hold requests to turn into title-level requests. This | ||||
| 1121 | will be fixed once reserves has separate columns for requested | ||||
| 1122 | itemnumber and supplying itemnumber. | ||||
| 1123 | |||||
| 1124 | =cut | ||||
| 1125 | |||||
| 1126 | sub ModReserve { | ||||
| 1127 | my ( $params ) = @_; | ||||
| 1128 | |||||
| 1129 | my $rank = $params->{'rank'}; | ||||
| 1130 | my $reserve_id = $params->{'reserve_id'}; | ||||
| 1131 | my $branchcode = $params->{'branchcode'}; | ||||
| 1132 | my $itemnumber = $params->{'itemnumber'}; | ||||
| 1133 | my $suspend_until = $params->{'suspend_until'}; | ||||
| 1134 | my $borrowernumber = $params->{'borrowernumber'}; | ||||
| 1135 | my $biblionumber = $params->{'biblionumber'}; | ||||
| 1136 | |||||
| 1137 | return if $rank eq "W"; | ||||
| 1138 | return if $rank eq "n"; | ||||
| 1139 | |||||
| 1140 | return unless ( $reserve_id || ( $borrowernumber && ( $biblionumber || $itemnumber ) ) ); | ||||
| 1141 | $reserve_id = GetReserveId({ biblionumber => $biblionumber, borrowernumber => $borrowernumber, itemnumber => $itemnumber }) unless ( $reserve_id ); | ||||
| 1142 | |||||
| 1143 | my $dbh = C4::Context->dbh; | ||||
| 1144 | if ( $rank eq "del" ) { | ||||
| 1145 | CancelReserve({ reserve_id => $reserve_id }); | ||||
| 1146 | } | ||||
| 1147 | elsif ($rank =~ /^\d+/ and $rank > 0) { | ||||
| 1148 | my $query = " | ||||
| 1149 | UPDATE reserves SET priority = ? ,branchcode = ?, itemnumber = ?, found = NULL, waitingdate = NULL | ||||
| 1150 | WHERE reserve_id = ? | ||||
| 1151 | "; | ||||
| 1152 | my $sth = $dbh->prepare($query); | ||||
| 1153 | $sth->execute( $rank, $branchcode, $itemnumber, $reserve_id ); | ||||
| 1154 | |||||
| 1155 | if ( defined( $suspend_until ) ) { | ||||
| 1156 | if ( $suspend_until ) { | ||||
| 1157 | $suspend_until = C4::Dates->new( $suspend_until )->output("iso"); | ||||
| 1158 | $dbh->do("UPDATE reserves SET suspend = 1, suspend_until = ? WHERE reserve_id = ?", undef, ( $suspend_until, $reserve_id ) ); | ||||
| 1159 | } else { | ||||
| 1160 | $dbh->do("UPDATE reserves SET suspend_until = NULL WHERE reserve_id = ?", undef, ( $reserve_id ) ); | ||||
| 1161 | } | ||||
| 1162 | } | ||||
| 1163 | |||||
| 1164 | _FixPriority({ reserve_id => $reserve_id, rank =>$rank }); | ||||
| 1165 | } | ||||
| 1166 | } | ||||
| 1167 | |||||
| 1168 | =head2 ModReserveFill | ||||
| 1169 | |||||
| 1170 | &ModReserveFill($reserve); | ||||
| 1171 | |||||
| 1172 | Fill a reserve. If I understand this correctly, this means that the | ||||
| 1173 | reserved book has been found and given to the patron who reserved it. | ||||
| 1174 | |||||
| 1175 | C<$reserve> specifies the reserve to fill. It is a reference-to-hash | ||||
| 1176 | whose keys are fields from the reserves table in the Koha database. | ||||
| 1177 | |||||
| 1178 | =cut | ||||
| 1179 | |||||
| 1180 | sub ModReserveFill { | ||||
| 1181 | my ($res) = @_; | ||||
| 1182 | my $dbh = C4::Context->dbh; | ||||
| 1183 | # fill in a reserve record.... | ||||
| 1184 | my $reserve_id = $res->{'reserve_id'}; | ||||
| 1185 | my $biblionumber = $res->{'biblionumber'}; | ||||
| 1186 | my $borrowernumber = $res->{'borrowernumber'}; | ||||
| 1187 | my $resdate = $res->{'reservedate'}; | ||||
| 1188 | |||||
| 1189 | # get the priority on this record.... | ||||
| 1190 | my $priority; | ||||
| 1191 | my $query = "SELECT priority | ||||
| 1192 | FROM reserves | ||||
| 1193 | WHERE biblionumber = ? | ||||
| 1194 | AND borrowernumber = ? | ||||
| 1195 | AND reservedate = ?"; | ||||
| 1196 | my $sth = $dbh->prepare($query); | ||||
| 1197 | $sth->execute( $biblionumber, $borrowernumber, $resdate ); | ||||
| 1198 | ($priority) = $sth->fetchrow_array; | ||||
| 1199 | |||||
| 1200 | # update the database... | ||||
| 1201 | $query = "UPDATE reserves | ||||
| 1202 | SET found = 'F', | ||||
| 1203 | priority = 0 | ||||
| 1204 | WHERE biblionumber = ? | ||||
| 1205 | AND reservedate = ? | ||||
| 1206 | AND borrowernumber = ? | ||||
| 1207 | "; | ||||
| 1208 | $sth = $dbh->prepare($query); | ||||
| 1209 | $sth->execute( $biblionumber, $resdate, $borrowernumber ); | ||||
| 1210 | |||||
| 1211 | # move to old_reserves | ||||
| 1212 | $query = "INSERT INTO old_reserves | ||||
| 1213 | SELECT * FROM reserves | ||||
| 1214 | WHERE biblionumber = ? | ||||
| 1215 | AND reservedate = ? | ||||
| 1216 | AND borrowernumber = ? | ||||
| 1217 | "; | ||||
| 1218 | $sth = $dbh->prepare($query); | ||||
| 1219 | $sth->execute( $biblionumber, $resdate, $borrowernumber ); | ||||
| 1220 | $query = "DELETE FROM reserves | ||||
| 1221 | WHERE biblionumber = ? | ||||
| 1222 | AND reservedate = ? | ||||
| 1223 | AND borrowernumber = ? | ||||
| 1224 | "; | ||||
| 1225 | $sth = $dbh->prepare($query); | ||||
| 1226 | $sth->execute( $biblionumber, $resdate, $borrowernumber ); | ||||
| 1227 | |||||
| 1228 | # now fix the priority on the others (if the priority wasn't | ||||
| 1229 | # already sorted!).... | ||||
| 1230 | unless ( $priority == 0 ) { | ||||
| 1231 | _FixPriority({ reserve_id => $reserve_id }); | ||||
| 1232 | } | ||||
| 1233 | } | ||||
| 1234 | |||||
| 1235 | =head2 ModReserveStatus | ||||
| 1236 | |||||
| 1237 | &ModReserveStatus($itemnumber, $newstatus); | ||||
| 1238 | |||||
| 1239 | Update the reserve status for the active (priority=0) reserve. | ||||
| 1240 | |||||
| 1241 | $itemnumber is the itemnumber the reserve is on | ||||
| 1242 | |||||
| 1243 | $newstatus is the new status. | ||||
| 1244 | |||||
| 1245 | =cut | ||||
| 1246 | |||||
| 1247 | sub ModReserveStatus { | ||||
| 1248 | |||||
| 1249 | #first : check if we have a reservation for this item . | ||||
| 1250 | my ($itemnumber, $newstatus) = @_; | ||||
| 1251 | my $dbh = C4::Context->dbh; | ||||
| 1252 | |||||
| 1253 | my $query = "UPDATE reserves SET found = ?, waitingdate = NOW() WHERE itemnumber = ? AND found IS NULL AND priority = 0"; | ||||
| 1254 | my $sth_set = $dbh->prepare($query); | ||||
| 1255 | $sth_set->execute( $newstatus, $itemnumber ); | ||||
| 1256 | |||||
| 1257 | if ( C4::Context->preference("ReturnToShelvingCart") && $newstatus ) { | ||||
| 1258 | CartToShelf( $itemnumber ); | ||||
| 1259 | } | ||||
| 1260 | } | ||||
| 1261 | |||||
| 1262 | =head2 ModReserveAffect | ||||
| 1263 | |||||
| 1264 | &ModReserveAffect($itemnumber,$borrowernumber,$diffBranchSend); | ||||
| 1265 | |||||
| 1266 | This function affect an item and a status for a given reserve | ||||
| 1267 | The itemnumber parameter is used to find the biblionumber. | ||||
| 1268 | with the biblionumber & the borrowernumber, we can affect the itemnumber | ||||
| 1269 | to the correct reserve. | ||||
| 1270 | |||||
| 1271 | if $transferToDo is not set, then the status is set to "Waiting" as well. | ||||
| 1272 | otherwise, a transfer is on the way, and the end of the transfer will | ||||
| 1273 | take care of the waiting status | ||||
| 1274 | |||||
| 1275 | =cut | ||||
| 1276 | |||||
| 1277 | sub ModReserveAffect { | ||||
| 1278 | my ( $itemnumber, $borrowernumber,$transferToDo ) = @_; | ||||
| 1279 | my $dbh = C4::Context->dbh; | ||||
| 1280 | |||||
| 1281 | # we want to attach $itemnumber to $borrowernumber, find the biblionumber | ||||
| 1282 | # attached to $itemnumber | ||||
| 1283 | my $sth = $dbh->prepare("SELECT biblionumber FROM items WHERE itemnumber=?"); | ||||
| 1284 | $sth->execute($itemnumber); | ||||
| 1285 | my ($biblionumber) = $sth->fetchrow; | ||||
| 1286 | |||||
| 1287 | # get request - need to find out if item is already | ||||
| 1288 | # waiting in order to not send duplicate hold filled notifications | ||||
| 1289 | my $reserve_id = GetReserveId({ | ||||
| 1290 | borrowernumber => $borrowernumber, | ||||
| 1291 | biblionumber => $biblionumber, | ||||
| 1292 | }); | ||||
| 1293 | return unless defined $reserve_id; | ||||
| 1294 | my $request = GetReserveInfo($reserve_id); | ||||
| 1295 | my $already_on_shelf = ($request && $request->{found} eq 'W') ? 1 : 0; | ||||
| 1296 | |||||
| 1297 | # If we affect a reserve that has to be transfered, don't set to Waiting | ||||
| 1298 | my $query; | ||||
| 1299 | if ($transferToDo) { | ||||
| 1300 | $query = " | ||||
| 1301 | UPDATE reserves | ||||
| 1302 | SET priority = 0, | ||||
| 1303 | itemnumber = ?, | ||||
| 1304 | found = 'T' | ||||
| 1305 | WHERE borrowernumber = ? | ||||
| 1306 | AND biblionumber = ? | ||||
| 1307 | "; | ||||
| 1308 | } | ||||
| 1309 | else { | ||||
| 1310 | # affect the reserve to Waiting as well. | ||||
| 1311 | $query = " | ||||
| 1312 | UPDATE reserves | ||||
| 1313 | SET priority = 0, | ||||
| 1314 | found = 'W', | ||||
| 1315 | waitingdate = NOW(), | ||||
| 1316 | itemnumber = ? | ||||
| 1317 | WHERE borrowernumber = ? | ||||
| 1318 | AND biblionumber = ? | ||||
| 1319 | "; | ||||
| 1320 | } | ||||
| 1321 | $sth = $dbh->prepare($query); | ||||
| 1322 | $sth->execute( $itemnumber, $borrowernumber,$biblionumber); | ||||
| 1323 | _koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber ) if ( !$transferToDo && !$already_on_shelf ); | ||||
| 1324 | _FixPriority( { biblionumber => $biblionumber } ); | ||||
| 1325 | if ( C4::Context->preference("ReturnToShelvingCart") ) { | ||||
| 1326 | CartToShelf( $itemnumber ); | ||||
| 1327 | } | ||||
| 1328 | |||||
| 1329 | return; | ||||
| 1330 | } | ||||
| 1331 | |||||
| 1332 | =head2 ModReserveCancelAll | ||||
| 1333 | |||||
| 1334 | ($messages,$nextreservinfo) = &ModReserveCancelAll($itemnumber,$borrowernumber); | ||||
| 1335 | |||||
| 1336 | function to cancel reserv,check other reserves, and transfer document if it's necessary | ||||
| 1337 | |||||
| 1338 | =cut | ||||
| 1339 | |||||
| 1340 | sub ModReserveCancelAll { | ||||
| 1341 | my $messages; | ||||
| 1342 | my $nextreservinfo; | ||||
| 1343 | my ( $itemnumber, $borrowernumber ) = @_; | ||||
| 1344 | |||||
| 1345 | #step 1 : cancel the reservation | ||||
| 1346 | my $CancelReserve = CancelReserve({ itemnumber => $itemnumber, borrowernumber => $borrowernumber }); | ||||
| 1347 | |||||
| 1348 | #step 2 launch the subroutine of the others reserves | ||||
| 1349 | ( $messages, $nextreservinfo ) = GetOtherReserves($itemnumber); | ||||
| 1350 | |||||
| 1351 | return ( $messages, $nextreservinfo ); | ||||
| 1352 | } | ||||
| 1353 | |||||
| 1354 | =head2 ModReserveMinusPriority | ||||
| 1355 | |||||
| 1356 | &ModReserveMinusPriority($itemnumber,$borrowernumber,$biblionumber) | ||||
| 1357 | |||||
| 1358 | Reduce the values of queued list | ||||
| 1359 | |||||
| 1360 | =cut | ||||
| 1361 | |||||
| 1362 | sub ModReserveMinusPriority { | ||||
| 1363 | my ( $itemnumber, $reserve_id ) = @_; | ||||
| 1364 | |||||
| 1365 | #first step update the value of the first person on reserv | ||||
| 1366 | my $dbh = C4::Context->dbh; | ||||
| 1367 | my $query = " | ||||
| 1368 | UPDATE reserves | ||||
| 1369 | SET priority = 0 , itemnumber = ? | ||||
| 1370 | WHERE reserve_id = ? | ||||
| 1371 | "; | ||||
| 1372 | my $sth_upd = $dbh->prepare($query); | ||||
| 1373 | $sth_upd->execute( $itemnumber, $reserve_id ); | ||||
| 1374 | # second step update all others reserves | ||||
| 1375 | _FixPriority({ reserve_id => $reserve_id, rank => '0' }); | ||||
| 1376 | } | ||||
| 1377 | |||||
| 1378 | =head2 GetReserveInfo | ||||
| 1379 | |||||
| 1380 | &GetReserveInfo($reserve_id); | ||||
| 1381 | |||||
| 1382 | Get item and borrower details for a current hold. | ||||
| 1383 | Current implementation this query should have a single result. | ||||
| 1384 | |||||
| 1385 | =cut | ||||
| 1386 | |||||
| 1387 | sub GetReserveInfo { | ||||
| 1388 | my ( $reserve_id ) = @_; | ||||
| 1389 | my $dbh = C4::Context->dbh; | ||||
| 1390 | my $strsth="SELECT | ||||
| 1391 | reserve_id, | ||||
| 1392 | reservedate, | ||||
| 1393 | reservenotes, | ||||
| 1394 | reserves.borrowernumber, | ||||
| 1395 | reserves.biblionumber, | ||||
| 1396 | reserves.branchcode, | ||||
| 1397 | reserves.waitingdate, | ||||
| 1398 | notificationdate, | ||||
| 1399 | reminderdate, | ||||
| 1400 | priority, | ||||
| 1401 | found, | ||||
| 1402 | firstname, | ||||
| 1403 | surname, | ||||
| 1404 | phone, | ||||
| 1405 | email, | ||||
| 1406 | address, | ||||
| 1407 | address2, | ||||
| 1408 | cardnumber, | ||||
| 1409 | city, | ||||
| 1410 | zipcode, | ||||
| 1411 | biblio.title, | ||||
| 1412 | biblio.author, | ||||
| 1413 | items.holdingbranch, | ||||
| 1414 | items.itemcallnumber, | ||||
| 1415 | items.itemnumber, | ||||
| 1416 | items.location, | ||||
| 1417 | barcode, | ||||
| 1418 | notes | ||||
| 1419 | FROM reserves | ||||
| 1420 | LEFT JOIN items USING(itemnumber) | ||||
| 1421 | LEFT JOIN borrowers USING(borrowernumber) | ||||
| 1422 | LEFT JOIN biblio ON (reserves.biblionumber=biblio.biblionumber) | ||||
| 1423 | WHERE reserves.reserve_id = ?"; | ||||
| 1424 | my $sth = $dbh->prepare($strsth); | ||||
| 1425 | $sth->execute($reserve_id); | ||||
| 1426 | |||||
| 1427 | my $data = $sth->fetchrow_hashref; | ||||
| 1428 | return $data; | ||||
| 1429 | } | ||||
| 1430 | |||||
| 1431 | =head2 IsAvailableForItemLevelRequest | ||||
| 1432 | |||||
| 1433 | my $is_available = IsAvailableForItemLevelRequest($itemnumber); | ||||
| 1434 | |||||
| 1435 | Checks whether a given item record is available for an | ||||
| 1436 | item-level hold request. An item is available if | ||||
| 1437 | |||||
| 1438 | * it is not lost AND | ||||
| 1439 | * it is not damaged AND | ||||
| 1440 | * it is not withdrawn AND | ||||
| 1441 | * does not have a not for loan value > 0 | ||||
| 1442 | |||||
| 1443 | Whether or not the item is currently on loan is | ||||
| 1444 | also checked - if the AllowOnShelfHolds system preference | ||||
| 1445 | is ON, an item can be requested even if it is currently | ||||
| 1446 | on loan to somebody else. If the system preference | ||||
| 1447 | is OFF, an item that is currently checked out cannot | ||||
| 1448 | be the target of an item-level hold request. | ||||
| 1449 | |||||
| 1450 | Note that IsAvailableForItemLevelRequest() does not | ||||
| 1451 | check if the staff operator is authorized to place | ||||
| 1452 | a request on the item - in particular, | ||||
| 1453 | this routine does not check IndependentBranches | ||||
| 1454 | and canreservefromotherbranches. | ||||
| 1455 | |||||
| 1456 | =cut | ||||
| 1457 | |||||
| 1458 | sub IsAvailableForItemLevelRequest { | ||||
| 1459 | my $itemnumber = shift; | ||||
| 1460 | |||||
| 1461 | my $item = GetItem($itemnumber); | ||||
| 1462 | |||||
| 1463 | # must check the notforloan setting of the itemtype | ||||
| 1464 | # FIXME - a lot of places in the code do this | ||||
| 1465 | # or something similar - need to be | ||||
| 1466 | # consolidated | ||||
| 1467 | my $dbh = C4::Context->dbh; | ||||
| 1468 | my $notforloan_query; | ||||
| 1469 | if (C4::Context->preference('item-level_itypes')) { | ||||
| 1470 | $notforloan_query = "SELECT itemtypes.notforloan | ||||
| 1471 | FROM items | ||||
| 1472 | JOIN itemtypes ON (itemtypes.itemtype = items.itype) | ||||
| 1473 | WHERE itemnumber = ?"; | ||||
| 1474 | } else { | ||||
| 1475 | $notforloan_query = "SELECT itemtypes.notforloan | ||||
| 1476 | FROM items | ||||
| 1477 | JOIN biblioitems USING (biblioitemnumber) | ||||
| 1478 | JOIN itemtypes USING (itemtype) | ||||
| 1479 | WHERE itemnumber = ?"; | ||||
| 1480 | } | ||||
| 1481 | my $sth = $dbh->prepare($notforloan_query); | ||||
| 1482 | $sth->execute($itemnumber); | ||||
| 1483 | my $notforloan_per_itemtype = 0; | ||||
| 1484 | if (my ($notforloan) = $sth->fetchrow_array) { | ||||
| 1485 | $notforloan_per_itemtype = 1 if $notforloan; | ||||
| 1486 | } | ||||
| 1487 | |||||
| 1488 | my $available_per_item = 1; | ||||
| 1489 | $available_per_item = 0 if $item->{itemlost} or | ||||
| 1490 | ( $item->{notforloan} > 0 ) or | ||||
| 1491 | ($item->{damaged} and not C4::Context->preference('AllowHoldsOnDamagedItems')) or | ||||
| 1492 | $item->{withdrawn} or | ||||
| 1493 | $notforloan_per_itemtype; | ||||
| 1494 | |||||
| 1495 | |||||
| 1496 | if (C4::Context->preference('AllowOnShelfHolds')) { | ||||
| 1497 | return $available_per_item; | ||||
| 1498 | } else { | ||||
| 1499 | return ($available_per_item and ($item->{onloan} or GetReserveStatus($itemnumber) eq "Waiting")); | ||||
| 1500 | } | ||||
| 1501 | } | ||||
| 1502 | |||||
| 1503 | =head2 AlterPriority | ||||
| 1504 | |||||
| 1505 | AlterPriority( $where, $reserve_id ); | ||||
| 1506 | |||||
| 1507 | This function changes a reserve's priority up, down, to the top, or to the bottom. | ||||
| 1508 | Input: $where is 'up', 'down', 'top' or 'bottom'. Biblionumber, Date reserve was placed | ||||
| 1509 | |||||
| 1510 | =cut | ||||
| 1511 | |||||
| 1512 | sub AlterPriority { | ||||
| 1513 | my ( $where, $reserve_id ) = @_; | ||||
| 1514 | |||||
| 1515 | my $dbh = C4::Context->dbh; | ||||
| 1516 | |||||
| 1517 | my $reserve = GetReserve( $reserve_id ); | ||||
| 1518 | |||||
| 1519 | if ( $reserve->{cancellationdate} ) { | ||||
| 1520 | warn "I cannot alter the priority for reserve_id $reserve_id, the reserve has been cancelled (".$reserve->{cancellationdate}.')'; | ||||
| 1521 | return; | ||||
| 1522 | } | ||||
| 1523 | |||||
| 1524 | if ( $where eq 'up' || $where eq 'down' ) { | ||||
| 1525 | |||||
| 1526 | my $priority = $reserve->{'priority'}; | ||||
| 1527 | $priority = $where eq 'up' ? $priority - 1 : $priority + 1; | ||||
| 1528 | _FixPriority({ reserve_id => $reserve_id, rank => $priority }) | ||||
| 1529 | |||||
| 1530 | } elsif ( $where eq 'top' ) { | ||||
| 1531 | |||||
| 1532 | _FixPriority({ reserve_id => $reserve_id, rank => '1' }) | ||||
| 1533 | |||||
| 1534 | } elsif ( $where eq 'bottom' ) { | ||||
| 1535 | |||||
| 1536 | _FixPriority({ reserve_id => $reserve_id, rank => '999999' }); | ||||
| 1537 | |||||
| 1538 | } | ||||
| 1539 | } | ||||
| 1540 | |||||
| 1541 | =head2 ToggleLowestPriority | ||||
| 1542 | |||||
| 1543 | ToggleLowestPriority( $borrowernumber, $biblionumber ); | ||||
| 1544 | |||||
| 1545 | This function sets the lowestPriority field to true if is false, and false if it is true. | ||||
| 1546 | |||||
| 1547 | =cut | ||||
| 1548 | |||||
| 1549 | sub ToggleLowestPriority { | ||||
| 1550 | my ( $reserve_id ) = @_; | ||||
| 1551 | |||||
| 1552 | my $dbh = C4::Context->dbh; | ||||
| 1553 | |||||
| 1554 | my $sth = $dbh->prepare( "UPDATE reserves SET lowestPriority = NOT lowestPriority WHERE reserve_id = ?"); | ||||
| 1555 | $sth->execute( $reserve_id ); | ||||
| 1556 | |||||
| 1557 | _FixPriority({ reserve_id => $reserve_id, rank => '999999' }); | ||||
| 1558 | } | ||||
| 1559 | |||||
| 1560 | =head2 ToggleSuspend | ||||
| 1561 | |||||
| 1562 | ToggleSuspend( $reserve_id ); | ||||
| 1563 | |||||
| 1564 | This function sets the suspend field to true if is false, and false if it is true. | ||||
| 1565 | If the reserve is currently suspended with a suspend_until date, that date will | ||||
| 1566 | be cleared when it is unsuspended. | ||||
| 1567 | |||||
| 1568 | =cut | ||||
| 1569 | |||||
| 1570 | sub ToggleSuspend { | ||||
| 1571 | my ( $reserve_id, $suspend_until ) = @_; | ||||
| 1572 | |||||
| 1573 | $suspend_until = output_pref({ dt => dt_from_string( $suspend_until ), dateformat => 'iso' }) if ( $suspend_until ); | ||||
| 1574 | |||||
| 1575 | my $do_until = ( $suspend_until ) ? '?' : 'NULL'; | ||||
| 1576 | |||||
| 1577 | my $dbh = C4::Context->dbh; | ||||
| 1578 | |||||
| 1579 | my $sth = $dbh->prepare( | ||||
| 1580 | "UPDATE reserves SET suspend = NOT suspend, | ||||
| 1581 | suspend_until = CASE WHEN suspend = 0 THEN NULL ELSE $do_until END | ||||
| 1582 | WHERE reserve_id = ? | ||||
| 1583 | "); | ||||
| 1584 | |||||
| 1585 | my @params; | ||||
| 1586 | push( @params, $suspend_until ) if ( $suspend_until ); | ||||
| 1587 | push( @params, $reserve_id ); | ||||
| 1588 | |||||
| 1589 | $sth->execute( @params ); | ||||
| 1590 | } | ||||
| 1591 | |||||
| 1592 | =head2 SuspendAll | ||||
| 1593 | |||||
| 1594 | SuspendAll( | ||||
| 1595 | borrowernumber => $borrowernumber, | ||||
| 1596 | [ biblionumber => $biblionumber, ] | ||||
| 1597 | [ suspend_until => $suspend_until, ] | ||||
| 1598 | [ suspend => $suspend ] | ||||
| 1599 | ); | ||||
| 1600 | |||||
| 1601 | This function accepts a set of hash keys as its parameters. | ||||
| 1602 | It requires either borrowernumber or biblionumber, or both. | ||||
| 1603 | |||||
| 1604 | suspend_until is wholly optional. | ||||
| 1605 | |||||
| 1606 | =cut | ||||
| 1607 | |||||
| 1608 | sub SuspendAll { | ||||
| 1609 | my %params = @_; | ||||
| 1610 | |||||
| 1611 | my $borrowernumber = $params{'borrowernumber'} || undef; | ||||
| 1612 | my $biblionumber = $params{'biblionumber'} || undef; | ||||
| 1613 | my $suspend_until = $params{'suspend_until'} || undef; | ||||
| 1614 | my $suspend = defined( $params{'suspend'} ) ? $params{'suspend'} : 1; | ||||
| 1615 | |||||
| 1616 | $suspend_until = C4::Dates->new( $suspend_until )->output("iso") if ( defined( $suspend_until ) ); | ||||
| 1617 | |||||
| 1618 | return unless ( $borrowernumber || $biblionumber ); | ||||
| 1619 | |||||
| 1620 | my ( $query, $sth, $dbh, @query_params ); | ||||
| 1621 | |||||
| 1622 | $query = "UPDATE reserves SET suspend = ? "; | ||||
| 1623 | push( @query_params, $suspend ); | ||||
| 1624 | if ( !$suspend ) { | ||||
| 1625 | $query .= ", suspend_until = NULL "; | ||||
| 1626 | } elsif ( $suspend_until ) { | ||||
| 1627 | $query .= ", suspend_until = ? "; | ||||
| 1628 | push( @query_params, $suspend_until ); | ||||
| 1629 | } | ||||
| 1630 | $query .= " WHERE "; | ||||
| 1631 | if ( $borrowernumber ) { | ||||
| 1632 | $query .= " borrowernumber = ? "; | ||||
| 1633 | push( @query_params, $borrowernumber ); | ||||
| 1634 | } | ||||
| 1635 | $query .= " AND " if ( $borrowernumber && $biblionumber ); | ||||
| 1636 | if ( $biblionumber ) { | ||||
| 1637 | $query .= " biblionumber = ? "; | ||||
| 1638 | push( @query_params, $biblionumber ); | ||||
| 1639 | } | ||||
| 1640 | $query .= " AND found IS NULL "; | ||||
| 1641 | |||||
| 1642 | $dbh = C4::Context->dbh; | ||||
| 1643 | $sth = $dbh->prepare( $query ); | ||||
| 1644 | $sth->execute( @query_params ); | ||||
| 1645 | } | ||||
| 1646 | |||||
| 1647 | |||||
| 1648 | =head2 _FixPriority | ||||
| 1649 | |||||
| 1650 | _FixPriority({ | ||||
| 1651 | reserve_id => $reserve_id, | ||||
| 1652 | [rank => $rank,] | ||||
| 1653 | [ignoreSetLowestRank => $ignoreSetLowestRank] | ||||
| 1654 | }); | ||||
| 1655 | |||||
| 1656 | or | ||||
| 1657 | |||||
| 1658 | _FixPriority({ biblionumber => $biblionumber}); | ||||
| 1659 | |||||
| 1660 | This routine adjusts the priority of a hold request and holds | ||||
| 1661 | on the same bib. | ||||
| 1662 | |||||
| 1663 | In the first form, where a reserve_id is passed, the priority of the | ||||
| 1664 | hold is set to supplied rank, and other holds for that bib are adjusted | ||||
| 1665 | accordingly. If the rank is "del", the hold is cancelled. If no rank | ||||
| 1666 | is supplied, all of the holds on that bib have their priority adjusted | ||||
| 1667 | as if the second form had been used. | ||||
| 1668 | |||||
| 1669 | In the second form, where a biblionumber is passed, the holds on that | ||||
| 1670 | bib (that are not captured) are sorted in order of increasing priority, | ||||
| 1671 | then have reserves.priority set so that the first non-captured hold | ||||
| 1672 | has its priority set to 1, the second non-captured hold has its priority | ||||
| 1673 | set to 2, and so forth. | ||||
| 1674 | |||||
| 1675 | In both cases, holds that have the lowestPriority flag on are have their | ||||
| 1676 | priority adjusted to ensure that they remain at the end of the line. | ||||
| 1677 | |||||
| 1678 | Note that the ignoreSetLowestRank parameter is meant to be used only | ||||
| 1679 | when _FixPriority calls itself. | ||||
| 1680 | |||||
| 1681 | =cut | ||||
| 1682 | |||||
| 1683 | sub _FixPriority { | ||||
| 1684 | my ( $params ) = @_; | ||||
| 1685 | my $reserve_id = $params->{reserve_id}; | ||||
| 1686 | my $rank = $params->{rank} // ''; | ||||
| 1687 | my $ignoreSetLowestRank = $params->{ignoreSetLowestRank}; | ||||
| 1688 | my $biblionumber = $params->{biblionumber}; | ||||
| 1689 | |||||
| 1690 | my $dbh = C4::Context->dbh; | ||||
| 1691 | |||||
| 1692 | unless ( $biblionumber ) { | ||||
| 1693 | my $res = GetReserve( $reserve_id ); | ||||
| 1694 | $biblionumber = $res->{biblionumber}; | ||||
| 1695 | } | ||||
| 1696 | |||||
| 1697 | if ( $rank eq "del" ) { | ||||
| 1698 | CancelReserve({ reserve_id => $reserve_id }); | ||||
| 1699 | } | ||||
| 1700 | elsif ( $rank eq "W" || $rank eq "0" ) { | ||||
| 1701 | |||||
| 1702 | # make sure priority for waiting or in-transit items is 0 | ||||
| 1703 | my $query = " | ||||
| 1704 | UPDATE reserves | ||||
| 1705 | SET priority = 0 | ||||
| 1706 | WHERE reserve_id = ? | ||||
| 1707 | AND found IN ('W', 'T') | ||||
| 1708 | "; | ||||
| 1709 | my $sth = $dbh->prepare($query); | ||||
| 1710 | $sth->execute( $reserve_id ); | ||||
| 1711 | } | ||||
| 1712 | my @priority; | ||||
| 1713 | |||||
| 1714 | # get whats left | ||||
| 1715 | my $query = " | ||||
| 1716 | SELECT reserve_id, borrowernumber, reservedate, constrainttype | ||||
| 1717 | FROM reserves | ||||
| 1718 | WHERE biblionumber = ? | ||||
| 1719 | AND ((found <> 'W' AND found <> 'T') OR found IS NULL) | ||||
| 1720 | ORDER BY priority ASC | ||||
| 1721 | "; | ||||
| 1722 | my $sth = $dbh->prepare($query); | ||||
| 1723 | $sth->execute( $biblionumber ); | ||||
| 1724 | while ( my $line = $sth->fetchrow_hashref ) { | ||||
| 1725 | push( @priority, $line ); | ||||
| 1726 | } | ||||
| 1727 | |||||
| 1728 | # To find the matching index | ||||
| 1729 | my $i; | ||||
| 1730 | my $key = -1; # to allow for 0 to be a valid result | ||||
| 1731 | for ( $i = 0 ; $i < @priority ; $i++ ) { | ||||
| 1732 | if ( $reserve_id == $priority[$i]->{'reserve_id'} ) { | ||||
| 1733 | $key = $i; # save the index | ||||
| 1734 | last; | ||||
| 1735 | } | ||||
| 1736 | } | ||||
| 1737 | |||||
| 1738 | # if index exists in array then move it to new position | ||||
| 1739 | if ( $key > -1 && $rank ne 'del' && $rank > 0 ) { | ||||
| 1740 | my $new_rank = $rank - | ||||
| 1741 | 1; # $new_rank is what you want the new index to be in the array | ||||
| 1742 | my $moving_item = splice( @priority, $key, 1 ); | ||||
| 1743 | splice( @priority, $new_rank, 0, $moving_item ); | ||||
| 1744 | } | ||||
| 1745 | |||||
| 1746 | # now fix the priority on those that are left.... | ||||
| 1747 | $query = " | ||||
| 1748 | UPDATE reserves | ||||
| 1749 | SET priority = ? | ||||
| 1750 | WHERE reserve_id = ? | ||||
| 1751 | "; | ||||
| 1752 | $sth = $dbh->prepare($query); | ||||
| 1753 | for ( my $j = 0 ; $j < @priority ; $j++ ) { | ||||
| 1754 | $sth->execute( | ||||
| 1755 | $j + 1, | ||||
| 1756 | $priority[$j]->{'reserve_id'} | ||||
| 1757 | ); | ||||
| 1758 | } | ||||
| 1759 | |||||
| 1760 | $sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" ); | ||||
| 1761 | $sth->execute(); | ||||
| 1762 | |||||
| 1763 | unless ( $ignoreSetLowestRank ) { | ||||
| 1764 | while ( my $res = $sth->fetchrow_hashref() ) { | ||||
| 1765 | _FixPriority({ | ||||
| 1766 | reserve_id => $res->{'reserve_id'}, | ||||
| 1767 | rank => '999999', | ||||
| 1768 | ignoreSetLowestRank => 1 | ||||
| 1769 | }); | ||||
| 1770 | } | ||||
| 1771 | } | ||||
| 1772 | } | ||||
| 1773 | |||||
| 1774 | =head2 _Findgroupreserve | ||||
| 1775 | |||||
| 1776 | @results = &_Findgroupreserve($biblioitemnumber, $biblionumber, $itemnumber, $lookahead); | ||||
| 1777 | |||||
| 1778 | Looks for an item-specific match first, then for a title-level match, returning the | ||||
| 1779 | first match found. If neither, then we look for a 3rd kind of match based on | ||||
| 1780 | reserve constraints. | ||||
| 1781 | Lookahead is the number of days to look in advance. | ||||
| 1782 | |||||
| 1783 | TODO: add more explanation about reserve constraints | ||||
| 1784 | |||||
| 1785 | C<&_Findgroupreserve> returns : | ||||
| 1786 | C<@results> is an array of references-to-hash whose keys are mostly | ||||
| 1787 | fields from the reserves table of the Koha database, plus | ||||
| 1788 | C<biblioitemnumber>. | ||||
| 1789 | |||||
| 1790 | =cut | ||||
| 1791 | |||||
| 1792 | sub _Findgroupreserve { | ||||
| 1793 | my ( $bibitem, $biblio, $itemnumber, $lookahead) = @_; | ||||
| 1794 | my $dbh = C4::Context->dbh; | ||||
| 1795 | |||||
| 1796 | # TODO: consolidate at least the SELECT portion of the first 2 queries to a common $select var. | ||||
| 1797 | # check for exact targetted match | ||||
| 1798 | my $item_level_target_query = qq/ | ||||
| 1799 | SELECT reserves.biblionumber AS biblionumber, | ||||
| 1800 | reserves.borrowernumber AS borrowernumber, | ||||
| 1801 | reserves.reservedate AS reservedate, | ||||
| 1802 | reserves.branchcode AS branchcode, | ||||
| 1803 | reserves.cancellationdate AS cancellationdate, | ||||
| 1804 | reserves.found AS found, | ||||
| 1805 | reserves.reservenotes AS reservenotes, | ||||
| 1806 | reserves.priority AS priority, | ||||
| 1807 | reserves.timestamp AS timestamp, | ||||
| 1808 | biblioitems.biblioitemnumber AS biblioitemnumber, | ||||
| 1809 | reserves.itemnumber AS itemnumber, | ||||
| 1810 | reserves.reserve_id AS reserve_id | ||||
| 1811 | FROM reserves | ||||
| 1812 | JOIN biblioitems USING (biblionumber) | ||||
| 1813 | JOIN hold_fill_targets USING (biblionumber, borrowernumber, itemnumber) | ||||
| 1814 | WHERE found IS NULL | ||||
| 1815 | AND priority > 0 | ||||
| 1816 | AND item_level_request = 1 | ||||
| 1817 | AND itemnumber = ? | ||||
| 1818 | AND reservedate <= DATE_ADD(NOW(),INTERVAL ? DAY) | ||||
| 1819 | AND suspend = 0 | ||||
| 1820 | /; | ||||
| 1821 | my $sth = $dbh->prepare($item_level_target_query); | ||||
| 1822 | $sth->execute($itemnumber, $lookahead||0); | ||||
| 1823 | my @results; | ||||
| 1824 | if ( my $data = $sth->fetchrow_hashref ) { | ||||
| 1825 | push( @results, $data ); | ||||
| 1826 | } | ||||
| 1827 | return @results if @results; | ||||
| 1828 | |||||
| 1829 | # check for title-level targetted match | ||||
| 1830 | my $title_level_target_query = qq/ | ||||
| 1831 | SELECT reserves.biblionumber AS biblionumber, | ||||
| 1832 | reserves.borrowernumber AS borrowernumber, | ||||
| 1833 | reserves.reservedate AS reservedate, | ||||
| 1834 | reserves.branchcode AS branchcode, | ||||
| 1835 | reserves.cancellationdate AS cancellationdate, | ||||
| 1836 | reserves.found AS found, | ||||
| 1837 | reserves.reservenotes AS reservenotes, | ||||
| 1838 | reserves.priority AS priority, | ||||
| 1839 | reserves.timestamp AS timestamp, | ||||
| 1840 | biblioitems.biblioitemnumber AS biblioitemnumber, | ||||
| 1841 | reserves.itemnumber AS itemnumber, | ||||
| 1842 | reserves.reserve_id AS reserve_id | ||||
| 1843 | FROM reserves | ||||
| 1844 | JOIN biblioitems USING (biblionumber) | ||||
| 1845 | JOIN hold_fill_targets USING (biblionumber, borrowernumber) | ||||
| 1846 | WHERE found IS NULL | ||||
| 1847 | AND priority > 0 | ||||
| 1848 | AND item_level_request = 0 | ||||
| 1849 | AND hold_fill_targets.itemnumber = ? | ||||
| 1850 | AND reservedate <= DATE_ADD(NOW(),INTERVAL ? DAY) | ||||
| 1851 | AND suspend = 0 | ||||
| 1852 | /; | ||||
| 1853 | $sth = $dbh->prepare($title_level_target_query); | ||||
| 1854 | $sth->execute($itemnumber, $lookahead||0); | ||||
| 1855 | @results = (); | ||||
| 1856 | if ( my $data = $sth->fetchrow_hashref ) { | ||||
| 1857 | push( @results, $data ); | ||||
| 1858 | } | ||||
| 1859 | return @results if @results; | ||||
| 1860 | |||||
| 1861 | my $query = qq/ | ||||
| 1862 | SELECT reserves.biblionumber AS biblionumber, | ||||
| 1863 | reserves.borrowernumber AS borrowernumber, | ||||
| 1864 | reserves.reservedate AS reservedate, | ||||
| 1865 | reserves.waitingdate AS waitingdate, | ||||
| 1866 | reserves.branchcode AS branchcode, | ||||
| 1867 | reserves.cancellationdate AS cancellationdate, | ||||
| 1868 | reserves.found AS found, | ||||
| 1869 | reserves.reservenotes AS reservenotes, | ||||
| 1870 | reserves.priority AS priority, | ||||
| 1871 | reserves.timestamp AS timestamp, | ||||
| 1872 | reserveconstraints.biblioitemnumber AS biblioitemnumber, | ||||
| 1873 | reserves.itemnumber AS itemnumber, | ||||
| 1874 | reserves.reserve_id AS reserve_id | ||||
| 1875 | FROM reserves | ||||
| 1876 | LEFT JOIN reserveconstraints ON reserves.biblionumber = reserveconstraints.biblionumber | ||||
| 1877 | WHERE reserves.biblionumber = ? | ||||
| 1878 | AND ( ( reserveconstraints.biblioitemnumber = ? | ||||
| 1879 | AND reserves.borrowernumber = reserveconstraints.borrowernumber | ||||
| 1880 | AND reserves.reservedate = reserveconstraints.reservedate ) | ||||
| 1881 | OR reserves.constrainttype='a' ) | ||||
| 1882 | AND (reserves.itemnumber IS NULL OR reserves.itemnumber = ?) | ||||
| 1883 | AND reserves.reservedate <= DATE_ADD(NOW(),INTERVAL ? DAY) | ||||
| 1884 | AND suspend = 0 | ||||
| 1885 | /; | ||||
| 1886 | $sth = $dbh->prepare($query); | ||||
| 1887 | $sth->execute( $biblio, $bibitem, $itemnumber, $lookahead||0); | ||||
| 1888 | @results = (); | ||||
| 1889 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
| 1890 | push( @results, $data ); | ||||
| 1891 | } | ||||
| 1892 | return @results; | ||||
| 1893 | } | ||||
| 1894 | |||||
| 1895 | =head2 _koha_notify_reserve | ||||
| 1896 | |||||
| 1897 | _koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber ); | ||||
| 1898 | |||||
| 1899 | Sends a notification to the patron that their hold has been filled (through | ||||
| 1900 | ModReserveAffect, _not_ ModReserveFill) | ||||
| 1901 | |||||
| 1902 | =cut | ||||
| 1903 | |||||
| 1904 | sub _koha_notify_reserve { | ||||
| 1905 | my ($itemnumber, $borrowernumber, $biblionumber) = @_; | ||||
| 1906 | |||||
| 1907 | my $dbh = C4::Context->dbh; | ||||
| 1908 | my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber); | ||||
| 1909 | |||||
| 1910 | # Try to get the borrower's email address | ||||
| 1911 | my $to_address = C4::Members::GetNoticeEmailAddress($borrowernumber); | ||||
| 1912 | |||||
| 1913 | my $letter_code; | ||||
| 1914 | my $print_mode = 0; | ||||
| 1915 | my $messagingprefs; | ||||
| 1916 | if ( $to_address || $borrower->{'smsalertnumber'} ) { | ||||
| 1917 | $messagingprefs = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $borrowernumber, message_name => 'Hold_Filled' } ); | ||||
| 1918 | } else { | ||||
| 1919 | $print_mode = 1; | ||||
| 1920 | } | ||||
| 1921 | |||||
| 1922 | my $sth = $dbh->prepare(" | ||||
| 1923 | SELECT * | ||||
| 1924 | FROM reserves | ||||
| 1925 | WHERE borrowernumber = ? | ||||
| 1926 | AND biblionumber = ? | ||||
| 1927 | "); | ||||
| 1928 | $sth->execute( $borrowernumber, $biblionumber ); | ||||
| 1929 | my $reserve = $sth->fetchrow_hashref; | ||||
| 1930 | my $branch_details = GetBranchDetail( $reserve->{'branchcode'} ); | ||||
| 1931 | |||||
| 1932 | my $admin_email_address = $branch_details->{'branchemail'} || C4::Context->preference('KohaAdminEmailAddress'); | ||||
| 1933 | |||||
| 1934 | my %letter_params = ( | ||||
| 1935 | module => 'reserves', | ||||
| 1936 | branchcode => $reserve->{branchcode}, | ||||
| 1937 | tables => { | ||||
| 1938 | 'branches' => $branch_details, | ||||
| 1939 | 'borrowers' => $borrower, | ||||
| 1940 | 'biblio' => $biblionumber, | ||||
| 1941 | 'reserves' => $reserve, | ||||
| 1942 | 'items', $reserve->{'itemnumber'}, | ||||
| 1943 | }, | ||||
| 1944 | substitute => { today => C4::Dates->new()->output() }, | ||||
| 1945 | ); | ||||
| 1946 | |||||
| 1947 | |||||
| 1948 | if ( $print_mode ) { | ||||
| 1949 | $letter_params{ 'letter_code' } = 'HOLD_PRINT'; | ||||
| 1950 | my $letter = C4::Letters::GetPreparedLetter ( %letter_params ) or die "Could not find a letter called '$letter_params{'letter_code'}' in the 'reserves' module"; | ||||
| 1951 | |||||
| 1952 | C4::Letters::EnqueueLetter( { | ||||
| 1953 | letter => $letter, | ||||
| 1954 | borrowernumber => $borrowernumber, | ||||
| 1955 | message_transport_type => 'print', | ||||
| 1956 | } ); | ||||
| 1957 | |||||
| 1958 | return; | ||||
| 1959 | } | ||||
| 1960 | |||||
| 1961 | if ( $to_address && defined $messagingprefs->{transports}->{'email'} ) { | ||||
| 1962 | $letter_params{ 'letter_code' } = $messagingprefs->{transports}->{'email'}; | ||||
| 1963 | my $letter = C4::Letters::GetPreparedLetter ( %letter_params ) or die "Could not find a letter called '$letter_params{'letter_code'}' in the 'reserves' module"; | ||||
| 1964 | |||||
| 1965 | C4::Letters::EnqueueLetter( | ||||
| 1966 | { letter => $letter, | ||||
| 1967 | borrowernumber => $borrowernumber, | ||||
| 1968 | message_transport_type => 'email', | ||||
| 1969 | from_address => $admin_email_address, | ||||
| 1970 | } | ||||
| 1971 | ); | ||||
| 1972 | } | ||||
| 1973 | |||||
| 1974 | if ( $borrower->{'smsalertnumber'} && defined $messagingprefs->{transports}->{'sms'} ) { | ||||
| 1975 | $letter_params{ 'letter_code' } = $messagingprefs->{transports}->{'sms'}; | ||||
| 1976 | my $letter = C4::Letters::GetPreparedLetter ( %letter_params ) or die "Could not find a letter called '$letter_params{'letter_code'}' in the 'reserves' module"; | ||||
| 1977 | |||||
| 1978 | C4::Letters::EnqueueLetter( | ||||
| 1979 | { letter => $letter, | ||||
| 1980 | borrowernumber => $borrowernumber, | ||||
| 1981 | message_transport_type => 'sms', | ||||
| 1982 | } | ||||
| 1983 | ); | ||||
| 1984 | } | ||||
| 1985 | } | ||||
| 1986 | |||||
| 1987 | =head2 _ShiftPriorityByDateAndPriority | ||||
| 1988 | |||||
| 1989 | $new_priority = _ShiftPriorityByDateAndPriority( $biblionumber, $reservedate, $priority ); | ||||
| 1990 | |||||
| 1991 | This increments the priority of all reserves after the one | ||||
| 1992 | with either the lowest date after C<$reservedate> | ||||
| 1993 | or the lowest priority after C<$priority>. | ||||
| 1994 | |||||
| 1995 | It effectively makes room for a new reserve to be inserted with a certain | ||||
| 1996 | priority, which is returned. | ||||
| 1997 | |||||
| 1998 | This is most useful when the reservedate can be set by the user. It allows | ||||
| 1999 | the new reserve to be placed before other reserves that have a later | ||||
| 2000 | reservedate. Since priority also is set by the form in reserves/request.pl | ||||
| 2001 | the sub accounts for that too. | ||||
| 2002 | |||||
| 2003 | =cut | ||||
| 2004 | |||||
| 2005 | sub _ShiftPriorityByDateAndPriority { | ||||
| 2006 | my ( $biblio, $resdate, $new_priority ) = @_; | ||||
| 2007 | |||||
| 2008 | my $dbh = C4::Context->dbh; | ||||
| 2009 | my $query = "SELECT priority FROM reserves WHERE biblionumber = ? AND ( reservedate > ? OR priority > ? ) ORDER BY priority ASC LIMIT 1"; | ||||
| 2010 | my $sth = $dbh->prepare( $query ); | ||||
| 2011 | $sth->execute( $biblio, $resdate, $new_priority ); | ||||
| 2012 | my $min_priority = $sth->fetchrow; | ||||
| 2013 | # if no such matches are found, $new_priority remains as original value | ||||
| 2014 | $new_priority = $min_priority if ( $min_priority ); | ||||
| 2015 | |||||
| 2016 | # Shift the priority up by one; works in conjunction with the next SQL statement | ||||
| 2017 | $query = "UPDATE reserves | ||||
| 2018 | SET priority = priority+1 | ||||
| 2019 | WHERE biblionumber = ? | ||||
| 2020 | AND borrowernumber = ? | ||||
| 2021 | AND reservedate = ? | ||||
| 2022 | AND found IS NULL"; | ||||
| 2023 | my $sth_update = $dbh->prepare( $query ); | ||||
| 2024 | |||||
| 2025 | # Select all reserves for the biblio with priority greater than $new_priority, and order greatest to least | ||||
| 2026 | $query = "SELECT borrowernumber, reservedate FROM reserves WHERE priority >= ? AND biblionumber = ? ORDER BY priority DESC"; | ||||
| 2027 | $sth = $dbh->prepare( $query ); | ||||
| 2028 | $sth->execute( $new_priority, $biblio ); | ||||
| 2029 | while ( my $row = $sth->fetchrow_hashref ) { | ||||
| 2030 | $sth_update->execute( $biblio, $row->{borrowernumber}, $row->{reservedate} ); | ||||
| 2031 | } | ||||
| 2032 | |||||
| 2033 | return $new_priority; # so the caller knows what priority they wind up receiving | ||||
| 2034 | } | ||||
| 2035 | |||||
| 2036 | =head2 MoveReserve | ||||
| 2037 | |||||
| 2038 | MoveReserve( $itemnumber, $borrowernumber, $cancelreserve ) | ||||
| 2039 | |||||
| 2040 | Use when checking out an item to handle reserves | ||||
| 2041 | If $cancelreserve boolean is set to true, it will remove existing reserve | ||||
| 2042 | |||||
| 2043 | =cut | ||||
| 2044 | |||||
| 2045 | sub MoveReserve { | ||||
| 2046 | my ( $itemnumber, $borrowernumber, $cancelreserve ) = @_; | ||||
| 2047 | |||||
| 2048 | my ( $restype, $res, $all_reserves ) = CheckReserves( $itemnumber ); | ||||
| 2049 | return unless $res; | ||||
| 2050 | |||||
| 2051 | my $biblionumber = $res->{biblionumber}; | ||||
| 2052 | my $biblioitemnumber = $res->{biblioitemnumber}; | ||||
| 2053 | |||||
| 2054 | if ($res->{borrowernumber} == $borrowernumber) { | ||||
| 2055 | ModReserveFill($res); | ||||
| 2056 | } | ||||
| 2057 | else { | ||||
| 2058 | # warn "Reserved"; | ||||
| 2059 | # The item is reserved by someone else. | ||||
| 2060 | # Find this item in the reserves | ||||
| 2061 | |||||
| 2062 | my $borr_res; | ||||
| 2063 | foreach (@$all_reserves) { | ||||
| 2064 | $_->{'borrowernumber'} == $borrowernumber or next; | ||||
| 2065 | $_->{'biblionumber'} == $biblionumber or next; | ||||
| 2066 | |||||
| 2067 | $borr_res = $_; | ||||
| 2068 | last; | ||||
| 2069 | } | ||||
| 2070 | |||||
| 2071 | if ( $borr_res ) { | ||||
| 2072 | # The item is reserved by the current patron | ||||
| 2073 | ModReserveFill($borr_res); | ||||
| 2074 | } | ||||
| 2075 | |||||
| 2076 | if ( $cancelreserve eq 'revert' ) { ## Revert waiting reserve to priority 1 | ||||
| 2077 | RevertWaitingStatus({ itemnumber => $itemnumber }); | ||||
| 2078 | } | ||||
| 2079 | elsif ( $cancelreserve eq 'cancel' || $cancelreserve ) { # cancel reserves on this item | ||||
| 2080 | CancelReserve({ | ||||
| 2081 | biblionumber => $res->{'biblionumber'}, | ||||
| 2082 | itemnumber => $res->{'itemnumber'}, | ||||
| 2083 | borrowernumber => $res->{'borrowernumber'} | ||||
| 2084 | }); | ||||
| 2085 | } | ||||
| 2086 | } | ||||
| 2087 | } | ||||
| 2088 | |||||
| 2089 | =head2 MergeHolds | ||||
| 2090 | |||||
| 2091 | MergeHolds($dbh,$to_biblio, $from_biblio); | ||||
| 2092 | |||||
| 2093 | This shifts the holds from C<$from_biblio> to C<$to_biblio> and reorders them by the date they were placed | ||||
| 2094 | |||||
| 2095 | =cut | ||||
| 2096 | |||||
| 2097 | sub MergeHolds { | ||||
| 2098 | my ( $dbh, $to_biblio, $from_biblio ) = @_; | ||||
| 2099 | my $sth = $dbh->prepare( | ||||
| 2100 | "SELECT count(*) as reserve_count FROM reserves WHERE biblionumber = ?" | ||||
| 2101 | ); | ||||
| 2102 | $sth->execute($from_biblio); | ||||
| 2103 | if ( my $data = $sth->fetchrow_hashref() ) { | ||||
| 2104 | |||||
| 2105 | # holds exist on old record, if not we don't need to do anything | ||||
| 2106 | $sth = $dbh->prepare( | ||||
| 2107 | "UPDATE reserves SET biblionumber = ? WHERE biblionumber = ?"); | ||||
| 2108 | $sth->execute( $to_biblio, $from_biblio ); | ||||
| 2109 | |||||
| 2110 | # Reorder by date | ||||
| 2111 | # don't reorder those already waiting | ||||
| 2112 | |||||
| 2113 | $sth = $dbh->prepare( | ||||
| 2114 | "SELECT * FROM reserves WHERE biblionumber = ? AND (found <> ? AND found <> ? OR found is NULL) ORDER BY reservedate ASC" | ||||
| 2115 | ); | ||||
| 2116 | my $upd_sth = $dbh->prepare( | ||||
| 2117 | "UPDATE reserves SET priority = ? WHERE biblionumber = ? AND borrowernumber = ? | ||||
| 2118 | AND reservedate = ? AND constrainttype = ? AND (itemnumber = ? or itemnumber is NULL) " | ||||
| 2119 | ); | ||||
| 2120 | $sth->execute( $to_biblio, 'W', 'T' ); | ||||
| 2121 | my $priority = 1; | ||||
| 2122 | while ( my $reserve = $sth->fetchrow_hashref() ) { | ||||
| 2123 | $upd_sth->execute( | ||||
| 2124 | $priority, $to_biblio, | ||||
| 2125 | $reserve->{'borrowernumber'}, $reserve->{'reservedate'}, | ||||
| 2126 | $reserve->{'constrainttype'}, $reserve->{'itemnumber'} | ||||
| 2127 | ); | ||||
| 2128 | $priority++; | ||||
| 2129 | } | ||||
| 2130 | } | ||||
| 2131 | } | ||||
| 2132 | |||||
| 2133 | =head2 RevertWaitingStatus | ||||
| 2134 | |||||
| 2135 | $success = RevertWaitingStatus({ itemnumber => $itemnumber }); | ||||
| 2136 | |||||
| 2137 | Reverts a 'waiting' hold back to a regular hold with a priority of 1. | ||||
| 2138 | |||||
| 2139 | Caveat: Any waiting hold fixed with RevertWaitingStatus will be an | ||||
| 2140 | item level hold, even if it was only a bibliolevel hold to | ||||
| 2141 | begin with. This is because we can no longer know if a hold | ||||
| 2142 | was item-level or bib-level after a hold has been set to | ||||
| 2143 | waiting status. | ||||
| 2144 | |||||
| 2145 | =cut | ||||
| 2146 | |||||
| 2147 | sub RevertWaitingStatus { | ||||
| 2148 | my ( $params ) = @_; | ||||
| 2149 | my $itemnumber = $params->{'itemnumber'}; | ||||
| 2150 | |||||
| 2151 | return unless ( $itemnumber ); | ||||
| 2152 | |||||
| 2153 | my $dbh = C4::Context->dbh; | ||||
| 2154 | |||||
| 2155 | ## Get the waiting reserve we want to revert | ||||
| 2156 | my $query = " | ||||
| 2157 | SELECT * FROM reserves | ||||
| 2158 | WHERE itemnumber = ? | ||||
| 2159 | AND found IS NOT NULL | ||||
| 2160 | "; | ||||
| 2161 | my $sth = $dbh->prepare( $query ); | ||||
| 2162 | $sth->execute( $itemnumber ); | ||||
| 2163 | my $reserve = $sth->fetchrow_hashref(); | ||||
| 2164 | |||||
| 2165 | ## Increment the priority of all other non-waiting | ||||
| 2166 | ## reserves for this bib record | ||||
| 2167 | $query = " | ||||
| 2168 | UPDATE reserves | ||||
| 2169 | SET | ||||
| 2170 | priority = priority + 1 | ||||
| 2171 | WHERE | ||||
| 2172 | biblionumber = ? | ||||
| 2173 | AND | ||||
| 2174 | priority > 0 | ||||
| 2175 | "; | ||||
| 2176 | $sth = $dbh->prepare( $query ); | ||||
| 2177 | $sth->execute( $reserve->{'biblionumber'} ); | ||||
| 2178 | |||||
| 2179 | ## Fix up the currently waiting reserve | ||||
| 2180 | $query = " | ||||
| 2181 | UPDATE reserves | ||||
| 2182 | SET | ||||
| 2183 | priority = 1, | ||||
| 2184 | found = NULL, | ||||
| 2185 | waitingdate = NULL | ||||
| 2186 | WHERE | ||||
| 2187 | reserve_id = ? | ||||
| 2188 | "; | ||||
| 2189 | $sth = $dbh->prepare( $query ); | ||||
| 2190 | return $sth->execute( $reserve->{'reserve_id'} ); | ||||
| 2191 | } | ||||
| 2192 | |||||
| 2193 | =head2 GetReserveId | ||||
| 2194 | |||||
| 2195 | $reserve_id = GetReserveId({ biblionumber => $biblionumber, borrowernumber => $borrowernumber [, itemnumber => $itemnumber ] }); | ||||
| 2196 | |||||
| 2197 | Returnes the first reserve id that matches the given criteria | ||||
| 2198 | |||||
| 2199 | =cut | ||||
| 2200 | |||||
| 2201 | sub GetReserveId { | ||||
| 2202 | my ( $params ) = @_; | ||||
| 2203 | |||||
| 2204 | return unless ( ( $params->{'biblionumber'} || $params->{'itemnumber'} ) && $params->{'borrowernumber'} ); | ||||
| 2205 | |||||
| 2206 | my $dbh = C4::Context->dbh(); | ||||
| 2207 | |||||
| 2208 | my $sql = "SELECT reserve_id FROM reserves WHERE "; | ||||
| 2209 | |||||
| 2210 | my @params; | ||||
| 2211 | my @limits; | ||||
| 2212 | foreach my $key ( keys %$params ) { | ||||
| 2213 | if ( defined( $params->{$key} ) ) { | ||||
| 2214 | push( @limits, "$key = ?" ); | ||||
| 2215 | push( @params, $params->{$key} ); | ||||
| 2216 | } | ||||
| 2217 | } | ||||
| 2218 | |||||
| 2219 | $sql .= join( " AND ", @limits ); | ||||
| 2220 | |||||
| 2221 | my $sth = $dbh->prepare( $sql ); | ||||
| 2222 | $sth->execute( @params ); | ||||
| 2223 | my $row = $sth->fetchrow_hashref(); | ||||
| 2224 | |||||
| 2225 | return $row->{'reserve_id'}; | ||||
| 2226 | } | ||||
| 2227 | |||||
| 2228 | =head2 ReserveSlip | ||||
| 2229 | |||||
| 2230 | ReserveSlip($branchcode, $borrowernumber, $biblionumber) | ||||
| 2231 | |||||
| 2232 | Returns letter hash ( see C4::Letters::GetPreparedLetter ) or undef | ||||
| 2233 | |||||
| 2234 | =cut | ||||
| 2235 | |||||
| 2236 | sub ReserveSlip { | ||||
| 2237 | my ($branch, $borrowernumber, $biblionumber) = @_; | ||||
| 2238 | |||||
| 2239 | # return unless ( C4::Context->boolean_preference('printreserveslips') ); | ||||
| 2240 | |||||
| 2241 | my $reserve_id = GetReserveId({ | ||||
| 2242 | biblionumber => $biblionumber, | ||||
| 2243 | borrowernumber => $borrowernumber | ||||
| 2244 | }) or return; | ||||
| 2245 | my $reserve = GetReserveInfo($reserve_id) or return; | ||||
| 2246 | |||||
| 2247 | return C4::Letters::GetPreparedLetter ( | ||||
| 2248 | module => 'circulation', | ||||
| 2249 | letter_code => 'RESERVESLIP', | ||||
| 2250 | branchcode => $branch, | ||||
| 2251 | tables => { | ||||
| 2252 | 'reserves' => $reserve, | ||||
| 2253 | 'branches' => $reserve->{branchcode}, | ||||
| 2254 | 'borrowers' => $reserve->{borrowernumber}, | ||||
| 2255 | 'biblio' => $reserve->{biblionumber}, | ||||
| 2256 | 'items' => $reserve->{itemnumber}, | ||||
| 2257 | }, | ||||
| 2258 | ); | ||||
| 2259 | } | ||||
| 2260 | |||||
| 2261 | =head2 GetReservesControlBranch | ||||
| 2262 | |||||
| 2263 | my $reserves_control_branch = GetReservesControlBranch($item, $borrower); | ||||
| 2264 | |||||
| 2265 | Return the branchcode to be used to determine which reserves | ||||
| 2266 | policy applies to a transaction. | ||||
| 2267 | |||||
| 2268 | C<$item> is a hashref for an item. Only 'homebranch' is used. | ||||
| 2269 | |||||
| 2270 | C<$borrower> is a hashref to borrower. Only 'branchcode' is used. | ||||
| 2271 | |||||
| 2272 | =cut | ||||
| 2273 | |||||
| 2274 | sub GetReservesControlBranch { | ||||
| 2275 | my ( $item, $borrower ) = @_; | ||||
| 2276 | |||||
| 2277 | my $reserves_control = C4::Context->preference('ReservesControlBranch'); | ||||
| 2278 | |||||
| 2279 | my $branchcode = | ||||
| 2280 | ( $reserves_control eq 'ItemHomeLibrary' ) ? $item->{'homebranch'} | ||||
| 2281 | : ( $reserves_control eq 'PatronLibrary' ) ? $borrower->{'branchcode'} | ||||
| 2282 | : undef; | ||||
| 2283 | |||||
| 2284 | return $branchcode; | ||||
| 2285 | } | ||||
| 2286 | |||||
| 2287 | =head2 CalculatePriority | ||||
| 2288 | |||||
| 2289 | my $p = CalculatePriority($biblionumber, $resdate); | ||||
| 2290 | |||||
| 2291 | Calculate priority for a new reserve on biblionumber, placing it at | ||||
| 2292 | the end of the line of all holds whose start date falls before | ||||
| 2293 | the current system time and that are neither on the hold shelf | ||||
| 2294 | or in transit. | ||||
| 2295 | |||||
| 2296 | The reserve date parameter is optional; if it is supplied, the | ||||
| 2297 | priority is based on the set of holds whose start date falls before | ||||
| 2298 | the parameter value. | ||||
| 2299 | |||||
| 2300 | After calculation of this priority, it is recommended to call | ||||
| 2301 | _ShiftPriorityByDateAndPriority. Note that this is currently done in | ||||
| 2302 | AddReserves. | ||||
| 2303 | |||||
| 2304 | =cut | ||||
| 2305 | |||||
| 2306 | sub CalculatePriority { | ||||
| 2307 | my ( $biblionumber, $resdate ) = @_; | ||||
| 2308 | |||||
| 2309 | my $sql = q{ | ||||
| 2310 | SELECT COUNT(*) FROM reserves | ||||
| 2311 | WHERE biblionumber = ? | ||||
| 2312 | AND priority > 0 | ||||
| 2313 | AND (found IS NULL OR found = '') | ||||
| 2314 | }; | ||||
| 2315 | #skip found==W or found==T (waiting or transit holds) | ||||
| 2316 | if( $resdate ) { | ||||
| 2317 | $sql.= ' AND ( reservedate <= ? )'; | ||||
| 2318 | } | ||||
| 2319 | else { | ||||
| 2320 | $sql.= ' AND ( reservedate < NOW() )'; | ||||
| 2321 | } | ||||
| 2322 | my $dbh = C4::Context->dbh(); | ||||
| 2323 | my @row = $dbh->selectrow_array( | ||||
| 2324 | $sql, | ||||
| 2325 | undef, | ||||
| 2326 | $resdate ? ($biblionumber, $resdate) : ($biblionumber) | ||||
| 2327 | ); | ||||
| 2328 | |||||
| 2329 | return @row ? $row[0]+1 : 1; | ||||
| 2330 | } | ||||
| 2331 | |||||
| 2332 | =head1 AUTHOR | ||||
| 2333 | |||||
| 2334 | Koha Development Team <http://koha-community.org/> | ||||
| 2335 | |||||
| 2336 | =cut | ||||
| 2337 | |||||
| 2338 | 1 | 3µs | 1; |