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