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