Add print slip to hold confirmation dialog.
[koha.git] / C4 / Reserves.pm
1 # -*- tab-width: 8 -*-
2 # NOTE: This file uses standard 8-character tabs
3
4 package C4::Reserves;
5
6 # Copyright 2000-2002 Katipo Communications
7 #           2006 SAN Ouest Provence
8 #           2007 BibLibre Paul POULAIN
9 #
10 # This file is part of Koha.
11 #
12 # Koha is free software; you can redistribute it and/or modify it under the
13 # terms of the GNU General Public License as published by the Free Software
14 # Foundation; either version 2 of the License, or (at your option) any later
15 # version.
16 #
17 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
18 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
19 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License along with
22 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23 # Suite 330, Boston, MA  02111-1307 USA
24
25
26 use strict;
27 use C4::Context;
28 use C4::Biblio;
29 use C4::Items;
30 use C4::Search;
31 use C4::Circulation;
32 use C4::Accounts;
33
34 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
35
36 my $library_name = C4::Context->preference("LibraryName");
37
38 =head1 NAME
39
40 C4::Reserves - Koha functions for dealing with reservation.
41
42 =head1 SYNOPSIS
43
44   use C4::Reserves;
45
46 =head1 DESCRIPTION
47
48   this modules provides somes functions to deal with reservations.
49   
50   Reserves are stored in reserves table.
51   The following columns contains important values :
52   - priority >0      : then the reserve is at 1st stage, and not yet affected to any item.
53              =0      : then the reserve is being dealed
54   - found : NULL       : means the patron requested the 1st available, and we haven't choosen the item
55             W(aiting)  : the reserve has an itemnumber affected, and is on the way
56             F(inished) : the reserve has been completed, and is done
57   - itemnumber : empty : the reserve is still unaffected to an item
58                  filled: the reserve is attached to an item
59   The complete workflow is :
60   ==== 1st use case ====
61   patron request a document, 1st available :                      P >0, F=NULL, I=NULL
62   a library having it run "transfertodo", and clic on the list    
63          if there is no transfer to do, the reserve waiting
64          patron can pick it up                                    P =0, F=W,    I=filled 
65          if there is a transfer to do, write in branchtransfer    P =0, F=NULL, I=filled
66            The pickup library recieve the book, it check in       P =0, F=W,    I=filled
67   The patron borrow the book                                      P =0, F=F,    I=filled
68   
69   ==== 2nd use case ====
70   patron requests a document, a given item,
71     If pickup is holding branch                                   P =0, F=W,   I=filled
72     If transfer needed, write in branchtransfer                   P =0, F=NULL, I=filled
73         The pickup library recieve the book, it checks it in      P =0, F=W,    I=filled
74   The patron borrow the book                                      P =0, F=F,    I=filled
75   
76 =head1 FUNCTIONS
77
78 =over 2
79
80 =cut
81
82 BEGIN {
83     # set the version for version checking
84     $VERSION = 3.01;
85         require Exporter;
86     @ISA = qw(Exporter);
87     @EXPORT = qw(
88         &AddReserve
89   
90         &GetReservesFromItemnumber
91         &GetReservesFromBiblionumber
92         &GetReservesFromBorrowernumber
93         &GetReservesForBranch
94         &GetReservesToBranch
95         &GetReserveCount
96         &GetReserveFee
97                 &GetReserveInfo
98     
99         &GetOtherReserves
100         
101         &ModReserveFill
102         &ModReserveAffect
103         &ModReserve
104         &ModReserveStatus
105         &ModReserveCancelAll
106         &ModReserveMinusPriority
107         
108         &CheckReserves
109         &CancelReserve
110     );
111 }    
112
113 =item AddReserve
114
115     AddReserve($branch,$borrowernumber,$biblionumber,$constraint,$bibitems,$priority,$notes,$title,$checkitem,$found)
116
117 =cut
118
119 sub AddReserve {
120     my (
121         $branch,    $borrowernumber, $biblionumber,
122         $constraint, $bibitems,  $priority,       $notes,
123         $title,      $checkitem, $found
124     ) = @_;
125     my $fee =
126           GetReserveFee($borrowernumber, $biblionumber, $constraint,
127             $bibitems );
128     my $dbh     = C4::Context->dbh;
129     my $const   = lc substr( $constraint, 0, 1 );
130     my @datearr = localtime(time);
131     my $resdate =
132       ( 1900 + $datearr[5] ) . "-" . ( $datearr[4] + 1 ) . "-" . $datearr[3];
133     my $waitingdate;
134
135     # If the reserv had the waiting status, we had the value of the resdate
136     if ( $found eq 'W' ) {
137         $waitingdate = $resdate;
138     }
139
140     #eval {
141     # updates take place here
142     if ( $fee > 0 ) {
143         my $nextacctno = &getnextacctno( $borrowernumber );
144         my $query      = qq/
145         INSERT INTO accountlines
146             (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding)
147         VALUES
148             (?,?,now(),?,?,'Res',?)
149     /;
150         my $usth = $dbh->prepare($query);
151         $usth->execute( $borrowernumber, $nextacctno, $fee,
152             "Reserve Charge - $title", $fee );
153         $usth->finish;
154     }
155
156     #if ($const eq 'a'){
157     my $query = qq/
158         INSERT INTO reserves
159             (borrowernumber,biblionumber,reservedate,branchcode,constrainttype,
160             priority,reservenotes,itemnumber,found,waitingdate)
161         VALUES
162              (?,?,?,?,?,
163              ?,?,?,?,?)
164     /;
165     my $sth = $dbh->prepare($query);
166     $sth->execute(
167         $borrowernumber, $biblionumber, $resdate, $branch,
168         $const,          $priority,     $notes,   $checkitem,
169         $found,          $waitingdate
170     );
171     $sth->finish;
172
173     #}
174     if ( ( $const eq "o" ) || ( $const eq "e" ) ) {
175         my $numitems = @$bibitems;
176         my $i        = 0;
177         while ( $i < $numitems ) {
178             my $biblioitem = @$bibitems[$i];
179             my $query      = qq/
180           INSERT INTO reserveconstraints
181               (borrowernumber,biblionumber,reservedate,biblioitemnumber)
182           VALUES
183             (?,?,?,?)
184       /;
185             my $sth = $dbh->prepare("");
186             $sth->execute( $borrowernumber, $biblionumber, $resdate,
187                 $biblioitem );
188             $sth->finish;
189             $i++;
190         }
191     }
192     return;
193 }
194
195 =item GetReservesFromBiblionumber
196
197 @borrowerreserv=&GetReserves($biblionumber,$itemnumber,$borrowernumber);
198
199 this function get the list of reservation for an C<$biblionumber>, C<$itemnumber> or C<$borrowernumber>
200 given on input arg. 
201 Only 1 argument has to be passed.
202
203 =cut
204
205 sub GetReservesFromBiblionumber {
206     my ( $biblionumber, $itemnumber, $borrowernumber ) = @_;
207     my $dbh   = C4::Context->dbh;
208
209     # Find the desired items in the reserves
210     my $query = "
211         SELECT  branchcode,
212                 timestamp AS rtimestamp,
213                 priority,
214                 biblionumber,
215                 borrowernumber,
216                 reservedate,
217                 constrainttype,
218                 found,
219                 itemnumber,
220                 reservenotes
221         FROM     reserves
222         WHERE biblionumber = ?
223         ORDER BY priority";
224     my $sth = $dbh->prepare($query);
225     $sth->execute($biblionumber);
226     my @results;
227     my $i = 0;
228     while ( my $data = $sth->fetchrow_hashref ) {
229
230         # FIXME - What is this if-statement doing? How do constraints work?
231         if ( $data->{constrainttype} eq 'o' ) {
232             $query = '
233                 SELECT biblioitemnumber
234                 FROM reserveconstraints
235                 WHERE biblionumber   = ?
236                     AND borrowernumber = ?
237                 AND reservedate    = ?
238             ';
239             my $csth = $dbh->prepare($query);
240             $csth->execute( $data->{biblionumber}, $data->{borrowernumber},
241                 $data->{reservedate}, );
242
243             my @bibitemno;
244             while ( my $bibitemnos = $csth->fetchrow_array ) {
245                 push( @bibitemno, $bibitemnos );
246             }
247             my $count = @bibitemno;
248
249             # if we have two or more different specific itemtypes
250             # reserved by same person on same day
251             my $bdata;
252             if ( $count > 1 ) {
253                 $bdata = GetBiblioItemData( $bibitemno[$i] );
254                 $i++;
255             }
256             else {
257
258                 # Look up the book we just found.
259                 $bdata = GetBiblioItemData( $bibitemno[0] );
260             }
261             $csth->finish;
262
263             # Add the results of this latest search to the current
264             # results.
265             # FIXME - An 'each' would probably be more efficient.
266             foreach my $key ( keys %$bdata ) {
267                 $data->{$key} = $bdata->{$key};
268             }
269         }
270         push @results, $data;
271     }
272     $sth->finish;
273     return ( $#results + 1, \@results );
274 }
275
276 =item GetReservesFromItemnumber
277
278  ( $reservedate, $borrowernumber, $branchcode ) = GetReservesFromItemnumber($itemnumber);
279
280    TODO :: Description here
281
282 =cut
283
284 sub GetReservesFromItemnumber {
285     my ( $itemnumber ) = @_;
286     my $dbh   = C4::Context->dbh;
287     my $query = "
288     SELECT reservedate,borrowernumber,branchcode
289     FROM   reserves
290     WHERE  itemnumber=?
291     ";
292     my $sth_res = $dbh->prepare($query);
293     $sth_res->execute($itemnumber);
294     my ( $reservedate, $borrowernumber,$branchcode ) = $sth_res->fetchrow_array;
295     return ( $reservedate, $borrowernumber, $branchcode );
296 }
297
298 =item GetReservesFromBorrowernumber
299
300     $borrowerreserv = GetReservesFromBorrowernumber($borrowernumber,$tatus);
301     
302     TODO :: Descritpion
303     
304 =cut
305
306 sub GetReservesFromBorrowernumber {
307     my ( $borrowernumber, $status ) = @_;
308     my $dbh   = C4::Context->dbh;
309     my $sth;
310     if ($status) {
311         $sth = $dbh->prepare("
312             SELECT *
313             FROM   reserves
314             WHERE  borrowernumber=?
315                 AND found =?
316             ORDER BY reservedate
317         ");
318         $sth->execute($borrowernumber,$status);
319     } else {
320         $sth = $dbh->prepare("
321             SELECT *
322             FROM   reserves
323             WHERE  borrowernumber=?
324             ORDER BY reservedate
325         ");
326         $sth->execute($borrowernumber);
327     }
328     my $data = $sth->fetchall_arrayref({});
329     return @$data;
330 }
331 #-------------------------------------------------------------------------------------
332
333 =item GetReserveCount
334
335 $number = &GetReserveCount($borrowernumber);
336
337 this function returns the number of reservation for a borrower given on input arg.
338
339 =cut
340
341 sub GetReserveCount {
342     my ($borrowernumber) = @_;
343
344     my $dbh = C4::Context->dbh;
345
346     my $query = '
347         SELECT COUNT(*) AS counter
348         FROM reserves
349           WHERE borrowernumber = ?
350     ';
351     my $sth = $dbh->prepare($query);
352     $sth->execute($borrowernumber);
353     my $row = $sth->fetchrow_hashref;
354     $sth->finish;
355
356     return $row->{counter};
357 }
358
359 =item GetOtherReserves
360
361 ($messages,$nextreservinfo)=$GetOtherReserves(itemnumber);
362
363 Check queued list of this document and check if this document must be  transfered
364
365 =cut
366
367 sub GetOtherReserves {
368     my ($itemnumber) = @_;
369     my $messages;
370     my $nextreservinfo;
371     my ( $restype, $checkreserves ) = CheckReserves($itemnumber);
372     if ($checkreserves) {
373         my $iteminfo = GetItem($itemnumber);
374         if ( $iteminfo->{'holdingbranch'} ne $checkreserves->{'branchcode'} ) {
375             $messages->{'transfert'} = $checkreserves->{'branchcode'};
376             #minus priorities of others reservs
377             ModReserveMinusPriority(
378                 $itemnumber,
379                 $checkreserves->{'borrowernumber'},
380                 $iteminfo->{'biblionumber'}
381             );
382
383             #launch the subroutine dotransfer
384             C4::Circulation::ModItemTransfer(
385                 $itemnumber,
386                 $iteminfo->{'holdingbranch'},
387                 $checkreserves->{'branchcode'}
388               ),
389               ;
390         }
391
392      #step 2b : case of a reservation on the same branch, set the waiting status
393         else {
394             $messages->{'waiting'} = 1;
395             ModReserveMinusPriority(
396                 $itemnumber,
397                 $checkreserves->{'borrowernumber'},
398                 $iteminfo->{'biblionumber'}
399             );
400             ModReserveStatus($itemnumber,'W');
401         }
402
403         $nextreservinfo = $checkreserves->{'borrowernumber'};
404     }
405
406     return ( $messages, $nextreservinfo );
407 }
408
409 =item GetReserveFee
410
411 $fee = GetReserveFee($borrowernumber,$biblionumber,$constraint,$biblionumber);
412
413 Calculate the fee for a reserve
414
415 =cut
416
417 sub GetReserveFee {
418     my ($borrowernumber, $biblionumber, $constraint, $bibitems ) = @_;
419
420     #check for issues;
421     my $dbh   = C4::Context->dbh;
422     my $const = lc substr( $constraint, 0, 1 );
423     my $query = qq/
424       SELECT * FROM borrowers
425     LEFT JOIN categories ON borrowers.categorycode = categories.categorycode
426     WHERE borrowernumber = ?
427     /;
428     my $sth = $dbh->prepare($query);
429     $sth->execute($borrowernumber);
430     my $data = $sth->fetchrow_hashref;
431     $sth->finish();
432     my $fee      = $data->{'reservefee'};
433     my $cntitems = @- > $bibitems;
434
435     if ( $fee > 0 ) {
436
437         # check for items on issue
438         # first find biblioitem records
439         my @biblioitems;
440         my $sth1 = $dbh->prepare(
441             "SELECT * FROM biblio LEFT JOIN biblioitems on biblio.biblionumber = biblioitems.biblionumber
442                    WHERE (biblio.biblionumber = ?)"
443         );
444         $sth1->execute($biblionumber);
445         while ( my $data1 = $sth1->fetchrow_hashref ) {
446             if ( $const eq "a" ) {
447                 push @biblioitems, $data1;
448             }
449             else {
450                 my $found = 0;
451                 my $x     = 0;
452                 while ( $x < $cntitems ) {
453                     if ( @$bibitems->{'biblioitemnumber'} ==
454                         $data->{'biblioitemnumber'} )
455                     {
456                         $found = 1;
457                     }
458                     $x++;
459                 }
460                 if ( $const eq 'o' ) {
461                     if ( $found == 1 ) {
462                         push @biblioitems, $data1;
463                     }
464                 }
465                 else {
466                     if ( $found == 0 ) {
467                         push @biblioitems, $data1;
468                     }
469                 }
470             }
471         }
472         $sth1->finish;
473         my $cntitemsfound = @biblioitems;
474         my $issues        = 0;
475         my $x             = 0;
476         my $allissued     = 1;
477         while ( $x < $cntitemsfound ) {
478             my $bitdata = $biblioitems[$x];
479             my $sth2    = $dbh->prepare(
480                 "SELECT * FROM items
481                      WHERE biblioitemnumber = ?"
482             );
483             $sth2->execute( $bitdata->{'biblioitemnumber'} );
484             while ( my $itdata = $sth2->fetchrow_hashref ) {
485                 my $sth3 = $dbh->prepare(
486                     "SELECT * FROM issues
487                        WHERE itemnumber = ?"
488                 );
489                 $sth3->execute( $itdata->{'itemnumber'} );
490                 if ( my $isdata = $sth3->fetchrow_hashref ) {
491                 }
492                 else {
493                     $allissued = 0;
494                 }
495             }
496             $x++;
497         }
498         if ( $allissued == 0 ) {
499             my $rsth =
500               $dbh->prepare("SELECT * FROM reserves WHERE biblionumber = ?");
501             $rsth->execute($biblionumber);
502             if ( my $rdata = $rsth->fetchrow_hashref ) {
503             }
504             else {
505                 $fee = 0;
506             }
507         }
508     }
509     return $fee;
510 }
511
512 =item GetReservesToBranch
513
514 @transreserv = GetReservesToBranch( $frombranch );
515
516 Get reserve list for a given branch
517
518 =cut
519
520 sub GetReservesToBranch {
521     my ( $frombranch ) = @_;
522     my $dbh = C4::Context->dbh;
523     my $sth = $dbh->prepare(
524         "SELECT borrowernumber,reservedate,itemnumber,timestamp
525          FROM reserves 
526          WHERE priority='0' 
527            AND branchcode=?"
528     );
529     $sth->execute( $frombranch );
530     my @transreserv;
531     my $i = 0;
532     while ( my $data = $sth->fetchrow_hashref ) {
533         $transreserv[$i] = $data;
534         $i++;
535     }
536     $sth->finish;
537     return (@transreserv);
538 }
539
540 =item GetReservesForBranch
541
542 @transreserv = GetReservesForBranch($frombranch);
543
544 =cut
545
546 sub GetReservesForBranch {
547     my ($frombranch) = @_;
548     my $dbh          = C4::Context->dbh;
549         my $query        = "SELECT borrowernumber,reservedate,itemnumber,waitingdate
550         FROM   reserves 
551         WHERE   priority='0'
552             AND found='W' ";
553     if ($frombranch){
554         $query .= " AND branchcode=? ";
555         }
556     $query .= "ORDER BY waitingdate" ;
557     my $sth = $dbh->prepare($query);
558     if ($frombranch){
559                 $sth->execute($frombranch);
560         }
561     else {
562                 $sth->execute();
563         }
564     my @transreserv;
565     my $i = 0;
566     while ( my $data = $sth->fetchrow_hashref ) {
567         $transreserv[$i] = $data;
568         $i++;
569     }
570     $sth->finish;
571     return (@transreserv);
572 }
573
574 =item CheckReserves
575
576   ($status, $reserve) = &CheckReserves($itemnumber);
577
578 Find a book in the reserves.
579
580 C<$itemnumber> is the book's item number.
581
582 As I understand it, C<&CheckReserves> looks for the given item in the
583 reserves. If it is found, that's a match, and C<$status> is set to
584 C<Waiting>.
585
586 Otherwise, it finds the most important item in the reserves with the
587 same biblio number as this book (I'm not clear on this) and returns it
588 with C<$status> set to C<Reserved>.
589
590 C<&CheckReserves> returns a two-element list:
591
592 C<$status> is either C<Waiting>, C<Reserved> (see above), or 0.
593
594 C<$reserve> is the reserve item that matched. It is a
595 reference-to-hash whose keys are mostly the fields of the reserves
596 table in the Koha database.
597
598 =cut
599
600 sub CheckReserves {
601     my ( $item, $barcode ) = @_;
602     my $dbh = C4::Context->dbh;
603     my $sth;
604     if ($item) {
605         my $qitem = $dbh->quote($item);
606         # Look up the item by itemnumber
607         my $query = "
608             SELECT items.biblionumber, items.biblioitemnumber, itemtypes.notforloan
609             FROM   items
610             LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber
611             LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
612             WHERE  itemnumber=$qitem
613         ";
614         $sth = $dbh->prepare($query);
615     }
616     else {
617         my $qbc = $dbh->quote($barcode);
618         # Look up the item by barcode
619         my $query = "
620             SELECT items.biblionumber, items.biblioitemnumber, itemtypes.notforloan
621             FROM   items
622             LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber
623             LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
624             WHERE  items.biblioitemnumber = biblioitems.biblioitemnumber
625               AND biblioitems.itemtype = itemtypes.itemtype
626               AND barcode=$qbc
627         ";
628         $sth = $dbh->prepare($query);
629
630         # FIXME - This function uses $item later on. Ought to set it here.
631     }
632     $sth->execute;
633     my ( $biblio, $bibitem, $notforloan ) = $sth->fetchrow_array;
634     $sth->finish;
635
636     # if item is not for loan it cannot be reserved either.....
637     return ( 0, 0 ) if $notforloan;
638
639     # get the reserves...
640     # Find this item in the reserves
641     my @reserves = _Findgroupreserve( $bibitem, $biblio );
642     my $count    = scalar @reserves;
643
644     # $priority and $highest are used to find the most important item
645     # in the list returned by &_Findgroupreserve. (The lower $priority,
646     # the more important the item.)
647     # $highest is the most important item we've seen so far.
648     my $priority = 10000000;
649     my $highest;
650     if ($count) {
651         foreach my $res (@reserves) {
652             # FIXME - $item might be undefined or empty: the caller
653             # might be searching by barcode.
654             if ( $res->{'itemnumber'} == $item ) {
655                 # Found it
656                 return ( "Waiting", $res );
657             }
658             else {
659                 # See if this item is more important than what we've got
660                 # so far.
661                 if ( $res->{'priority'} != 0 && $res->{'priority'} < $priority )
662                 {
663                     $priority = $res->{'priority'};
664                     $highest  = $res;
665                 }
666             }
667         }
668     }
669
670     # If we get this far, then no exact match was found. Print the
671     # most important item on the list. I think this tells us who's
672     # next in line to get this book.
673     if ($highest) {    # FIXME - $highest might be undefined
674         $highest->{'itemnumber'} = $item;
675         return ( "Reserved", $highest );
676     }
677     else {
678         return ( 0, 0 );
679     }
680 }
681
682 =item CancelReserve
683
684   &CancelReserve($biblionumber, $itemnumber, $borrowernumber);
685
686 Cancels a reserve.
687
688 Use either C<$biblionumber> or C<$itemnumber> to specify the item to
689 cancel, but not both: if both are given, C<&CancelReserve> does
690 nothing.
691
692 C<$borrowernumber> is the borrower number of the patron on whose
693 behalf the book was reserved.
694
695 If C<$biblionumber> was given, C<&CancelReserve> also adjusts the
696 priorities of the other people who are waiting on the book.
697
698 =cut
699
700 sub CancelReserve {
701     my ( $biblio, $item, $borr ) = @_;
702     my $dbh = C4::Context->dbh;
703         if ( $item and $borr ) {
704         # removing a waiting reserve record....
705         # update the database...
706         my $query = "
707             UPDATE reserves
708             SET    cancellationdate = now(),
709                    found            = Null,
710                    priority         = 0
711             WHERE  itemnumber       = ?
712              AND   borrowernumber   = ?
713         ";
714         my $sth = $dbh->prepare($query);
715         $sth->execute( $item, $borr );
716         $sth->finish;
717         $query = "
718             INSERT INTO old_reserves
719             SELECT * FROM reserves
720             WHERE  itemnumber       = ?
721              AND   borrowernumber   = ?
722         ";
723         $sth = $dbh->prepare($query);
724         $sth->execute( $item, $borr );
725         $query = "
726             DELETE FROM reserves
727             WHERE  itemnumber       = ?
728              AND   borrowernumber   = ?
729         ";
730         $sth = $dbh->prepare($query);
731         $sth->execute( $item, $borr );
732     }
733     else {
734         # removing a reserve record....
735         # get the prioritiy on this record....
736         my $priority;
737         my $query = qq/
738             SELECT priority FROM reserves
739             WHERE biblionumber   = ?
740               AND borrowernumber = ?
741               AND cancellationdate IS NULL
742               AND itemnumber IS NULL
743         /;
744         my $sth = $dbh->prepare($query);
745         $sth->execute( $biblio, $borr );
746         ($priority) = $sth->fetchrow_array;
747         $sth->finish;
748         $query = qq/
749             UPDATE reserves
750             SET    cancellationdate = now(),
751                    found            = Null,
752                    priority         = 0
753             WHERE  biblionumber     = ?
754               AND  borrowernumber   = ?
755         /;
756
757         # update the database, removing the record...
758         $sth = $dbh->prepare($query);
759         $sth->execute( $biblio, $borr );
760         $sth->finish;
761
762         $query = qq/
763             INSERT INTO old_reserves
764             SELECT * FROM reserves
765             WHERE  biblionumber     = ?
766               AND  borrowernumber   = ?
767         /;
768         $sth = $dbh->prepare($query);
769         $sth->execute( $biblio, $borr );
770
771         $query = qq/
772             DELETE FROM reserves
773             WHERE  biblionumber     = ?
774               AND  borrowernumber   = ?
775         /;
776         $sth = $dbh->prepare($query);
777         $sth->execute( $biblio, $borr );
778
779         # now fix the priority on the others....
780         _FixPriority( $priority, $biblio );
781     }
782 }
783
784 =item ModReserve
785
786 &ModReserve($rank,$biblio,$borrower,$branch)
787
788 =cut
789
790 sub ModReserve {
791     #subroutine to update a reserve
792     my ( $rank, $biblio, $borrower, $branch , $itemnumber) = @_;
793      return if $rank eq "W";
794      return if $rank eq "n";
795     my $dbh = C4::Context->dbh;
796     if ( $rank eq "del" ) {
797         my $query = qq/
798             UPDATE reserves
799             SET    cancellationdate=now()
800             WHERE  biblionumber   = ?
801              AND   borrowernumber = ?
802         /;
803         my $sth = $dbh->prepare($query);
804         $sth->execute( $biblio, $borrower );
805         $sth->finish;
806         $query = qq/
807             INSERT INTO old_reserves
808             SELECT *
809             FROM   reserves 
810             WHERE  biblionumber   = ?
811              AND   borrowernumber = ?
812         /;
813         $sth = $dbh->prepare($query);
814         $sth->execute( $biblio, $borrower );
815         $query = qq/
816             DELETE FROM reserves 
817             WHERE  biblionumber   = ?
818              AND   borrowernumber = ?
819         /;
820         $sth = $dbh->prepare($query);
821         $sth->execute( $biblio, $borrower );
822         
823     }
824     else {
825         my $query = qq/
826         UPDATE reserves SET priority = ? ,branchcode = ?, itemnumber = ?, found = NULL
827             WHERE biblionumber   = ?
828              AND borrowernumber = ?
829         /;
830         my $sth = $dbh->prepare($query);
831         $sth->execute( $rank, $branch,$itemnumber, $biblio, $borrower);
832         $sth->finish;
833         _FixPriority( $biblio, $borrower, $rank);
834     }
835 }
836
837 =item ModReserveFill
838
839   &ModReserveFill($reserve);
840
841 Fill a reserve. If I understand this correctly, this means that the
842 reserved book has been found and given to the patron who reserved it.
843
844 C<$reserve> specifies the reserve to fill. It is a reference-to-hash
845 whose keys are fields from the reserves table in the Koha database.
846
847 =cut
848
849 sub ModReserveFill {
850     my ($res) = @_;
851     my $dbh = C4::Context->dbh;
852     # fill in a reserve record....
853     my $biblionumber = $res->{'biblionumber'};
854     my $borrowernumber    = $res->{'borrowernumber'};
855     my $resdate = $res->{'reservedate'};
856
857     # get the priority on this record....
858     my $priority;
859     my $query = "SELECT priority
860                  FROM   reserves
861                  WHERE  biblionumber   = ?
862                   AND   borrowernumber = ?
863                   AND   reservedate    = ?";
864     my $sth = $dbh->prepare($query);
865     $sth->execute( $biblionumber, $borrowernumber, $resdate );
866     ($priority) = $sth->fetchrow_array;
867     $sth->finish;
868
869     # update the database...
870     $query = "UPDATE reserves
871                   SET    found            = 'F',
872                          priority         = 0
873                  WHERE  biblionumber     = ?
874                     AND reservedate      = ?
875                     AND borrowernumber   = ?
876                 ";
877     $sth = $dbh->prepare($query);
878     $sth->execute( $biblionumber, $resdate, $borrowernumber );
879     $sth->finish;
880
881     # move to old_reserves
882     $query = "INSERT INTO old_reserves
883                  SELECT * FROM reserves
884                  WHERE  biblionumber     = ?
885                     AND reservedate      = ?
886                     AND borrowernumber   = ?
887                 ";
888     $sth = $dbh->prepare($query);
889     $sth->execute( $biblionumber, $resdate, $borrowernumber );
890     $query = "DELETE FROM reserves
891                  WHERE  biblionumber     = ?
892                     AND reservedate      = ?
893                     AND borrowernumber   = ?
894                 ";
895     $sth = $dbh->prepare($query);
896     $sth->execute( $biblionumber, $resdate, $borrowernumber );
897     
898     # now fix the priority on the others (if the priority wasn't
899     # already sorted!)....
900     unless ( $priority == 0 ) {
901         _FixPriority( $priority, $biblionumber );
902     }
903 }
904
905 =item ModReserveStatus
906
907 &ModReserveStatus($itemnumber, $newstatus);
908
909 Update the reserve status for the active (priority=0) reserve.
910
911 $itemnumber is the itemnumber the reserve is on
912
913 $newstatus is the new status.
914
915 =cut
916
917 sub ModReserveStatus {
918
919     #first : check if we have a reservation for this item .
920     my ($itemnumber, $newstatus) = @_;
921     my $dbh          = C4::Context->dbh;
922     my $query = " UPDATE reserves
923     SET    found=?,waitingdate = now()
924     WHERE itemnumber=?
925       AND found IS NULL
926       AND priority = 0
927     ";
928     my $sth_set = $dbh->prepare($query);
929     $sth_set->execute( $newstatus, $itemnumber );
930     $sth_set->finish;
931 }
932
933 =item ModReserveAffect
934
935 &ModReserveAffect($itemnumber,$borrowernumber,$diffBranchSend);
936
937 This function affect an item and a status for a given reserve
938 The itemnumber parameter is used to find the biblionumber.
939 with the biblionumber & the borrowernumber, we can affect the itemnumber
940 to the correct reserve.
941
942 if $transferToDo is not set, then the status is set to "Waiting" as well.
943 otherwise, a transfer is on the way, and the end of the transfer will 
944 take care of the waiting status
945 =cut
946
947 sub ModReserveAffect {
948     my ( $itemnumber, $borrowernumber,$transferToDo ) = @_;
949     my $dbh = C4::Context->dbh;
950
951     # we want to attach $itemnumber to $borrowernumber, find the biblionumber
952     # attached to $itemnumber
953     my $sth = $dbh->prepare("SELECT biblionumber FROM items WHERE itemnumber=?");
954     $sth->execute($itemnumber);
955     my ($biblionumber) = $sth->fetchrow;
956     # If we affect a reserve that has to be transfered, don't set to Waiting
957     my $query;
958     if ($transferToDo) {
959     $query = "
960         UPDATE reserves
961         SET    priority = 0,
962                itemnumber = ?
963         WHERE borrowernumber = ?
964           AND biblionumber = ?
965     ";
966     }
967     else {
968     # affect the reserve to Waiting as well.
969     $query = "
970         UPDATE reserves
971         SET     priority = 0,
972                 found = 'W',
973                 waitingdate=now(),
974                 itemnumber = ?
975         WHERE borrowernumber = ?
976           AND biblionumber = ?
977     ";
978     }
979     $sth = $dbh->prepare($query);
980     $sth->execute( $itemnumber, $borrowernumber,$biblionumber);
981     $sth->finish;
982     return;
983 }
984
985 =item ModReserveCancelAll
986
987 ($messages,$nextreservinfo) = &ModReserveCancelAll($itemnumber,$borrowernumber);
988
989     function to cancel reserv,check other reserves, and transfer document if it's necessary
990
991 =cut
992
993 sub ModReserveCancelAll {
994     my $messages;
995     my $nextreservinfo;
996     my ( $itemnumber, $borrowernumber ) = @_;
997
998     #step 1 : cancel the reservation
999     my $CancelReserve = CancelReserve( undef, $itemnumber, $borrowernumber );
1000
1001     #step 2 launch the subroutine of the others reserves
1002     ( $messages, $nextreservinfo ) = GetOtherReserves($itemnumber);
1003
1004     return ( $messages, $nextreservinfo );
1005 }
1006
1007 =item ModReserveMinusPriority
1008
1009 &ModReserveMinusPriority($itemnumber,$borrowernumber,$biblionumber)
1010
1011 Reduce the values of queuded list     
1012
1013 =cut
1014
1015 sub ModReserveMinusPriority {
1016     my ( $itemnumber, $borrowernumber, $biblionumber ) = @_;
1017
1018     #first step update the value of the first person on reserv
1019     my $dbh   = C4::Context->dbh;
1020     my $query = "
1021         UPDATE reserves
1022         SET    priority = 0 , itemnumber = ? 
1023         WHERE  borrowernumber=?
1024           AND  biblionumber=?
1025     ";
1026     my $sth_upd = $dbh->prepare($query);
1027     $sth_upd->execute( $itemnumber, $borrowernumber, $biblionumber );
1028     $sth_upd->finish;
1029     # second step update all others reservs
1030     $query = "
1031             UPDATE reserves
1032             SET    priority = priority-1
1033             WHERE  biblionumber = ?
1034             AND priority > 0
1035     ";
1036     $sth_upd = $dbh->prepare($query);
1037     $sth_upd->execute( $biblionumber );
1038     $sth_upd->finish;
1039     $sth_upd->finish;
1040 }
1041
1042 =item GetReserveInfo
1043
1044 &GetReserveInfo($borrowernumber,$biblionumber);
1045
1046  Get item and borrower details for a current hold.
1047  Current implementation this query should have a single result.
1048 =cut
1049
1050 sub GetReserveInfo {
1051         my ( $borrowernumber, $biblionumber ) = @_;
1052     my $dbh = C4::Context->dbh;
1053         my $strsth="SELECT reservedate, reservenotes, reserves.borrowernumber,
1054                                 reserves.biblionumber, reserves.branchcode,
1055                                 notificationdate, reminderdate, priority, found,
1056                                 firstname, surname, phone, 
1057                                 email, address, address2,
1058                                 cardnumber, city, zipcode,
1059                                 biblio.title, biblio.author,
1060                                 items.holdingbranch, items.itemcallnumber, items.itemnumber, 
1061                                 barcode, notes
1062                         FROM reserves left join items 
1063                                 ON items.itemnumber=reserves.itemnumber , 
1064                                 borrowers, biblio 
1065                         WHERE 
1066                                 reserves.borrowernumber=?  &&
1067                                 reserves.biblionumber=? && 
1068                                 reserves.borrowernumber=borrowers.borrowernumber && 
1069                                 reserves.biblionumber=biblio.biblionumber ";
1070         my $sth = $dbh->prepare($strsth); 
1071         $sth->execute($borrowernumber,$biblionumber);
1072
1073         my $data = $sth->fetchrow_hashref;
1074         return $data;
1075
1076 }
1077
1078 =item _FixPriority
1079
1080 &_FixPriority($biblio,$borrowernumber,$rank);
1081
1082  Only used internally (so don't export it)
1083  Changed how this functions works #
1084  Now just gets an array of reserves in the rank order and updates them with
1085  the array index (+1 as array starts from 0)
1086  and if $rank is supplied will splice item from the array and splice it back in again
1087  in new priority rank
1088
1089 =cut 
1090
1091 sub _FixPriority {
1092     my ( $biblio, $borrowernumber, $rank ) = @_;
1093     my $dbh = C4::Context->dbh;
1094      if ( $rank eq "del" ) {
1095          CancelReserve( $biblio, undef, $borrowernumber );
1096      }
1097     if ( $rank eq "W" || $rank eq "0" ) {
1098
1099         # make sure priority for waiting items is 0
1100         my $query = qq/
1101             UPDATE reserves
1102             SET    priority = 0
1103             WHERE biblionumber = ?
1104               AND borrowernumber = ?
1105               AND found ='W'
1106         /;
1107         my $sth = $dbh->prepare($query);
1108         $sth->execute( $biblio, $borrowernumber );
1109     }
1110     my @priority;
1111     my @reservedates;
1112
1113     # get whats left
1114 # FIXME adding a new security in returned elements for changing priority,
1115 # now, we don't care anymore any reservations with itemnumber linked (suppose a waiting reserve)
1116         # This is wrong a waiting reserve has W set
1117         # The assumption that having an itemnumber set means waiting is wrong and should be corrected any place it occurs
1118     my $query = qq/
1119         SELECT borrowernumber, reservedate, constrainttype
1120         FROM   reserves
1121         WHERE  biblionumber   = ?
1122           AND  ((found <> 'W') or found is NULL)
1123         ORDER BY priority ASC
1124     /;
1125     my $sth = $dbh->prepare($query);
1126     $sth->execute($biblio);
1127     while ( my $line = $sth->fetchrow_hashref ) {
1128         push( @reservedates, $line );
1129         push( @priority,     $line );
1130     }
1131
1132     # To find the matching index
1133     my $i;
1134     my $key = -1;    # to allow for 0 to be a valid result
1135     for ( $i = 0 ; $i < @priority ; $i++ ) {
1136         if ( $borrowernumber == $priority[$i]->{'borrowernumber'} ) {
1137             $key = $i;    # save the index
1138             last;
1139         }
1140     }
1141
1142     # if index exists in array then move it to new position
1143     if ( $key > -1 && $rank ne 'del' && $rank > 0 ) {
1144         my $new_rank = $rank -
1145           1;    # $new_rank is what you want the new index to be in the array
1146         my $moving_item = splice( @priority, $key, 1 );
1147         splice( @priority, $new_rank, 0, $moving_item );
1148     }
1149
1150     # now fix the priority on those that are left....
1151     $query = "
1152             UPDATE reserves
1153             SET    priority = ?
1154                 WHERE  biblionumber = ?
1155                  AND borrowernumber   = ?
1156                  AND reservedate = ?
1157          AND found IS NULL
1158     ";
1159     $sth = $dbh->prepare($query);
1160     for ( my $j = 0 ; $j < @priority ; $j++ ) {
1161         $sth->execute(
1162             $j + 1, $biblio,
1163             $priority[$j]->{'borrowernumber'},
1164             $priority[$j]->{'reservedate'}
1165         );
1166         $sth->finish;
1167     }
1168 }
1169
1170 =item _Findgroupreserve
1171
1172   @results = &_Findgroupreserve($biblioitemnumber, $biblionumber);
1173
1174 ****** FIXME ******
1175 I don't know what this does, because I don't understand how reserve
1176 constraints work. I think the idea is that you reserve a particular
1177 biblio, and the constraint allows you to restrict it to a given
1178 biblioitem (e.g., if you want to borrow the audio book edition of "The
1179 Prophet", rather than the first available publication).
1180
1181 C<&_Findgroupreserve> returns :
1182 C<@results> is an array of references-to-hash whose keys are mostly
1183 fields from the reserves table of the Koha database, plus
1184 C<biblioitemnumber>.
1185
1186 =cut
1187
1188 sub _Findgroupreserve {
1189     my ( $bibitem, $biblio ) = @_;
1190     my $dbh   = C4::Context->dbh;
1191     my $query = qq/
1192         SELECT reserves.biblionumber AS biblionumber,
1193                reserves.borrowernumber AS borrowernumber,
1194                reserves.reservedate AS reservedate,
1195                reserves.branchcode AS branchcode,
1196                reserves.cancellationdate AS cancellationdate,
1197                reserves.found AS found,
1198                reserves.reservenotes AS reservenotes,
1199                reserves.priority AS priority,
1200                reserves.timestamp AS timestamp,
1201                reserveconstraints.biblioitemnumber AS biblioitemnumber,
1202                reserves.itemnumber AS itemnumber
1203         FROM reserves
1204           LEFT JOIN reserveconstraints ON reserves.biblionumber = reserveconstraints.biblionumber
1205         WHERE reserves.biblionumber = ?
1206           AND ( ( reserveconstraints.biblioitemnumber = ?
1207           AND reserves.borrowernumber = reserveconstraints.borrowernumber
1208           AND reserves.reservedate    =reserveconstraints.reservedate )
1209           OR  reserves.constrainttype='a' )
1210     /;
1211     my $sth = $dbh->prepare($query);
1212     $sth->execute( $biblio, $bibitem );
1213     my @results;
1214     while ( my $data = $sth->fetchrow_hashref ) {
1215         push( @results, $data );
1216     }
1217     $sth->finish;
1218     return @results;
1219 }
1220
1221 =back
1222
1223 =head1 AUTHOR
1224
1225 Koha Developement team <info@koha.org>
1226
1227 =cut
1228
1229 1;