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