add call to doc-head-open.inc and doc-head-close.inc
[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     my $i = 0;
287     while (my $data = $sth->fetchrow_hashref){
288         # FIXME - What is this if-statement doing? How do constraints work?
289         if ($data->{constrainttype} eq 'o') {
290             $query = '
291 SELECT biblioitemnumber
292   FROM reserveconstraints
293   WHERE biblionumber   = ?
294     AND borrowernumber = ?
295     AND reservedate    = ?
296 ';
297             my $csth=$dbh->prepare($query);
298             $csth->execute(
299                 $data->{biblionumber},
300                 $data->{borrowernumber},
301                 $data->{reservedate},
302             );
303
304            my @bibitemno;
305            while (my $bibitemnos = $csth->fetchrow_array){
306                 push (@bibitemno,$bibitemnos);
307            }
308            my $count = @bibitemno;
309
310            # if we have two or more different specific itemtypes
311            # reserved by same person on same day
312            my $bdata;
313            if($count > 1){
314                 warn "bibitemno $bibitemno[$i]";
315                 $bdata = C4::Search::bibitemdata($bibitemno[$i]);
316                 $i++;
317            } else {
318                 # Look up the book we just found.
319                 $bdata = C4::Search::bibitemdata($bibitemno[0]);
320            }
321            $csth->finish;
322            # Add the results of this latest search to the current
323            # results.
324            # FIXME - An 'each' would probably be more efficient.
325            foreach my $key (keys %$bdata) {
326                $data->{$key} = $bdata->{$key};
327            }
328         }
329         push @results, $data;
330     }
331     $sth->finish;
332
333     return($#results+1,\@results);
334 }
335
336 sub GetNumberReservesFromBorrower {
337     my ($borrowernumber) = @_;
338
339     my $dbh = C4::Context->dbh;
340
341     my $query = '
342 SELECT COUNT(*) AS counter
343   FROM reserves
344   WHERE borrowernumber = ?
345     AND cancellationdate IS NULL
346     AND (found != \'F\' OR found IS NULL)
347 ';
348     my $sth = $dbh->prepare($query);
349     $sth->execute($borrowernumber);
350     my $row = $sth->fetchrow_hashref;
351     $sth->finish;
352
353     return $row->{counter};
354 }
355
356 sub GetFirstReserveDateFromItem {
357     my ($itemnumber) = @_;
358
359     my $dbh = C4::Context->dbh;
360
361     my $query = '
362 SELECT reservedate
363   FROM reserves
364   WHERE itemnumber = ?
365     AND cancellationdate IS NULL
366     AND (found != \'F\' OR found IS NULL)
367 ';
368     my $sth = $dbh->prepare($query);
369     $sth->execute($itemnumber);
370     my $row = $sth->fetchrow_hashref;
371     $sth->finish;
372
373     return $row->{reservedate};
374 }
375
376 =item CheckReserves
377
378   ($status, $reserve) = &CheckReserves($itemnumber, $barcode);
379
380 Find a book in the reserves.
381
382 C<$itemnumber> is the book's item number. C<$barcode> is its barcode.
383 Either one, but not both, may be false. If both are specified,
384 C<&CheckReserves> uses C<$itemnumber>.
385
386 $itemnubmer can be false, in which case uses the barcode. (Never uses
387 both. $itemnumber gets priority).
388
389 As I understand it, C<&CheckReserves> looks for the given item in the
390 reserves. If it is found, that's a match, and C<$status> is set to
391 C<Waiting>.
392
393 Otherwise, it finds the most important item in the reserves with the
394 same biblio number as this book (I'm not clear on this) and returns it
395 with C<$status> set to C<Reserved>.
396
397 C<&CheckReserves> returns a two-element list:
398
399 C<$status> is either C<Waiting>, C<Reserved> (see above), or 0.
400
401 C<$reserve> is the reserve item that matched. It is a
402 reference-to-hash whose keys are mostly the fields of the reserves
403 table in the Koha database.
404
405 =cut
406 #'
407 sub CheckReserves {
408     my ($item, $barcode) = @_;
409 #    warn "In CheckReserves: itemnumber = $item";
410     my $dbh = C4::Context->dbh;
411     my $sth;
412     if ($item) {
413         my $qitem=$dbh->quote($item);
414         # Look up the item by itemnumber
415         $sth=$dbh->prepare("SELECT items.biblionumber, items.biblioitemnumber, itemtypes.notforloan
416                              FROM items, biblioitems, itemtypes
417                             WHERE items.biblioitemnumber = biblioitems.biblioitemnumber
418                               AND biblioitems.itemtype = itemtypes.itemtype
419                               AND itemnumber=$qitem");
420     } else {
421         my $qbc=$dbh->quote($barcode);
422         # Look up the item by barcode
423         $sth=$dbh->prepare("SELECT items.biblionumber, items.biblioitemnumber, itemtypes.notforloan
424                              FROM items, biblioitems, itemtypes
425                             WHERE items.biblioitemnumber = biblioitems.biblioitemnumber
426                               AND biblioitems.itemtype = itemtypes.itemtype
427                               AND barcode=$qbc");
428         # FIXME - This function uses $item later on. Ought to set it here.
429     }
430     $sth->execute;
431     my ($biblio, $bibitem, $notforloan) = $sth->fetchrow_array;
432     $sth->finish;
433 # if item is not for loan it cannot be reserved either.....
434     return (0, 0) if ($notforloan);
435 # get the reserves...
436     # Find this item in the reserves
437     my ($count, @reserves) = Findgroupreserve($bibitem, $biblio);
438     # $priority and $highest are used to find the most important item
439     # in the list returned by &Findgroupreserve. (The lower $priority,
440     # the more important the item.)
441     # $highest is the most important item we've seen so far.
442     my $priority = 10000000;
443     my $highest;
444     if ($count) {
445         foreach my $res (@reserves) {
446             # FIXME - $item might be undefined or empty: the caller
447             # might be searching by barcode.
448             if ($res->{'itemnumber'} == $item) {
449                 # Found it
450                 return ("Waiting", $res);
451             } else {
452                 # See if this item is more important than what we've got
453                 # so far.
454                 if ($res->{'priority'} != 0 && $res->{'priority'} < $priority) {
455                     $priority = $res->{'priority'};
456                     $highest = $res;
457                 }
458             }
459         }
460     }
461
462     # If we get this far, then no exact match was found. Print the
463     # most important item on the list. I think this tells us who's
464     # next in line to get this book.
465     if ($highest) {     # FIXME - $highest might be undefined
466         $highest->{'itemnumber'} = $item;
467         return ("Reserved", $highest);
468     } else {
469         return (0, 0);
470     }
471 }
472
473 =item CancelReserve
474
475   &CancelReserve($biblionumber, $itemnumber, $borrowernumber);
476
477 Cancels a reserve.
478
479 Use either C<$biblionumber> or C<$itemnumber> to specify the item to
480 cancel, but not both: if both are given, C<&CancelReserve> does
481 nothing.
482
483 C<$borrowernumber> is the borrower number of the patron on whose
484 behalf the book was reserved.
485
486 If C<$biblionumber> was given, C<&CancelReserve> also adjusts the
487 priorities of the other people who are waiting on the book.
488
489 =cut
490 #'
491 sub CancelReserve {
492     my ($biblio, $item, $borr) = @_;
493     my $dbh = C4::Context->dbh;
494     #warn "In CancelReserve";
495     if (($item and $borr) and (not $biblio)) {
496                 # removing a waiting reserve record....
497                 # update the database...
498                 my $sth = $dbh->prepare("update reserves set cancellationdate = now(),
499                                                                                         found            = Null,
500                                                                                         priority         = 0
501                                                                         where itemnumber       = ?
502                                                                                 and borrowernumber   = ?");
503                 $sth->execute($item,$borr);
504                 $sth->finish;
505     }
506     if (($biblio and $borr) and (not $item)) {
507                 # removing a reserve record....
508                 # get the prioritiy on this record....
509                 my $priority;
510                 my $sth=$dbh->prepare("SELECT priority FROM reserves
511                                                                                 WHERE biblionumber   = ?
512                                                                                 AND borrowernumber = ?
513                                                                                 AND cancellationdate is NULL
514                                                                                 AND (found <> 'F' or found is NULL)");
515                 $sth->execute($biblio,$borr);
516                 ($priority) = $sth->fetchrow_array;
517                 $sth->finish;
518
519                 # update the database, removing the record...
520                 $sth = $dbh->prepare("update reserves set cancellationdate = now(),
521                                                                                         found            = Null,
522                                                                                         priority         = 0
523                                                                         where biblionumber     = ?
524                                                                                 and borrowernumber   = ?
525                                                                                 and cancellationdate is NULL
526                                                                                 and (found <> 'F' or found is NULL)");
527                 $sth->execute($biblio,$borr);
528                 $sth->finish;
529                 # now fix the priority on the others....
530                 fixpriority($priority, $biblio);
531     }
532 }
533
534 =item FillReserve
535
536   &FillReserve($reserve);
537
538 Fill a reserve. If I understand this correctly, this means that the
539 reserved book has been found and given to the patron who reserved it.
540
541 C<$reserve> specifies the reserve to fill. It is a reference-to-hash
542 whose keys are fields from the reserves table in the Koha database.
543
544 =cut
545 #'
546 sub FillReserve {
547     my ($res) = @_;
548     my $dbh = C4::Context->dbh;
549
550     # fill in a reserve record....
551     # FIXME - Remove some of the redundancy here
552     my $biblio = $res->{'biblionumber'}; my $qbiblio =$biblio;
553     my $borr = $res->{'borrowernumber'}; 
554     my $resdate = $res->{'reservedate'}; 
555
556     # get the priority on this record....
557     my $priority;
558     {
559     my $query = "SELECT priority FROM reserves
560                                 WHERE biblionumber   = ?
561                                   AND borrowernumber = ?
562                                   AND reservedate    = ?";
563     my $sth=$dbh->prepare($query);
564     $sth->execute($qbiblio,$borr,$resdate);
565     ($priority) = $sth->fetchrow_array;
566     $sth->finish;
567     }
568
569     # update the database...
570     {
571     my $query = "UPDATE reserves SET found            = 'F',
572                                      priority         = 0
573                                WHERE biblionumber     = ?
574                                  AND reservedate      = ?
575                                  AND borrowernumber   = ?";
576     my $sth = $dbh->prepare($query);
577     $sth->execute($qbiblio,$resdate,$borr);
578     $sth->finish;
579     }
580
581     # now fix the priority on the others (if the priority wasn't
582     # already sorted!)....
583     unless ($priority == 0) {
584         fixpriority($priority, $biblio);
585     }
586 }
587
588 # Only used internally + reorder_reserve.pl
589 # Changed how this functions works #
590 # Now just gets an array of reserves in the rank order and updates them with
591 # the array index (+1 as array starts from 0)
592 # and if $rank is supplied will splice item from the array and splice it back in again
593 # in new priority rank
594 sub fixpriority {
595     my ($biblio,$borrowernumber,$rank) =  @_;
596     my $dbh = C4::Context->dbh;
597
598     warn "BIB: $biblio, BORR: $borrowernumber, RANK: $rank";
599     if($rank eq "del"){
600         warn "Cancel";
601         CancelReserve($biblio,undef,$borrowernumber);
602     }
603     if($rank eq "W" || $rank eq "0"){
604         # make sure priority for waiting items is 0
605         my $sth=$dbh->prepare("UPDATE reserves SET priority = 0
606                          WHERE biblionumber = ?
607                          AND borrowernumber = ?
608                          AND cancellationdate is NULL
609                          AND found ='W'");
610     $sth->execute($biblio,$borrowernumber);
611     }
612     my @priority;
613     my @reservedates;
614     # get whats left
615     my $sth=$dbh->prepare("SELECT borrowernumber, reservedate, constrainttype FROM reserves
616                          WHERE biblionumber   = ?
617                          AND cancellationdate is NULL
618                          AND ((found <> 'F' and found <> 'W') or found is NULL) ORDER BY priority ASC");
619     $sth->execute($biblio);
620     while(my $line = $sth->fetchrow_hashref){
621          push(@reservedates,$line);
622          push(@priority,$line);
623     }
624     # To find the matching index
625     my $i;
626     my $key = -1; # to allow for 0 to be a valid result
627     for ($i = 0; $i < @priority; $i++) {
628            if ($borrowernumber == $priority[$i]->{'borrowernumber'}) {
629                $key = $i; # save the index
630                last;
631             }
632     }
633     warn "key: $key";
634     # if index exists in array then move it to new position
635     if($key>-1 && $rank ne 'del' && $rank > 0){
636           my $new_rank = $rank-1; # $new_rank is what you want the new index to be in the array
637           my $moving_item = splice(@priority, $key, 1);
638           splice(@priority, $new_rank, 0, $moving_item);
639     }
640     # now fix the priority on those that are left....
641     for(my $j=0;$j<@priority;$j++){
642  # warn "update reserves set priority = ".($j+1)." where biblionumber = $biblio and borrowernumber = $priority[$j]->{'borrowernumber'} ";
643  # warn "and reservedate =$priority[$j]->{'reservedate'}";
644          my $sth = $dbh->prepare("UPDATE reserves SET priority = " . ($j+1 ) . "
645                            WHERE biblionumber     = ?
646                            AND borrowernumber   = ?
647                            AND reservedate = ? and found is null");
648         $sth->execute($biblio,$priority[$j]->{'borrowernumber'},$priority[$j]->{'reservedate'});
649         $sth->finish;
650     }
651 }
652
653 # XXX - POD
654 sub ReserveWaiting {
655     my ($item, $borr) = @_;
656     my $dbh = C4::Context->dbh;
657 # get priority and biblionumber....
658     my $sth = $dbh->prepare("SELECT reserves.priority     as priority,
659                         reserves.biblionumber as biblionumber,
660                         reserves.branchcode   as branchcode,
661                         reserves.timestamp     as timestamp
662                       FROM reserves,items
663                      WHERE reserves.biblionumber   = items.biblionumber
664                        AND items.itemnumber        = ?
665                        AND reserves.borrowernumber = ?
666                        AND reserves.cancellationdate is NULL
667                        AND (reserves.found <> 'F' or reserves.found is NULL)");
668     $sth->execute($item,$borr);
669     my $data = $sth->fetchrow_hashref;
670     $sth->finish;
671     my $biblio = $data->{'biblionumber'};
672     my $timestamp = $data->{'timestamp'};
673 # update reserves record....
674     $sth = $dbh->prepare("UPDATE reserves SET priority = 0, found = 'W', itemnumber = ?
675                             WHERE borrowernumber = ?
676                               AND biblionumber = ?
677                               AND timestamp = ?");
678     $sth->execute($item,$borr,$biblio,$timestamp);
679     $sth->finish;
680 # now fix up the remaining priorities....
681     fixpriority($data->{'priority'}, $biblio);
682     my $branchcode = $data->{'branchcode'};
683     return $branchcode;
684 }
685
686 # XXX - POD
687 sub CheckWaiting {
688     my ($borr)=@_;
689     my $dbh = C4::Context->dbh;
690     my @itemswaiting;
691     my $sth = $dbh->prepare("SELECT * FROM reserves
692                          WHERE borrowernumber = ?
693                            AND reserves.found = 'W'
694                            AND cancellationdate is NULL");
695     $sth->execute($borr);
696     while (my $data=$sth->fetchrow_hashref) {
697           push(@itemswaiting,$data);
698     }
699     $sth->finish;
700     return (scalar(@itemswaiting),\@itemswaiting);
701 }
702
703 =item Findgroupreserve
704
705   ($count, @results) = &Findgroupreserve($biblioitemnumber, $biblionumber);
706
707 I don't know what this does, because I don't understand how reserve
708 constraints work. I think the idea is that you reserve a particular
709 biblio, and the constraint allows you to restrict it to a given
710 biblioitem (e.g., if you want to borrow the audio book edition of "The
711 Prophet", rather than the first available publication).
712
713 C<&Findgroupreserve> returns a two-element array:
714
715 C<$count> is the number of elements in C<@results>.
716
717 C<@results> is an array of references-to-hash whose keys are mostly
718 fields from the reserves table of the Koha database, plus
719 C<biblioitemnumber>.
720
721 =cut
722 #'
723 sub Findgroupreserve {
724   my ($bibitem,$biblio)=@_;
725   my $dbh = C4::Context->dbh;
726   my $sth=$dbh->prepare("SELECT reserves.biblionumber               AS biblionumber,
727                       reserves.borrowernumber             AS borrowernumber,
728                       reserves.reservedate                AS reservedate,
729                       reserves.branchcode                 AS branchcode,
730                       reserves.cancellationdate           AS cancellationdate,
731                       reserves.found                      AS found,
732                       reserves.reservenotes               AS reservenotes,
733                       reserves.priority                   AS priority,
734                       reserves.timestamp                  AS timestamp,
735                       reserveconstraints.biblioitemnumber AS biblioitemnumber,
736                       reserves.itemnumber                 AS itemnumber
737                  FROM reserves LEFT JOIN reserveconstraints
738                    ON reserves.biblionumber = reserveconstraints.biblionumber
739                 WHERE reserves.biblionumber = ?
740                   AND ( ( reserveconstraints.biblioitemnumber = ?
741                       AND reserves.borrowernumber = reserveconstraints.borrowernumber
742                       AND reserves.reservedate    =reserveconstraints.reservedate )
743                    OR reserves.constrainttype='a' )
744                   AND reserves.cancellationdate is NULL
745                   AND (reserves.found <> 'F' or reserves.found is NULL)");
746   $sth->execute($biblio, $bibitem);
747   my @results;
748   while (my $data=$sth->fetchrow_hashref){
749     push(@results,$data);
750   }
751   $sth->finish;
752   return(scalar(@results),@results);
753 }
754
755 # FIXME - A somewhat different version of this function appears in
756 # C4::Reserves. Pick one and stick with it.
757 # XXX - POD
758 sub CreateReserve {
759   my ($env,$branch,$borrnum,$biblionumber,$constraint,$bibitems,$priority,$notes,$title,$checkitem,$found)= @_;
760   my $fee;
761   if($library_name =~ /Horowhenua/){
762       $fee = CalcHLTReserveFee($env,$borrnum,$biblionumber,$constraint,$bibitems);
763   } else {
764       $fee = CalcReserveFee($env,$borrnum,$biblionumber,$constraint,$bibitems);
765   }
766   my $dbh = C4::Context->dbh;
767   my $const = lc substr($constraint,0,1);
768   my @datearr = localtime(time);
769   my $resdate =(1900+$datearr[5])."-".($datearr[4]+1)."-".$datearr[3];
770   my $waitingdate;
771 # If the reserv had the waiting status, we had the value of the resdate
772   if ($found eq 'W'){
773   $waitingdate = $resdate;
774   }
775   #eval {
776   # updates take place here
777   if ($fee > 0) {
778 #    print $fee;
779     my $nextacctno = &getnextacctno($env,$borrnum,$dbh);
780     my $usth = $dbh->prepare("insert into accountlines
781     (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding)
782                                                           values
783     (?,?,now(),?,?,'Res',?)");
784     $usth->execute($borrnum,$nextacctno,$fee,"Reserve Charge - $title",$fee);
785     $usth->finish;
786   }
787   #if ($const eq 'a'){
788     my $sth = $dbh->prepare("insert into reserves
789    (borrowernumber,biblionumber,reservedate,branchcode,constrainttype,priority,reservenotes,itemnumber,found,waitingdate)
790     values (?,?,?,?,?,?,?,?,?,?)");
791     $sth->execute($borrnum,$biblionumber,$resdate,$branch,$const,$priority,$notes,$checkitem,$found,$waitingdate);
792     $sth->finish;
793   #}
794   if (($const eq "o") || ($const eq "e")) {
795     my $numitems = @$bibitems;
796     my $i = 0;
797     while ($i < $numitems) {
798       my $biblioitem = @$bibitems[$i];
799       my $sth = $dbh->prepare("insert into
800       reserveconstraints
801       (borrowernumber,biblionumber,reservedate,biblioitemnumber)
802       values (?,?,?,?)");
803       $sth->execute($borrnum,$biblionumber,$resdate,$biblioitem);
804       $sth->finish;
805       $i++;
806     }
807   }
808 #  print $query;
809   return();
810 }
811
812 # FIXME - A functionally identical version of this function appears in
813 # C4::Reserves. Pick one and stick with it.
814 # XXX - Internal use only
815 # FIXME - opac-reserves.pl need to use it, temporarily put into @EXPORT
816 sub CalcReserveFee {
817   my ($env,$borrnum,$biblionumber,$constraint,$bibitems) = @_;
818   #check for issues;
819   my $dbh = C4::Context->dbh;
820   my $const = lc substr($constraint,0,1);
821   my $sth = $dbh->prepare("SELECT * FROM borrowers,categories
822                 WHERE (borrowernumber = ?)
823                   AND (borrowers.categorycode = categories.categorycode)");
824   $sth->execute($borrnum);
825   my $data = $sth->fetchrow_hashref;
826   $sth->finish();
827   my $fee = $data->{'reservefee'};
828   my $cntitems = @->$bibitems;
829   if ($fee > 0) {
830     # check for items on issue
831     # first find biblioitem records
832     my @biblioitems;
833     my $sth1 = $dbh->prepare("SELECT * FROM biblio,biblioitems
834                    WHERE (biblio.biblionumber = ?)
835                      AND (biblio.biblionumber = biblioitems.biblionumber)");
836     $sth1->execute($biblionumber);
837     while (my $data1=$sth1->fetchrow_hashref) {
838       if ($const eq "a") {
839         push @biblioitems,$data1;
840       } else {
841         my $found = 0;
842         my $x = 0;
843         while ($x < $cntitems) {
844           if (@$bibitems->{'biblioitemnumber'} == $data->{'biblioitemnumber'}) {
845             $found = 1;
846           }
847           $x++;
848         }
849         if ($const eq 'o') {
850           if ( $found == 1) {
851             push @biblioitems,$data1;
852           }
853         } else {
854           if ($found == 0) {
855             push @biblioitems,$data1;
856           }
857         }
858       }
859     }
860     $sth1->finish;
861     my $cntitemsfound = @biblioitems;
862     my $issues = 0;
863     my $x = 0;
864     my $allissued = 1;
865     while ($x < $cntitemsfound) {
866       my $bitdata = $biblioitems[$x];
867       my $sth2 = $dbh->prepare("SELECT * FROM items
868                      WHERE biblioitemnumber = ?");
869       $sth2->execute($bitdata->{'biblioitemnumber'});
870       while (my $itdata=$sth2->fetchrow_hashref) {
871         my $sth3 = $dbh->prepare("SELECT * FROM issues
872                        WHERE itemnumber = ?
873                          AND returndate IS NULL");
874         $sth3->execute($itdata->{'itemnumber'});
875         if (my $isdata=$sth3->fetchrow_hashref) {
876         } else {
877           $allissued = 0;
878         }
879       }
880       $x++;
881     }
882     if ($allissued == 0) {
883       my $rsth = $dbh->prepare("SELECT * FROM reserves WHERE biblionumber = ?");
884       $rsth->execute($biblionumber);
885       if (my $rdata = $rsth->fetchrow_hashref) {
886       } else {
887         $fee = 0;
888       }
889     }
890   }
891 #  print "fee $fee";
892   return $fee;
893 }
894
895 # The following are junior and young adult item types that should not incur a
896 # reserve charge.
897 #
898 # Juniors: BJC, BJCN, BJF, BJK, BJM, BJN, BJP, BJSF, BJSN, DJ, DJP, FJ, JVID,
899 #  VJ, VJP, PJ, TJ, TJP, VJ, VJP.
900 #
901 # Young adults: BYF, BYN, BYP, DY, DYP, PY, PYP, TY, TYP, VY, VYP.
902 #
903 # All other item types should incur a reserve charge.
904 sub CalcHLTReserveFee {
905     my ($env,$borrnum,$biblionumber,$constraint,$bibitems) = @_;
906     my $dbh = C4::Context->dbh;
907     my $sth = $dbh->prepare("SELECT * FROM borrowers,categories
908                   WHERE (borrowernumber = ?)
909                     AND (borrowers.categorycode = categories.categorycode)");
910     $sth->execute($borrnum);
911     my $data = $sth->fetchrow_hashref;
912     $sth->finish();
913     my $fee = $data->{'reservefee'};
914
915     my $matchno;
916     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/;
917     my $sth = $dbh->prepare("SELECT * FROM biblio,biblioitems
918                      WHERE (biblio.biblionumber = ?)
919                        AND (biblio.biblionumber = biblioitems.biblionumber)");
920     $sth->execute($biblionumber);
921     my $data=$sth->fetchrow_hashref;
922     my $itemtype = $data->{'itemtype'};
923     for (my $i = 0; $i < @nocharge; $i++) {
924         if ($itemtype eq $nocharge[$i]) {
925             $matchno++;
926             last;
927         }
928     }
929
930     if($matchno>0){
931         $fee = 0;
932     }
933   warn "BOB DEBUG: Fee is $fee";
934   return $fee;
935 }
936
937
938
939 # XXX - Internal use
940 sub getnextacctno {
941   my ($env,$bornumber,$dbh)=@_;
942   my $nextaccntno = 1;
943   my $sth = $dbh->prepare("select * from accountlines
944   where (borrowernumber = ?)
945   order by accountno desc");
946   $sth->execute($bornumber);
947   if (my $accdata=$sth->fetchrow_hashref){
948     $nextaccntno = $accdata->{'accountno'} + 1;
949   }
950   $sth->finish;
951   return($nextaccntno);
952 }
953
954 # XXX - POD
955 sub updatereserves{
956   #subroutine to update a reserve
957   my ($rank,$biblio,$borrower,$del,$branch)=@_;
958   my $dbh = C4::Context->dbh;
959   if ($del == 0){
960     my $sth = $dbh->prepare("Update reserves set priority=?,branchcode=? where
961     biblionumber=? and borrowernumber=?");
962     $sth->execute($rank,$branch,$biblio,$borrower);
963     $sth->finish();
964   } else {
965     my $sth=$dbh->prepare("Select * from reserves where biblionumber=? and
966     borrowernumber=?");
967     $sth->execute($biblio,$borrower);
968     my $data=$sth->fetchrow_hashref;
969     $sth->finish();
970     $sth=$dbh->prepare("Select * from reserves where biblionumber=? and
971     priority > ? and cancellationdate is NULL
972     order by priority") || die $dbh->errstr;
973     $sth->execute($biblio,$data->{'priority'}) || die $sth->errstr;
974     while (my $data=$sth->fetchrow_hashref){
975       $data->{'priority'}--;
976       my $sth3=$dbh->prepare("Update reserves set priority=?
977       where biblionumber=? and borrowernumber=?");
978       $sth3->execute($data->{'priority'},$data->{'biblionumber'},$data->{'borrowernumber'}) || die $sth3->errstr;
979       $sth3->finish();
980     }
981     $sth->finish();
982     $sth=$dbh->prepare("update reserves set cancellationdate=now() where biblionumber=?
983     and borrowernumber=?");
984     $sth->execute($biblio,$borrower);
985     $sth->finish;
986   }
987 }
988
989 # XXX - POD
990 sub UpdateReserve {
991     #subroutine to update a reserve
992     my ($rank,$biblio,$borrower,$branch)=@_;
993     return if $rank eq "W";
994     return if $rank eq "n";
995     my $dbh = C4::Context->dbh;
996     if ($rank eq "del") {
997         my $sth=$dbh->prepare("UPDATE reserves SET cancellationdate=now()
998                                    WHERE biblionumber   = ?
999                                      AND borrowernumber = ?
1000                                      AND cancellationdate is NULL
1001                                      AND (found <> 'F' or found is NULL)");
1002         $sth->execute($biblio, $borrower);
1003         $sth->finish;
1004     } else {
1005         my $sth=$dbh->prepare("UPDATE reserves SET priority = ? ,branchcode = ?, itemnumber = NULL, found = NULL
1006                                    WHERE biblionumber   = ?
1007                                      AND borrowernumber = ?
1008                                      AND cancellationdate is NULL
1009                                      AND (found <> 'F' or found is NULL)");
1010         $sth->execute($rank, $branch, $biblio, $borrower);
1011         $sth->finish;
1012     }
1013 }
1014
1015 # XXX - POD
1016 sub getreservetitle {
1017  my ($biblio,$bor,$date,$timestamp)=@_;
1018  my $dbh = C4::Context->dbh;
1019  my $sth=$dbh->prepare("Select * from reserveconstraints,biblioitems where
1020  reserveconstraints.biblioitemnumber=biblioitems.biblioitemnumber
1021  and reserveconstraints.biblionumber=? and reserveconstraints.borrowernumber
1022  = ? and reserveconstraints.reservedate=? and
1023  reserveconstraints.timestamp=?");
1024  $sth->execute($biblio,$bor,$date,$timestamp);
1025  my $data=$sth->fetchrow_hashref;
1026  $sth->finish;
1027  return($data);
1028 }