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