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