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