Merging Katipo changes.
[koha.git] / C4 / Reserves2.pm
1 # -*- tab-width: 8 -*-
2 # NOTE: This file uses standard 8-character tabs
3
4 package C4::Reserves2;
5
6 # $Id$
7
8 # Copyright 2000-2002 Katipo Communications
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 use strict;
26 require Exporter;
27 use DBI;
28 use C4::Context;
29 use C4::Biblio;
30 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
31 my $library_name = C4::Context->preference("LibraryName");
32
33 # set the version for version checking
34 $VERSION = 0.01;
35
36 =head1 NAME
37
38 C4::Reserves2 - FIXME
39
40 =head1 SYNOPSIS
41
42   use C4::Reserves2;
43
44 =head1 DESCRIPTION
45
46 FIXME
47
48 =head1 FUNCTIONS
49
50 =over 2
51
52 =cut
53
54 @ISA = qw(Exporter);
55 # FIXME Take out CalcReserveFee after it can be removed from opac-reserves.pl
56 @EXPORT = qw(
57     &FindReserves
58     &CheckReserves
59     &CheckWaiting
60     &CancelReserve
61     &CalcReserveFee
62     &FillReserve
63     &ReserveWaiting
64     &CreateReserve
65     &updatereserves
66     &UpdateReserve
67     &getreservetitle
68     &Findgroupreserve
69     &FastFindReserves
70     &SetWaitingStatus
71     &GlobalCancel
72     &MinusPriority
73     &OtherReserves
74     GetFirstReserveDateFromItem
75     GetNumberReservesFromBorrower
76     &fixpriority 
77 );
78
79 # make all your functions, whether exported or not;
80 =item GlobalCancel
81         New op dev for the circulation based on item, global is a function to cancel reserv,check other reserves, and transfer document if it's necessary
82 =cut
83 #'
84 sub GlobalCancel {
85         my $messages;
86         my $nextreservinfo;
87         my ($itemnumber,$borrowernumber)=@_;
88         
89 #       step 1 : cancel the reservation
90         my $CancelReserve = CancelReserve(0,$itemnumber,$borrowernumber);
91
92 #       step 2 launch the subroutine of the others reserves
93         my ($messages,$nextreservinfo) = OtherReserves($itemnumber);
94
95 return ($messages,$nextreservinfo);
96 }
97
98 =item OtherReserves
99         New op dev: check queued list of this document and check if this document must be  transfered
100 =cut
101 #'
102 sub OtherReserves {
103         my ($itemnumber)=@_;
104         my $messages;
105         my $nextreservinfo;
106         my ($restype,$checkreserves) = CheckReserves($itemnumber);
107         if ($checkreserves){
108                 my %env;
109                 my $iteminfo = C4::Circulation::Circ2::getiteminformation(\%env,$itemnumber);
110                 if ($iteminfo->{'holdingbranch'} ne $checkreserves->{'branchcode'}){
111                 $messages->{'transfert'} = $checkreserves->{'branchcode'};
112
113 #               minus priorities of others reservs
114                 MinusPriority($itemnumber,$checkreserves->{'borrowernumber'},$iteminfo->{'biblionumber'});
115 #               launch the subroutine dotransfer
116                 C4::Circulation::Circ2::dotransfer($itemnumber,$iteminfo->{'holdingbranch'},$checkreserves->{'branchcode'}),
117                 }
118
119 #       step 2b : case of a reservation on the same branch, set the waiting status
120                 else{
121                 $messages->{'waiting'} = 1;
122                 MinusPriority($itemnumber,$checkreserves->{'borrowernumber'},$iteminfo->{'biblionumber'});
123                 SetWaitingStatus($itemnumber);
124                 }
125
126                 $nextreservinfo = $checkreserves->{'borrowernumber'};
127         }
128
129         return ($messages,$nextreservinfo);     
130 }
131
132 =item MinusPriority
133         Reduce the values of queuded list       
134 =cut
135 #'
136 sub MinusPriority{
137         my ($itemnumber,$borrowernumber,$biblionumber)=@_;
138 #       first step update the value of the first person on reserv
139         my $dbh = C4::Context->dbh;
140         my $sth_upd=$dbh->prepare("UPDATE reserves SET priority = 0 , itemnumber = ? 
141         WHERE cancellationdate is NULL 
142         AND borrowernumber=?
143         AND biblionumber=?");
144         $sth_upd->execute($itemnumber,$borrowernumber,$biblionumber);
145         $sth_upd->finish;
146
147 # second step update all others reservs
148         my $sth_oth=$dbh->prepare("SELECT priority,borrowernumber,biblionumber,reservedate FROM reserves WHERE priority !='0' AND cancellationdate is NULL");
149         $sth_oth->execute();
150         while (my ($priority,$borrowernumber,$biblionumber,$reservedate)=$sth_oth->fetchrow_array){
151                 $priority--;
152                  my $sth_upd_oth = $dbh->prepare("UPDATE reserves SET priority = ?
153                                WHERE biblionumber     = ?
154                                  AND borrowernumber   = ?
155                                  AND reservedate      = ?");
156                 $sth_upd_oth->execute($priority,$biblionumber,$borrowernumber,$reservedate);
157                 $sth_upd_oth->finish;
158         }
159         $sth_oth->finish;
160
161 }
162
163
164 =item GlobalCancel
165         New op dev for the circulation based on item, global is a function to cancel reserv,check other reserves, and transfer document if it's necessary
166 =cut
167 #'
168 # New op dev :
169 # we check if we have a reserves with itemnumber (New op system of reserves), if we found one, we update the status of the reservation when we have : 'priority' = 0, and we have an itemnumber 
170 sub SetWaitingStatus{
171 #       first : check if we have a reservation for this item .
172         my ($itemnumber)=@_;
173         my $dbh = C4::Context->dbh;
174         my $sth_find=$dbh->prepare("SELECT priority,borrowernumber from reserves WHERE itemnumber=? and cancellationdate is NULL and found is NULL and priority='0'");
175         $sth_find->execute($itemnumber);
176         my ($priority,$borrowernumber) = $sth_find->fetchrow_array;
177         $sth_find->finish;
178         if (not $borrowernumber){
179                 return();
180         }
181         else{
182 #               step 2 : if we have a borrowernumber, we update the value found to 'W' for notify the borrower
183                 my $sth_set=$dbh->prepare("UPDATE reserves SET found='W',waitingdate = now() where borrowernumber=? AND itemnumber=? AND found is null");
184         $sth_set->execute($borrowernumber,$itemnumber);
185         $sth_set->finish;
186         }
187
188 }
189
190 sub FastFindReserves {
191         my ($itemnumber,$borrowernumber)=@_;
192         if ($itemnumber){
193                 my $dbh = C4::Context->dbh;
194                 my $sth_res=$dbh->prepare("SELECT reservedate,borrowernumber from reserves WHERE itemnumber=? and cancellationdate is NULL AND (found != 'F' or found is null)");
195                 $sth_res->execute($itemnumber);
196                 my ($reservedate,$borrowernumber)=$sth_res->fetchrow_array;
197                 $sth_res->finish;
198                 return($reservedate,$borrowernumber);
199         }
200         if ($borrowernumber){
201                 my $dbh = C4::Context->dbh;
202                 my $sth_find=$dbh->prepare("SELECT * from reserves WHERE borrowernumber=? and cancellationdate is NULL and (found != 'F' or found is null) order by reservedate");
203                 $sth_find->execute($borrowernumber);    
204                 my @borrowerreserv;
205                 my $i=0;
206                 while (my $data=$sth_find->fetchrow_hashref){
207                         $borrowerreserv[$i]=$data;
208                         $i++;
209                 }
210                 $sth_find->finish;
211                 return (@borrowerreserv);
212         }
213
214 }
215
216
217
218 =item FindReserves
219
220   ($count, $results) = &FindReserves($biblionumber, $borrowernumber);
221
222 Looks books up in the reserves. C<$biblionumber> is the biblionumber
223 of the book to look up. C<$borrowernumber> is the borrower number of a
224 patron whose books to look up.
225
226 Either C<$biblionumber> or C<$borrowernumber> may be the empty string,
227 but not both. If both are specified, C<&FindReserves> looks up the
228 given book for the given patron. If only C<$biblionumber> is
229 specified, C<&FindReserves> looks up that book for all patrons. If
230 only C<$borrowernumber> is specified, C<&FindReserves> looks up all of
231 that patron's reserves. If neither is specified, C<&FindReserves>
232 barfs.
233
234 For each book thus found, C<&FindReserves> checks the reserve
235 constraints and does something I don't understand.
236
237 C<&FindReserves> returns a two-element array:
238
239 C<$count> is the number of elements in C<$results>.
240
241 C<$results> is a reference to an array of references of hashes. Each hash
242 has for keys a list of column from reserves table (see details in function).
243
244 =cut
245 #'
246 sub FindReserves {
247     my ($bib, $bor) = @_;
248     my $dbh = C4::Context->dbh;
249     my @bind;
250
251     # Find the desired items in the reserves
252     my $query = '
253 SELECT branchcode,
254        timestamp AS rtimestamp,
255        priority,
256        biblionumber,
257        borrowernumber,
258        reservedate,
259        constrainttype,
260        found
261   FROM reserves
262   WHERE cancellationdate IS NULL
263     AND (found <> \'F\' OR found IS NULL)
264 ';
265
266     if ($bib ne '') {
267         $query.= '
268     AND biblionumber = ?
269 ';
270         push @bind, $bib;
271     }
272
273     if ($bor ne '') {
274         $query.= '
275     AND borrowernumber = ?
276 ';
277         push @bind, $bor;
278     }
279
280     $query.= '
281   ORDER BY priority
282 ';
283     my $sth=$dbh->prepare($query);
284     $sth->execute(@bind);
285     my @results;
286     while (my $data = $sth->fetchrow_hashref){
287         # FIXME - What is this if-statement doing? How do constraints work?
288         if ($data->{constrainttype} eq 'o') {
289             $query = '
290 SELECT biblioitemnumber
291   FROM reserveconstraints
292   WHERE biblionumber   = ?
293     AND borrowernumber = ?
294     AND reservedate    = ?
295 ';
296             my $csth=$dbh->prepare($query);
297             $csth->execute(
298                 $data->{biblionumber},
299                 $data->{borrowernumber},
300                 $data->{reservedate},
301             );
302             my ($bibitemno) = $csth->fetchrow_array;
303             $csth->finish;
304             # Look up the book we just found.
305             my $bdata = bibitemdata($bibitemno);
306             # Add the results of this latest search to the current
307             # results.
308             # FIXME - An 'each' would probably be more efficient.
309             foreach my $key (keys %$bdata) {
310                 $data->{$key} = $bdata->{$key};
311             }
312         }
313         push @results, $data;
314     }
315     $sth->finish;
316
317     return($#results+1,\@results);
318 }
319
320 sub GetNumberReservesFromBorrower {
321     my ($borrowernumber) = @_;
322
323     my $dbh = C4::Context->dbh;
324
325     my $query = '
326 SELECT COUNT(*) AS counter
327   FROM reserves
328   WHERE borrowernumber = ?
329     AND cancellationdate IS NULL
330     AND (found != \'F\' OR found IS NULL)
331 ';
332     my $sth = $dbh->prepare($query);
333     $sth->execute($borrowernumber);
334     my $row = $sth->fetchrow_hashref;
335     $sth->finish;
336
337     return $row->{counter};
338 }
339
340 sub GetFirstReserveDateFromItem {
341     my ($itemnumber) = @_;
342
343     my $dbh = C4::Context->dbh;
344
345     my $query = '
346 SELECT reservedate
347   FROM reserves
348   WHERE itemnumber = ?
349     AND cancellationdate IS NULL
350     AND (found != \'F\' OR found IS NULL)
351 ';
352     my $sth = $dbh->prepare($query);
353     $sth->execute($itemnumber);
354     my $row = $sth->fetchrow_hashref;
355     $sth->finish;
356
357     return $row->{reservedate};
358 }
359
360 =item CheckReserves
361
362   ($status, $reserve) = &CheckReserves($itemnumber, $barcode);
363
364 Find a book in the reserves.
365
366 C<$itemnumber> is the book's item number. C<$barcode> is its barcode.
367 Either one, but not both, may be false. If both are specified,
368 C<&CheckReserves> uses C<$itemnumber>.
369
370 $itemnubmer can be false, in which case uses the barcode. (Never uses
371 both. $itemnumber gets priority).
372
373 As I understand it, C<&CheckReserves> looks for the given item in the
374 reserves. If it is found, that's a match, and C<$status> is set to
375 C<Waiting>.
376
377 Otherwise, it finds the most important item in the reserves with the
378 same biblio number as this book (I'm not clear on this) and returns it
379 with C<$status> set to C<Reserved>.
380
381 C<&CheckReserves> returns a two-element list:
382
383 C<$status> is either C<Waiting>, C<Reserved> (see above), or 0.
384
385 C<$reserve> is the reserve item that matched. It is a
386 reference-to-hash whose keys are mostly the fields of the reserves
387 table in the Koha database.
388
389 =cut
390 #'
391 sub CheckReserves {
392     my ($item, $barcode) = @_;
393 #    warn "In CheckReserves: itemnumber = $item";
394     my $dbh = C4::Context->dbh;
395     my $sth;
396     if ($item) {
397         my $qitem=$dbh->quote($item);
398         # Look up the item by itemnumber
399         $sth=$dbh->prepare("SELECT items.biblionumber, items.biblioitemnumber, itemtypes.notforloan
400                              FROM items, biblioitems, itemtypes
401                             WHERE items.biblioitemnumber = biblioitems.biblioitemnumber
402                               AND biblioitems.itemtype = itemtypes.itemtype
403                               AND itemnumber=$qitem");
404     } else {
405         my $qbc=$dbh->quote($barcode);
406         # Look up the item by barcode
407         $sth=$dbh->prepare("SELECT items.biblionumber, items.biblioitemnumber, itemtypes.notforloan
408                              FROM items, biblioitems, itemtypes
409                             WHERE items.biblioitemnumber = biblioitems.biblioitemnumber
410                               AND biblioitems.itemtype = itemtypes.itemtype
411                               AND barcode=$qbc");
412         # FIXME - This function uses $item later on. Ought to set it here.
413     }
414     $sth->execute;
415     my ($biblio, $bibitem, $notforloan) = $sth->fetchrow_array;
416     $sth->finish;
417 # if item is not for loan it cannot be reserved either.....
418     return (0, 0) if ($notforloan);
419 # get the reserves...
420     # Find this item in the reserves
421     my ($count, @reserves) = Findgroupreserve($bibitem, $biblio);
422     # $priority and $highest are used to find the most important item
423     # in the list returned by &Findgroupreserve. (The lower $priority,
424     # the more important the item.)
425     # $highest is the most important item we've seen so far.
426     my $priority = 10000000;
427     my $highest;
428     if ($count) {
429         foreach my $res (@reserves) {
430             # FIXME - $item might be undefined or empty: the caller
431             # might be searching by barcode.
432             if ($res->{'itemnumber'} == $item) {
433                 # Found it
434                 return ("Waiting", $res);
435             } else {
436                 # See if this item is more important than what we've got
437                 # so far.
438                 if ($res->{'priority'} != 0 && $res->{'priority'} < $priority) {
439                     $priority = $res->{'priority'};
440                     $highest = $res;
441                 }
442             }
443         }
444     }
445
446     # If we get this far, then no exact match was found. Print the
447     # most important item on the list. I think this tells us who's
448     # next in line to get this book.
449     if ($highest) {     # FIXME - $highest might be undefined
450         $highest->{'itemnumber'} = $item;
451         return ("Reserved", $highest);
452     } else {
453         return (0, 0);
454     }
455 }
456
457 =item CancelReserve
458
459   &CancelReserve($biblionumber, $itemnumber, $borrowernumber);
460
461 Cancels a reserve.
462
463 Use either C<$biblionumber> or C<$itemnumber> to specify the item to
464 cancel, but not both: if both are given, C<&CancelReserve> does
465 nothing.
466
467 C<$borrowernumber> is the borrower number of the patron on whose
468 behalf the book was reserved.
469
470 If C<$biblionumber> was given, C<&CancelReserve> also adjusts the
471 priorities of the other people who are waiting on the book.
472
473 =cut
474 #'
475 sub CancelReserve {
476     my ($biblio, $item, $borr) = @_;
477     my $dbh = C4::Context->dbh;
478     #warn "In CancelReserve";
479     if (($item and $borr) and (not $biblio)) {
480                 # removing a waiting reserve record....
481                 # update the database...
482                 my $sth = $dbh->prepare("update reserves set cancellationdate = now(),
483                                                                                         found            = Null,
484                                                                                         priority         = 0
485                                                                         where itemnumber       = ?
486                                                                                 and borrowernumber   = ?");
487                 $sth->execute($item,$borr);
488                 $sth->finish;
489     }
490     if (($biblio and $borr) and (not $item)) {
491                 # removing a reserve record....
492                 # get the prioritiy on this record....
493                 my $priority;
494                 my $sth=$dbh->prepare("SELECT priority FROM reserves
495                                                                                 WHERE biblionumber   = ?
496                                                                                 AND borrowernumber = ?
497                                                                                 AND cancellationdate is NULL
498                                                                                 AND (found <> 'F' or found is NULL)");
499                 $sth->execute($biblio,$borr);
500                 ($priority) = $sth->fetchrow_array;
501                 $sth->finish;
502
503                 # update the database, removing the record...
504                 $sth = $dbh->prepare("update reserves set cancellationdate = now(),
505                                                                                         found            = Null,
506                                                                                         priority         = 0
507                                                                         where biblionumber     = ?
508                                                                                 and borrowernumber   = ?
509                                                                                 and cancellationdate is NULL
510                                                                                 and (found <> 'F' or found is NULL)");
511                 $sth->execute($biblio,$borr);
512                 $sth->finish;
513                 # now fix the priority on the others....
514                 fixpriority($priority, $biblio);
515     }
516 }
517
518 =item FillReserve
519
520   &FillReserve($reserve);
521
522 Fill a reserve. If I understand this correctly, this means that the
523 reserved book has been found and given to the patron who reserved it.
524
525 C<$reserve> specifies the reserve to fill. It is a reference-to-hash
526 whose keys are fields from the reserves table in the Koha database.
527
528 =cut
529 #'
530 sub FillReserve {
531     my ($res) = @_;
532     my $dbh = C4::Context->dbh;
533
534     # fill in a reserve record....
535     # FIXME - Remove some of the redundancy here
536     my $biblio = $res->{'biblionumber'}; my $qbiblio =$biblio;
537     my $borr = $res->{'borrowernumber'}; 
538     my $resdate = $res->{'reservedate'}; 
539
540     # get the priority on this record....
541     my $priority;
542     {
543     my $query = "SELECT priority FROM reserves
544                                 WHERE biblionumber   = ?
545                                   AND borrowernumber = ?
546                                   AND reservedate    = ?";
547     my $sth=$dbh->prepare($query);
548     $sth->execute($qbiblio,$borr,$resdate);
549     ($priority) = $sth->fetchrow_array;
550     $sth->finish;
551     }
552
553     # update the database...
554     {
555     my $query = "UPDATE reserves SET found            = 'F',
556                                      priority         = 0
557                                WHERE biblionumber     = ?
558                                  AND reservedate      = ?
559                                  AND borrowernumber   = ?";
560     my $sth = $dbh->prepare($query);
561     $sth->execute($qbiblio,$resdate,$borr);
562     $sth->finish;
563     }
564
565     # now fix the priority on the others (if the priority wasn't
566     # already sorted!)....
567     unless ($priority == 0) {
568         fixpriority($priority, $biblio);
569     }
570 }
571
572 # Only used internally + reorder_reserve.pl
573 # Changed how this functions works #
574 # Now just gets an array of reserves in the rank order and updates them with
575 # the array index (+1 as array starts from 0)
576 # and if $rank is supplied will splice item from the array and splice it back in again
577 # in new priority rank
578 sub fixpriority {
579     my ($biblio,$borrowernumber,$rank) =  @_;
580     my $dbh = C4::Context->dbh;
581
582     warn "BIB: $biblio, BORR: $borrowernumber, RANK: $rank";
583     if($rank eq "del"){
584         warn "Cancel";
585         CancelReserve($biblio,undef,$borrowernumber);
586     }
587     if($rank eq "W" || $rank eq "0"){
588         # make sure priority for waiting items is 0
589         my $sth=$dbh->prepare("UPDATE reserves SET priority = 0
590                          WHERE biblionumber = ?
591                          AND borrowernumber = ?
592                          AND cancellationdate is NULL
593                          AND found ='W'");
594     $sth->execute($biblio,$borrowernumber);
595     }
596     my @priority;
597     my @reservedates;
598     # get whats left
599     my $sth=$dbh->prepare("SELECT borrowernumber, reservedate, constrainttype FROM reserves
600                          WHERE biblionumber   = ?
601                          AND cancellationdate is NULL
602                          AND ((found <> 'F' and found <> 'W') or found is NULL) ORDER BY priority ASC");
603     $sth->execute($biblio);
604     while(my $line = $sth->fetchrow_hashref){
605          push(@reservedates,$line);
606          push(@priority,$line);
607     }
608     # To find the matching index
609     my $i;
610     my $key = -1; # to allow for 0 to be a valid result
611     for ($i = 0; $i < @priority; $i++) {
612            if ($borrowernumber == $priority[$i]->{'borrowernumber'}) {
613                $key = $i; # save the index
614                last;
615             }
616     }
617     warn "key: $key";
618     # if index exists in array then move it to new position
619     if($key>-1 && $rank ne 'del' && $rank > 0){
620           my $new_rank = $rank-1; # $new_rank is what you want the new index to be in the array
621           my $moving_item = splice(@priority, $key, 1);
622           splice(@priority, $new_rank, 0, $moving_item);
623     }
624     # now fix the priority on those that are left....
625     for(my $j=0;$j<@priority;$j++){
626  # warn "update reserves set priority = ".($j+1)." where biblionumber = $biblio and borrowernumber = $priority[$j]->{'borrowernumber'} ";
627  # warn "and reservedate =$priority[$j]->{'reservedate'}";
628          my $sth = $dbh->prepare("UPDATE reserves SET priority = " . ($j+1 ) . "
629                            WHERE biblionumber     = ?
630                            AND borrowernumber   = ?
631                            AND reservedate = ? and found is null");
632         $sth->execute($biblio,$priority[$j]->{'borrowernumber'},$priority[$j]->{'reservedate'});
633         $sth->finish;
634     }
635 }
636
637 # XXX - POD
638 sub ReserveWaiting {
639     my ($item, $borr) = @_;
640     my $dbh = C4::Context->dbh;
641 # get priority and biblionumber....
642     my $sth = $dbh->prepare("SELECT reserves.priority     as priority,
643                         reserves.biblionumber as biblionumber,
644                         reserves.branchcode   as branchcode,
645                         reserves.timestamp     as timestamp
646                       FROM reserves,items
647                      WHERE reserves.biblionumber   = items.biblionumber
648                        AND items.itemnumber        = ?
649                        AND reserves.borrowernumber = ?
650                        AND reserves.cancellationdate is NULL
651                        AND (reserves.found <> 'F' or reserves.found is NULL)");
652     $sth->execute($item,$borr);
653     my $data = $sth->fetchrow_hashref;
654     $sth->finish;
655     my $biblio = $data->{'biblionumber'};
656     my $timestamp = $data->{'timestamp'};
657 # update reserves record....
658     $sth = $dbh->prepare("UPDATE reserves SET priority = 0, found = 'W', itemnumber = ?
659                             WHERE borrowernumber = ?
660                               AND biblionumber = ?
661                               AND timestamp = ?");
662     $sth->execute($item,$borr,$biblio,$timestamp);
663     $sth->finish;
664 # now fix up the remaining priorities....
665     fixpriority($data->{'priority'}, $biblio);
666     my $branchcode = $data->{'branchcode'};
667     return $branchcode;
668 }
669
670 # XXX - POD
671 sub CheckWaiting {
672     my ($borr)=@_;
673     my $dbh = C4::Context->dbh;
674     my @itemswaiting;
675     my $sth = $dbh->prepare("SELECT * FROM reserves
676                          WHERE borrowernumber = ?
677                            AND reserves.found = 'W'
678                            AND cancellationdate is NULL");
679     $sth->execute($borr);
680     while (my $data=$sth->fetchrow_hashref) {
681           push(@itemswaiting,$data);
682     }
683     $sth->finish;
684     return (scalar(@itemswaiting),\@itemswaiting);
685 }
686
687 =item Findgroupreserve
688
689   ($count, @results) = &Findgroupreserve($biblioitemnumber, $biblionumber);
690
691 I don't know what this does, because I don't understand how reserve
692 constraints work. I think the idea is that you reserve a particular
693 biblio, and the constraint allows you to restrict it to a given
694 biblioitem (e.g., if you want to borrow the audio book edition of "The
695 Prophet", rather than the first available publication).
696
697 C<&Findgroupreserve> returns a two-element array:
698
699 C<$count> is the number of elements in C<@results>.
700
701 C<@results> is an array of references-to-hash whose keys are mostly
702 fields from the reserves table of the Koha database, plus
703 C<biblioitemnumber>.
704
705 =cut
706 #'
707 sub Findgroupreserve {
708   my ($bibitem,$biblio)=@_;
709   my $dbh = C4::Context->dbh;
710   my $sth=$dbh->prepare("SELECT reserves.biblionumber               AS biblionumber,
711                       reserves.borrowernumber             AS borrowernumber,
712                       reserves.reservedate                AS reservedate,
713                       reserves.branchcode                 AS branchcode,
714                       reserves.cancellationdate           AS cancellationdate,
715                       reserves.found                      AS found,
716                       reserves.reservenotes               AS reservenotes,
717                       reserves.priority                   AS priority,
718                       reserves.timestamp                  AS timestamp,
719                       reserveconstraints.biblioitemnumber AS biblioitemnumber,
720                       reserves.itemnumber                 AS itemnumber
721                  FROM reserves LEFT JOIN reserveconstraints
722                    ON reserves.biblionumber = reserveconstraints.biblionumber
723                 WHERE reserves.biblionumber = ?
724                   AND ( ( reserveconstraints.biblioitemnumber = ?
725                       AND reserves.borrowernumber = reserveconstraints.borrowernumber
726                       AND reserves.reservedate    =reserveconstraints.reservedate )
727                    OR reserves.constrainttype='a' )
728                   AND reserves.cancellationdate is NULL
729                   AND (reserves.found <> 'F' or reserves.found is NULL)");
730   $sth->execute($biblio, $bibitem);
731   my @results;
732   while (my $data=$sth->fetchrow_hashref){
733     push(@results,$data);
734   }
735   $sth->finish;
736   return(scalar(@results),@results);
737 }
738
739 # FIXME - A somewhat different version of this function appears in
740 # C4::Reserves. Pick one and stick with it.
741 # XXX - POD
742 sub CreateReserve {
743   my ($env,$branch,$borrnum,$biblionumber,$constraint,$bibitems,$priority,$notes,$title,$checkitem,$found)= @_;
744   my $fee;
745   if($library_name =~ /Horowhenua/){
746       $fee = CalcHLTReserveFee($env,$borrnum,$biblionumber,$constraint,$bibitems);
747   } else {
748       $fee = CalcReserveFee($env,$borrnum,$biblionumber,$constraint,$bibitems);
749   }
750   my $dbh = C4::Context->dbh;
751   my $const = lc substr($constraint,0,1);
752   my @datearr = localtime(time);
753   my $resdate =(1900+$datearr[5])."-".($datearr[4]+1)."-".$datearr[3];
754   my $waitingdate;
755 # If the reserv had the waiting status, we had the value of the resdate
756   if ($found eq 'W'){
757   $waitingdate = $resdate;
758   }
759   #eval {
760   # updates take place here
761   if ($fee > 0) {
762 #    print $fee;
763     my $nextacctno = &getnextacctno($env,$borrnum,$dbh);
764     my $usth = $dbh->prepare("insert into accountlines
765     (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding)
766                                                           values
767     (?,?,now(),?,?,'Res',?)");
768     $usth->execute($borrnum,$nextacctno,$fee,"Reserve Charge - $title",$fee);
769     $usth->finish;
770   }
771   #if ($const eq 'a'){
772     my $sth = $dbh->prepare("insert into reserves
773    (borrowernumber,biblionumber,reservedate,branchcode,constrainttype,priority,reservenotes,itemnumber,found,waitingdate)
774     values (?,?,?,?,?,?,?,?,?,?)");
775     $sth->execute($borrnum,$biblionumber,$resdate,$branch,$const,$priority,$notes,$checkitem,$found,$waitingdate);
776     $sth->finish;
777   #}
778   if (($const eq "o") || ($const eq "e")) {
779     my $numitems = @$bibitems;
780     my $i = 0;
781     while ($i < $numitems) {
782       my $biblioitem = @$bibitems[$i];
783       my $sth = $dbh->prepare("insert into
784       reserveconstraints
785       (borrowernumber,biblionumber,reservedate,biblioitemnumber)
786       values (?,?,?,?)");
787       $sth->execute($borrnum,$biblionumber,$resdate,$biblioitem);
788       $sth->finish;
789       $i++;
790     }
791   }
792 #  print $query;
793   return();
794 }
795
796 # FIXME - A functionally identical version of this function appears in
797 # C4::Reserves. Pick one and stick with it.
798 # XXX - Internal use only
799 # FIXME - opac-reserves.pl need to use it, temporarily put into @EXPORT
800 sub CalcReserveFee {
801   my ($env,$borrnum,$biblionumber,$constraint,$bibitems) = @_;
802   #check for issues;
803   my $dbh = C4::Context->dbh;
804   my $const = lc substr($constraint,0,1);
805   my $sth = $dbh->prepare("SELECT * FROM borrowers,categories
806                 WHERE (borrowernumber = ?)
807                   AND (borrowers.categorycode = categories.categorycode)");
808   $sth->execute($borrnum);
809   my $data = $sth->fetchrow_hashref;
810   $sth->finish();
811   my $fee = $data->{'reservefee'};
812   my $cntitems = @->$bibitems;
813   if ($fee > 0) {
814     # check for items on issue
815     # first find biblioitem records
816     my @biblioitems;
817     my $sth1 = $dbh->prepare("SELECT * FROM biblio,biblioitems
818                    WHERE (biblio.biblionumber = ?)
819                      AND (biblio.biblionumber = biblioitems.biblionumber)");
820     $sth1->execute($biblionumber);
821     while (my $data1=$sth1->fetchrow_hashref) {
822       if ($const eq "a") {
823         push @biblioitems,$data1;
824       } else {
825         my $found = 0;
826         my $x = 0;
827         while ($x < $cntitems) {
828           if (@$bibitems->{'biblioitemnumber'} == $data->{'biblioitemnumber'}) {
829             $found = 1;
830           }
831           $x++;
832         }
833         if ($const eq 'o') {
834           if ( $found == 1) {
835             push @biblioitems,$data1;
836           }
837         } else {
838           if ($found == 0) {
839             push @biblioitems,$data1;
840           }
841         }
842       }
843     }
844     $sth1->finish;
845     my $cntitemsfound = @biblioitems;
846     my $issues = 0;
847     my $x = 0;
848     my $allissued = 1;
849     while ($x < $cntitemsfound) {
850       my $bitdata = $biblioitems[$x];
851       my $sth2 = $dbh->prepare("SELECT * FROM items
852                      WHERE biblioitemnumber = ?");
853       $sth2->execute($bitdata->{'biblioitemnumber'});
854       while (my $itdata=$sth2->fetchrow_hashref) {
855         my $sth3 = $dbh->prepare("SELECT * FROM issues
856                        WHERE itemnumber = ?
857                          AND returndate IS NULL");
858         $sth3->execute($itdata->{'itemnumber'});
859         if (my $isdata=$sth3->fetchrow_hashref) {
860         } else {
861           $allissued = 0;
862         }
863       }
864       $x++;
865     }
866     if ($allissued == 0) {
867       my $rsth = $dbh->prepare("SELECT * FROM reserves WHERE biblionumber = ?");
868       $rsth->execute($biblionumber);
869       if (my $rdata = $rsth->fetchrow_hashref) {
870       } else {
871         $fee = 0;
872       }
873     }
874   }
875 #  print "fee $fee";
876   return $fee;
877 }
878
879 # The following are junior and young adult item types that should not incur a
880 # reserve charge.
881 #
882 # Juniors: BJC, BJCN, BJF, BJK, BJM, BJN, BJP, BJSF, BJSN, DJ, DJP, FJ, JVID,
883 #  VJ, VJP, PJ, TJ, TJP, VJ, VJP.
884 #
885 # Young adults: BYF, BYN, BYP, DY, DYP, PY, PYP, TY, TYP, VY, VYP.
886 #
887 # All other item types should incur a reserve charge.
888 sub CalcHLTReserveFee {
889     my ($env,$borrnum,$biblionumber,$constraint,$bibitems) = @_;
890     my $dbh = C4::Context->dbh;
891     my $sth = $dbh->prepare("SELECT * FROM borrowers,categories
892                   WHERE (borrowernumber = ?)
893                     AND (borrowers.categorycode = categories.categorycode)");
894     $sth->execute($borrnum);
895     my $data = $sth->fetchrow_hashref;
896     $sth->finish();
897     my $fee = $data->{'reservefee'};
898
899     my $matchno;
900     my @nocharge = qw/BJC BJCN BJF BJK BJM BJN BJP BJSF BJSN DJ DJP FJ NJ CJ VJ VJP PJ TJ TJP BYF BYN BYP DY DYP PY PYP TY TYP VY VYP/;
901     my $sth = $dbh->prepare("SELECT * FROM biblio,biblioitems
902                      WHERE (biblio.biblionumber = ?)
903                        AND (biblio.biblionumber = biblioitems.biblionumber)");
904     $sth->execute($biblionumber);
905     my $data=$sth->fetchrow_hashref;
906     my $itemtype = $data->{'itemtype'};
907     for (my $i = 0; $i < @nocharge; $i++) {
908         if ($itemtype eq $nocharge[$i]) {
909             $matchno++;
910             last;
911         }
912     }
913
914     if($matchno>0){
915         $fee = 0;
916     }
917   warn "BOB DEBUG: Fee is $fee";
918   return $fee;
919 }
920
921
922
923 # XXX - Internal use
924 sub getnextacctno {
925   my ($env,$bornumber,$dbh)=@_;
926   my $nextaccntno = 1;
927   my $sth = $dbh->prepare("select * from accountlines
928   where (borrowernumber = ?)
929   order by accountno desc");
930   $sth->execute($bornumber);
931   if (my $accdata=$sth->fetchrow_hashref){
932     $nextaccntno = $accdata->{'accountno'} + 1;
933   }
934   $sth->finish;
935   return($nextaccntno);
936 }
937
938 # XXX - POD
939 sub updatereserves{
940   #subroutine to update a reserve
941   my ($rank,$biblio,$borrower,$del,$branch)=@_;
942   my $dbh = C4::Context->dbh;
943   if ($del == 0){
944     my $sth = $dbh->prepare("Update reserves set priority=?,branchcode=? where
945     biblionumber=? and borrowernumber=?");
946     $sth->execute($rank,$branch,$biblio,$borrower);
947     $sth->finish();
948   } else {
949     my $sth=$dbh->prepare("Select * from reserves where biblionumber=? and
950     borrowernumber=?");
951     $sth->execute($biblio,$borrower);
952     my $data=$sth->fetchrow_hashref;
953     $sth->finish();
954     $sth=$dbh->prepare("Select * from reserves where biblionumber=? and
955     priority > ? and cancellationdate is NULL
956     order by priority") || die $dbh->errstr;
957     $sth->execute($biblio,$data->{'priority'}) || die $sth->errstr;
958     while (my $data=$sth->fetchrow_hashref){
959       $data->{'priority'}--;
960       my $sth3=$dbh->prepare("Update reserves set priority=?
961       where biblionumber=? and borrowernumber=?");
962       $sth3->execute($data->{'priority'},$data->{'biblionumber'},$data->{'borrowernumber'}) || die $sth3->errstr;
963       $sth3->finish();
964     }
965     $sth->finish();
966     $sth=$dbh->prepare("update reserves set cancellationdate=now() where biblionumber=?
967     and borrowernumber=?");
968     $sth->execute($biblio,$borrower);
969     $sth->finish;
970   }
971 }
972
973 # XXX - POD
974 sub UpdateReserve {
975     #subroutine to update a reserve
976     my ($rank,$biblio,$borrower,$branch)=@_;
977     return if $rank eq "W";
978     return if $rank eq "n";
979     my $dbh = C4::Context->dbh;
980     if ($rank eq "del") {
981         my $sth=$dbh->prepare("UPDATE reserves SET cancellationdate=now()
982                                    WHERE biblionumber   = ?
983                                      AND borrowernumber = ?
984                                      AND cancellationdate is NULL
985                                      AND (found <> 'F' or found is NULL)");
986         $sth->execute($biblio, $borrower);
987         $sth->finish;
988     } else {
989         my $sth=$dbh->prepare("UPDATE reserves SET priority = ? ,branchcode = ?, itemnumber = NULL, found = NULL
990                                    WHERE biblionumber   = ?
991                                      AND borrowernumber = ?
992                                      AND cancellationdate is NULL
993                                      AND (found <> 'F' or found is NULL)");
994         $sth->execute($rank, $branch, $biblio, $borrower);
995         $sth->finish;
996     }
997 }
998
999 # XXX - POD
1000 sub getreservetitle {
1001  my ($biblio,$bor,$date,$timestamp)=@_;
1002  my $dbh = C4::Context->dbh;
1003  my $sth=$dbh->prepare("Select * from reserveconstraints,biblioitems where
1004  reserveconstraints.biblioitemnumber=biblioitems.biblioitemnumber
1005  and reserveconstraints.biblionumber=? and reserveconstraints.borrowernumber
1006  = ? and reserveconstraints.reservedate=? and
1007  reserveconstraints.timestamp=?");
1008  $sth->execute($biblio,$bor,$date,$timestamp);
1009  my $data=$sth->fetchrow_hashref;
1010  $sth->finish;
1011  return($data);
1012 }