oups... It can be more than one ISBN.... fixed now.
[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     while ( my $data = $sth->fetchrow_hashref ) {
291         push @borrowerreserv, $data;
292     }
293     return @borrowerreserv;
294 }
295 #-------------------------------------------------------------------------------------
296
297 =item GetReserveCount
298
299 $number = &GetReserveCount($borrowernumber);
300
301 this function returns the number of reservation for a borrower given on input arg.
302
303 =cut
304
305 sub GetReserveCount {
306     my ($borrowernumber) = @_;
307
308     my $dbh = C4::Context->dbh;
309
310     my $query = '
311         SELECT COUNT(*) AS counter
312         FROM reserves
313           WHERE borrowernumber = ?
314           AND cancellationdate IS NULL
315           AND (found != \'F\' OR found IS NULL)
316     ';
317     my $sth = $dbh->prepare($query);
318     $sth->execute($borrowernumber);
319     my $row = $sth->fetchrow_hashref;
320     $sth->finish;
321
322     return $row->{counter};
323 }
324
325 =item GetOtherReserves
326
327 ($messages,$nextreservinfo)=$GetOtherReserves(itemnumber);
328
329 Check queued list of this document and check if this document must be  transfered
330
331 =cut
332
333 sub GetOtherReserves {
334     my ($itemnumber) = @_;
335     my $messages;
336     my $nextreservinfo;
337     my ( $restype, $checkreserves ) = CheckReserves($itemnumber);
338     if ($checkreserves) {
339         my $iteminfo = GetItem($itemnumber);
340         if ( $iteminfo->{'holdingbranch'} ne $checkreserves->{'branchcode'} ) {
341             $messages->{'transfert'} = $checkreserves->{'branchcode'};
342             #minus priorities of others reservs
343             ModReserveMinusPriority(
344                 $itemnumber,
345                 $checkreserves->{'borrowernumber'},
346                 $iteminfo->{'biblionumber'}
347             );
348
349             #launch the subroutine dotransfer
350             C4::Circulation::ModItemTransfer(
351                 $itemnumber,
352                 $iteminfo->{'holdingbranch'},
353                 $checkreserves->{'branchcode'}
354               ),
355               ;
356         }
357
358      #step 2b : case of a reservation on the same branch, set the waiting status
359         else {
360             $messages->{'waiting'} = 1;
361             ModReserveMinusPriority(
362                 $itemnumber,
363                 $checkreserves->{'borrowernumber'},
364                 $iteminfo->{'biblionumber'}
365             );
366             ModReserveStatus($itemnumber,'W');
367         }
368
369         $nextreservinfo = $checkreserves->{'borrowernumber'};
370     }
371
372     return ( $messages, $nextreservinfo );
373 }
374
375 sub GetReserveFee {
376     my ($borrowernumber, $biblionumber, $constraint, $bibitems ) = @_;
377
378     #check for issues;
379     my $dbh   = C4::Context->dbh;
380     my $const = lc substr( $constraint, 0, 1 );
381     my $query = qq/
382       SELECT * FROM borrowers,categories
383     WHERE borrowernumber = ?
384       AND borrowers.categorycode = categories.categorycode
385     /;
386     my $sth = $dbh->prepare($query);
387     $sth->execute($borrowernumber);
388     my $data = $sth->fetchrow_hashref;
389     $sth->finish();
390     my $fee      = $data->{'reservefee'};
391     my $cntitems = @- > $bibitems;
392
393     if ( $fee > 0 ) {
394
395         # check for items on issue
396         # first find biblioitem records
397         my @biblioitems;
398         my $sth1 = $dbh->prepare(
399             "SELECT * FROM biblio,biblioitems
400                    WHERE (biblio.biblionumber = ?)
401                      AND (biblio.biblionumber = biblioitems.biblionumber)"
402         );
403         $sth1->execute($biblionumber);
404         while ( my $data1 = $sth1->fetchrow_hashref ) {
405             if ( $const eq "a" ) {
406                 push @biblioitems, $data1;
407             }
408             else {
409                 my $found = 0;
410                 my $x     = 0;
411                 while ( $x < $cntitems ) {
412                     if ( @$bibitems->{'biblioitemnumber'} ==
413                         $data->{'biblioitemnumber'} )
414                     {
415                         $found = 1;
416                     }
417                     $x++;
418                 }
419                 if ( $const eq 'o' ) {
420                     if ( $found == 1 ) {
421                         push @biblioitems, $data1;
422                     }
423                 }
424                 else {
425                     if ( $found == 0 ) {
426                         push @biblioitems, $data1;
427                     }
428                 }
429             }
430         }
431         $sth1->finish;
432         my $cntitemsfound = @biblioitems;
433         my $issues        = 0;
434         my $x             = 0;
435         my $allissued     = 1;
436         while ( $x < $cntitemsfound ) {
437             my $bitdata = $biblioitems[$x];
438             my $sth2    = $dbh->prepare(
439                 "SELECT * FROM items
440                      WHERE biblioitemnumber = ?"
441             );
442             $sth2->execute( $bitdata->{'biblioitemnumber'} );
443             while ( my $itdata = $sth2->fetchrow_hashref ) {
444                 my $sth3 = $dbh->prepare(
445                     "SELECT * FROM issues
446                        WHERE itemnumber = ?
447                          AND returndate IS NULL"
448                 );
449                 $sth3->execute( $itdata->{'itemnumber'} );
450                 if ( my $isdata = $sth3->fetchrow_hashref ) {
451                 }
452                 else {
453                     $allissued = 0;
454                 }
455             }
456             $x++;
457         }
458         if ( $allissued == 0 ) {
459             my $rsth =
460               $dbh->prepare("SELECT * FROM reserves WHERE biblionumber = ?");
461             $rsth->execute($biblionumber);
462             if ( my $rdata = $rsth->fetchrow_hashref ) {
463             }
464             else {
465                 $fee = 0;
466             }
467         }
468     }
469
470     #  print "fee $fee";
471     return $fee;
472 }
473
474 =head2 GetReservesToBranch
475
476 @transreserv = GetReservesToBranch( $frombranch );
477
478 Get reserve list for a given branch
479
480 =cut
481
482 sub GetReservesToBranch {
483     my ( $frombranch ) = @_;
484     my $dbh = C4::Context->dbh;
485     my $sth = $dbh->prepare(
486         "SELECT borrowernumber,reservedate,itemnumber,timestamp
487          FROM reserves 
488          WHERE priority='0' AND cancellationdate is null  
489            AND branchcode=?
490            AND found IS NULL "
491     );
492     $sth->execute( $frombranch );
493     my @transreserv;
494     my $i = 0;
495     while ( my $data = $sth->fetchrow_hashref ) {
496         $transreserv[$i] = $data;
497         $i++;
498     }
499     $sth->finish;
500     return (@transreserv);
501 }
502
503 =head2 GetReservesForBranch
504
505 @transreserv = GetReservesForBranch($frombranch);
506
507 =cut
508
509 sub GetReservesForBranch {
510     my ($frombranch) = @_;
511     my $dbh          = C4::Context->dbh;
512     my $sth          = $dbh->prepare( "
513         SELECT borrowernumber,reservedate,itemnumber,waitingdate
514         FROM   reserves 
515         WHERE   priority='0'
516             AND cancellationdate IS NULL 
517             AND found='W' 
518             AND branchcode=?
519         ORDER BY waitingdate" );
520     $sth->execute($frombranch);
521     my @transreserv;
522     my $i = 0;
523     while ( my $data = $sth->fetchrow_hashref ) {
524         $transreserv[$i] = $data;
525         $i++;
526     }
527     $sth->finish;
528     return (@transreserv);
529 }
530
531 =item CheckReserves
532
533   ($status, $reserve) = &CheckReserves($itemnumber);
534
535 Find a book in the reserves.
536
537 C<$itemnumber> is the book's item number.
538
539 As I understand it, C<&CheckReserves> looks for the given item in the
540 reserves. If it is found, that's a match, and C<$status> is set to
541 C<Waiting>.
542
543 Otherwise, it finds the most important item in the reserves with the
544 same biblio number as this book (I'm not clear on this) and returns it
545 with C<$status> set to C<Reserved>.
546
547 C<&CheckReserves> returns a two-element list:
548
549 C<$status> is either C<Waiting>, C<Reserved> (see above), or 0.
550
551 C<$reserve> is the reserve item that matched. It is a
552 reference-to-hash whose keys are mostly the fields of the reserves
553 table in the Koha database.
554
555 =cut
556
557 sub CheckReserves {
558     my ( $item, $barcode ) = @_;
559     my $dbh = C4::Context->dbh;
560     my $sth;
561     if ($item) {
562         my $qitem = $dbh->quote($item);
563         # Look up the item by itemnumber
564         my $query = "
565             SELECT items.biblionumber, items.biblioitemnumber, itemtypes.notforloan
566             FROM   items, biblioitems, itemtypes
567             WHERE  items.biblioitemnumber = biblioitems.biblioitemnumber
568                AND biblioitems.itemtype = itemtypes.itemtype
569                AND itemnumber=$qitem
570         ";
571         $sth = $dbh->prepare($query);
572     }
573     else {
574         my $qbc = $dbh->quote($barcode);
575         # Look up the item by barcode
576         my $query = "
577             SELECT items.biblionumber, items.biblioitemnumber, itemtypes.notforloan
578             FROM   items, biblioitems, itemtypes
579             WHERE  items.biblioitemnumber = biblioitems.biblioitemnumber
580               AND biblioitems.itemtype = itemtypes.itemtype
581               AND barcode=$qbc
582         ";
583         $sth = $dbh->prepare($query);
584
585         # FIXME - This function uses $item later on. Ought to set it here.
586     }
587     $sth->execute;
588     my ( $biblio, $bibitem, $notforloan ) = $sth->fetchrow_array;
589     $sth->finish;
590
591     # if item is not for loan it cannot be reserved either.....
592     return ( 0, 0 ) if $notforloan;
593
594     # get the reserves...
595     # Find this item in the reserves
596     my @reserves = _Findgroupreserve( $bibitem, $biblio );
597     my $count    = scalar @reserves;
598
599     # $priority and $highest are used to find the most important item
600     # in the list returned by &_Findgroupreserve. (The lower $priority,
601     # the more important the item.)
602     # $highest is the most important item we've seen so far.
603     my $priority = 10000000;
604     my $highest;
605     if ($count) {
606         foreach my $res (@reserves) {
607             # FIXME - $item might be undefined or empty: the caller
608             # might be searching by barcode.
609             if ( $res->{'itemnumber'} == $item ) {
610                 # Found it
611                 return ( "Waiting", $res );
612             }
613             else {
614                 # See if this item is more important than what we've got
615                 # so far.
616                 if ( $res->{'priority'} != 0 && $res->{'priority'} < $priority )
617                 {
618                     $priority = $res->{'priority'};
619                     $highest  = $res;
620                 }
621             }
622         }
623     }
624
625     # If we get this far, then no exact match was found. Print the
626     # most important item on the list. I think this tells us who's
627     # next in line to get this book.
628     if ($highest) {    # FIXME - $highest might be undefined
629         $highest->{'itemnumber'} = $item;
630         return ( "Reserved", $highest );
631     }
632     else {
633         return ( 0, 0 );
634     }
635 }
636
637 =item CancelReserve
638
639   &CancelReserve($biblionumber, $itemnumber, $borrowernumber);
640
641 Cancels a reserve.
642
643 Use either C<$biblionumber> or C<$itemnumber> to specify the item to
644 cancel, but not both: if both are given, C<&CancelReserve> does
645 nothing.
646
647 C<$borrowernumber> is the borrower number of the patron on whose
648 behalf the book was reserved.
649
650 If C<$biblionumber> was given, C<&CancelReserve> also adjusts the
651 priorities of the other people who are waiting on the book.
652
653 =cut
654
655 sub CancelReserve {
656     my ( $biblio, $item, $borr ) = @_;
657     my $dbh = C4::Context->dbh;
658         if ( ( $item and $borr ) and ( not $biblio ) ) {
659         # removing a waiting reserve record....
660         # update the database...
661         my $query = "
662             UPDATE reserves
663             SET    cancellationdate = now(),
664                    found            = Null,
665                    priority         = 0
666             WHERE  itemnumber       = ?
667              AND   borrowernumber   = ?
668         ";
669         my $sth = $dbh->prepare($query);
670         $sth->execute( $item, $borr );
671         $sth->finish;
672     }
673     if ( ( $biblio and $borr ) and ( not $item ) ) {
674         # removing a reserve record....
675         # get the prioritiy on this record....
676         my $priority;
677         my $query = qq/
678             SELECT priority FROM reserves
679             WHERE biblionumber   = ?
680               AND borrowernumber = ?
681               AND cancellationdate IS NULL
682               AND itemnumber IS NULL
683               AND (found <> 'F' OR found IS NULL)
684         /;
685         my $sth = $dbh->prepare($query);
686         $sth->execute( $biblio, $borr );
687         ($priority) = $sth->fetchrow_array;
688         $sth->finish;
689         $query = qq/
690             UPDATE reserves
691             SET    cancellationdate = now(),
692                    found            = Null,
693                    priority         = 0
694             WHERE  biblionumber     = ?
695               AND  borrowernumber   = ?
696               AND cancellationdate IS NULL
697               AND (found <> 'F' or found IS NULL)
698         /;
699
700         # update the database, removing the record...
701         $sth = $dbh->prepare($query);
702         $sth->execute( $biblio, $borr );
703         $sth->finish;
704
705         # now fix the priority on the others....
706         _FixPriority( $priority, $biblio );
707     }
708 }
709
710 =item ModReserve
711
712 &ModReserve($rank,$biblio,$borrower,$branch)
713
714 =cut
715
716 sub ModReserve {
717     #subroutine to update a reserve
718     my ( $rank, $biblio, $borrower, $branch , $itemnumber) = @_;
719      return if $rank eq "W";
720      return if $rank eq "n";
721     my $dbh = C4::Context->dbh;
722     if ( $rank eq "del" ) {
723         my $query = qq/
724             UPDATE reserves
725             SET    cancellationdate=now()
726             WHERE  biblionumber   = ?
727              AND   borrowernumber = ?
728              AND   cancellationdate is NULL
729              AND   (found <> 'F' or found is NULL)
730         /;
731         my $sth = $dbh->prepare($query);
732         $sth->execute( $biblio, $borrower );
733         $sth->finish;
734         
735     }
736     else {
737         my $query = qq/
738         UPDATE reserves SET priority = ? ,branchcode = ?, itemnumber = ?, found = NULL
739             WHERE biblionumber   = ?
740              AND borrowernumber = ?
741              AND cancellationdate is NULL
742              AND (found <> 'F' or found is NULL)
743         /;
744         my $sth = $dbh->prepare($query);
745         $sth->execute( $rank, $branch,$itemnumber, $biblio, $borrower);
746         $sth->finish;
747         _FixPriority( $biblio, $borrower, $rank);
748     }
749 }
750
751 =item ModReserveFill
752
753   &ModReserveFill($reserve);
754
755 Fill a reserve. If I understand this correctly, this means that the
756 reserved book has been found and given to the patron who reserved it.
757
758 C<$reserve> specifies the reserve to fill. It is a reference-to-hash
759 whose keys are fields from the reserves table in the Koha database.
760
761 =cut
762
763 sub ModReserveFill {
764     my ($res) = @_;
765     my $dbh = C4::Context->dbh;
766     # fill in a reserve record....
767     my $biblionumber = $res->{'biblionumber'};
768     my $borrowernumber    = $res->{'borrowernumber'};
769     my $resdate = $res->{'reservedate'};
770
771     # get the priority on this record....
772     my $priority;
773     my $query = "SELECT priority
774                  FROM   reserves
775                  WHERE  biblionumber   = ?
776                   AND   borrowernumber = ?
777                   AND   reservedate    = ?";
778     my $sth = $dbh->prepare($query);
779     $sth->execute( $biblionumber, $borrowernumber, $resdate );
780     ($priority) = $sth->fetchrow_array;
781     $sth->finish;
782
783     # update the database...
784     $query = "UPDATE reserves
785                   SET    found            = 'F',
786                          priority         = 0
787                  WHERE  biblionumber     = ?
788                     AND reservedate      = ?
789                     AND borrowernumber   = ?
790                 ";
791     $sth = $dbh->prepare($query);
792     $sth->execute( $biblionumber, $resdate, $borrowernumber );
793     $sth->finish;
794
795     # now fix the priority on the others (if the priority wasn't
796     # already sorted!)....
797     unless ( $priority == 0 ) {
798         _FixPriority( $priority, $biblionumber );
799     }
800 }
801
802 =item ModReserveStatus
803
804 &ModReserveStatus($itemnumber, $newstatus);
805
806 Update the reserve status for the active (priority=0) reserve.
807
808 $itemnumber is the itemnumber the reserve is on
809
810 $newstatus is the new status.
811
812 =cut
813
814 sub ModReserveStatus {
815
816     #first : check if we have a reservation for this item .
817     my ($itemnumber, $newstatus) = @_;
818     my $dbh          = C4::Context->dbh;
819     my $query = " UPDATE reserves
820     SET    found=?,waitingdate = now()
821     WHERE itemnumber=?
822       AND found IS NULL
823       AND priority = 0
824     ";
825     my $sth_set = $dbh->prepare($query);
826     $sth_set->execute( $newstatus, $itemnumber );
827     $sth_set->finish;
828 }
829
830 =item ModReserveAffect
831
832 &ModReserveAffect($itemnumber,$borrowernumber,$diffBranchSend);
833
834 This function affect an item and a status for a given reserve
835 The itemnumber parameter is used to find the biblionumber.
836 with the biblionumber & the borrowernumber, we can affect the itemnumber
837 to the correct reserve.
838
839 if $transferToDo is set, then the status is set to "Waiting" as well.
840 otherwise, a transfer is on the way, and the end of the transfer will 
841 take care of the waiting status
842 =cut
843
844 sub ModReserveAffect {
845     my ( $itemnumber, $borrowernumber,$transferToDo ) = @_;
846     my $dbh = C4::Context->dbh;
847
848     # we want to attach $itemnumber to $borrowernumber, find the biblionumber
849     # attached to $itemnumber
850     my $sth = $dbh->prepare("SELECT biblionumber FROM items WHERE itemnumber=?");
851     $sth->execute($itemnumber);
852     my ($biblionumber) = $sth->fetchrow;
853     # If we affect a reserve that has to be transfered, don't set to Waiting
854     my $query;
855     if ($transferToDo) {
856     $query = "
857         UPDATE reserves
858         SET    priority = 0,
859                itemnumber = ?
860         WHERE borrowernumber = ?
861           AND biblionumber = ?
862           AND reserves.cancellationdate IS NULL
863           AND (reserves.found <> 'F' OR reserves.found IS NULL)
864     ";
865     }
866     else {
867     # affect the reserve to Waiting as well.
868     $query = "
869         UPDATE reserves
870         SET     priority = 0,
871                 found = 'W',
872                 waitingdate=now(),
873                 itemnumber = ?
874         WHERE borrowernumber = ?
875           AND biblionumber = ?
876           AND reserves.cancellationdate IS NULL
877           AND (reserves.found <> 'F' OR reserves.found IS NULL)
878     ";
879     }
880     $sth = $dbh->prepare($query);
881     $sth->execute( $itemnumber, $borrowernumber,$biblionumber);
882     $sth->finish;
883
884     # now fix up the remaining priorities....
885 #     _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.
886     return;
887 }
888
889 =item ModReserveCancelAll
890
891 ($messages,$nextreservinfo) = &ModReserveCancelAll($itemnumber,$borrowernumber);
892
893     function to cancel reserv,check other reserves, and transfer document if it's necessary
894
895 =cut
896
897 sub ModReserveCancelAll {
898     my $messages;
899     my $nextreservinfo;
900     my ( $itemnumber, $borrowernumber ) = @_;
901
902     #step 1 : cancel the reservation
903     my $CancelReserve = CancelReserve( undef, $itemnumber, $borrowernumber );
904
905     #step 2 launch the subroutine of the others reserves
906     ( $messages, $nextreservinfo ) = GetOtherReserves($itemnumber);
907
908     return ( $messages, $nextreservinfo );
909 }
910
911 =item ModReserveMinusPriority
912
913 &ModReserveMinusPriority($itemnumber,$borrowernumber,$biblionumber)
914
915 Reduce the values of queuded list     
916
917 =cut
918
919 sub ModReserveMinusPriority {
920     my ( $itemnumber, $borrowernumber, $biblionumber ) = @_;
921
922     #first step update the value of the first person on reserv
923     my $dbh   = C4::Context->dbh;
924     my $query = "
925         UPDATE reserves
926         SET    priority = 0 , itemnumber = ? 
927         WHERE  cancellationdate IS NULL 
928           AND  borrowernumber=?
929           AND  biblionumber=?
930     ";
931     my $sth_upd = $dbh->prepare($query);
932     $sth_upd->execute( $itemnumber, $borrowernumber, $biblionumber );
933     $sth_upd->finish;
934     # second step update all others reservs
935     $query = "
936             UPDATE reserves
937             SET    priority = priority-1
938             WHERE  biblionumber = ?
939             AND priority > 0
940             AND cancellationdate IS NULL
941     ";
942     $sth_upd = $dbh->prepare($query);
943     $sth_upd->execute( $biblionumber );
944     $sth_upd->finish;
945     $sth_upd->finish;
946 }
947
948 =item _FixPriority
949
950 &_FixPriority($biblio,$borrowernumber,$rank);
951
952  Only used internally (so don't export it)
953  Changed how this functions works #
954  Now just gets an array of reserves in the rank order and updates them with
955  the array index (+1 as array starts from 0)
956  and if $rank is supplied will splice item from the array and splice it back in again
957  in new priority rank
958
959 =cut 
960
961 sub _FixPriority {
962     my ( $biblio, $borrowernumber, $rank ) = @_;
963     my $dbh = C4::Context->dbh;
964      if ( $rank eq "del" ) {
965          CancelReserve( $biblio, undef, $borrowernumber );
966      }
967     if ( $rank eq "W" || $rank eq "0" ) {
968
969         # make sure priority for waiting items is 0
970         my $query = qq/
971             UPDATE reserves
972             SET    priority = 0
973             WHERE biblionumber = ?
974               AND borrowernumber = ?
975               AND cancellationdate IS NULL
976               AND found ='W'
977         /;
978         my $sth = $dbh->prepare($query);
979         $sth->execute( $biblio, $borrowernumber );
980     }
981     my @priority;
982     my @reservedates;
983
984     # get whats left
985 # FIXME adding a new security in returned elements for changing priority,
986 # now, we don't care anymore any reservations with itemnumber linked (suppose a waiting reserve)
987     my $query = qq/
988         SELECT borrowernumber, reservedate, constrainttype
989         FROM   reserves
990         WHERE  biblionumber   = ?
991           AND  cancellationdate IS NULL
992           AND  itemnumber IS NULL
993           AND  ((found <> 'F' and found <> 'W') or found is NULL)
994         ORDER BY priority ASC
995     /;
996     my $sth = $dbh->prepare($query);
997     $sth->execute($biblio);
998     while ( my $line = $sth->fetchrow_hashref ) {
999         push( @reservedates, $line );
1000         push( @priority,     $line );
1001     }
1002
1003     # To find the matching index
1004     my $i;
1005     my $key = -1;    # to allow for 0 to be a valid result
1006     for ( $i = 0 ; $i < @priority ; $i++ ) {
1007         if ( $borrowernumber == $priority[$i]->{'borrowernumber'} ) {
1008             $key = $i;    # save the index
1009             last;
1010         }
1011     }
1012
1013     # if index exists in array then move it to new position
1014     if ( $key > -1 && $rank ne 'del' && $rank > 0 ) {
1015         my $new_rank = $rank -
1016           1;    # $new_rank is what you want the new index to be in the array
1017         my $moving_item = splice( @priority, $key, 1 );
1018         splice( @priority, $new_rank, 0, $moving_item );
1019     }
1020
1021     # now fix the priority on those that are left....
1022     $query = "
1023             UPDATE reserves
1024             SET    priority = ?
1025                 WHERE  biblionumber = ?
1026                  AND borrowernumber   = ?
1027                  AND reservedate = ?
1028          AND found IS NULL
1029     ";
1030     $sth = $dbh->prepare($query);
1031     for ( my $j = 0 ; $j < @priority ; $j++ ) {
1032         $sth->execute(
1033             $j + 1, $biblio,
1034             $priority[$j]->{'borrowernumber'},
1035             $priority[$j]->{'reservedate'}
1036         );
1037         $sth->finish;
1038     }
1039 }
1040
1041 =item _Findgroupreserve
1042
1043   @results = &_Findgroupreserve($biblioitemnumber, $biblionumber);
1044
1045 ****** FIXME ******
1046 I don't know what this does, because I don't understand how reserve
1047 constraints work. I think the idea is that you reserve a particular
1048 biblio, and the constraint allows you to restrict it to a given
1049 biblioitem (e.g., if you want to borrow the audio book edition of "The
1050 Prophet", rather than the first available publication).
1051
1052 C<&_Findgroupreserve> returns :
1053 C<@results> is an array of references-to-hash whose keys are mostly
1054 fields from the reserves table of the Koha database, plus
1055 C<biblioitemnumber>.
1056
1057 =cut
1058
1059 sub _Findgroupreserve {
1060     my ( $bibitem, $biblio ) = @_;
1061     my $dbh   = C4::Context->dbh;
1062     my $query = qq/
1063         SELECT reserves.biblionumber AS biblionumber,
1064                reserves.borrowernumber AS borrowernumber,
1065                reserves.reservedate AS reservedate,
1066                reserves.branchcode AS branchcode,
1067                reserves.cancellationdate AS cancellationdate,
1068                reserves.found AS found,
1069                reserves.reservenotes AS reservenotes,
1070                reserves.priority AS priority,
1071                reserves.timestamp AS timestamp,
1072                reserveconstraints.biblioitemnumber AS biblioitemnumber,
1073                reserves.itemnumber AS itemnumber
1074         FROM reserves
1075           LEFT JOIN reserveconstraints ON reserves.biblionumber = reserveconstraints.biblionumber
1076         WHERE reserves.biblionumber = ?
1077           AND ( ( reserveconstraints.biblioitemnumber = ?
1078           AND reserves.borrowernumber = reserveconstraints.borrowernumber
1079           AND reserves.reservedate    =reserveconstraints.reservedate )
1080           OR  reserves.constrainttype='a' )
1081           AND reserves.cancellationdate is NULL
1082           AND (reserves.found <> 'F' or reserves.found is NULL)
1083     /;
1084     my $sth = $dbh->prepare($query);
1085     $sth->execute( $biblio, $bibitem );
1086     my @results;
1087     while ( my $data = $sth->fetchrow_hashref ) {
1088         push( @results, $data );
1089     }
1090     $sth->finish;
1091     return @results;
1092 }
1093
1094 =item GetReserveFee
1095
1096 $fee = GetReserveFee($borrowernumber,$biblionumber,$constraint,$biblionumber);
1097
1098 Calculate the fee for a reserve
1099
1100 =cut
1101
1102 =back
1103
1104 =head1 AUTHOR
1105
1106 Koha Developement team <info@koha.org>
1107
1108 =cut
1109