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