fix for bug 858 (no warning when issuing a reserved book)
[koha.git] / C4 / Circulation / Circ2.pm
1 # -*- tab-width: 8 -*-
2 # Please use 8-character tabs for this file (indents are every 4 characters)
3
4 package C4::Circulation::Circ2;
5
6 # $Id$
7
8 #package to deal with Returns
9 #written 3/11/99 by olwen@katipo.co.nz
10
11
12 # Copyright 2000-2002 Katipo Communications
13 #
14 # This file is part of Koha.
15 #
16 # Koha is free software; you can redistribute it and/or modify it under the
17 # terms of the GNU General Public License as published by the Free Software
18 # Foundation; either version 2 of the License, or (at your option) any later
19 # version.
20 #
21 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
22 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
23 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
24 #
25 # You should have received a copy of the GNU General Public License along with
26 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
27 # Suite 330, Boston, MA  02111-1307 USA
28
29 use strict;
30 # use warnings;
31 require Exporter;
32 use DBI;
33 use C4::Context;
34 use C4::Stats;
35 use C4::Reserves2;
36 use C4::Koha;
37 use C4::Accounts;
38 use Date::Manip;
39
40 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
41
42 # set the version for version checking
43 $VERSION = 0.01;
44
45 =head1 NAME
46
47 C4::Circulation::Circ2 - Koha circulation module
48
49 =head1 SYNOPSIS
50
51   use C4::Circulation::Circ2;
52
53 =head1 DESCRIPTION
54
55 The functions in this module deal with circulation, issues, and
56 returns, as well as general information about the library.
57 Also deals with stocktaking.
58
59 =head1 FUNCTIONS
60
61 =over 2
62
63 =cut
64
65 @ISA = qw(Exporter);
66 @EXPORT = qw(&getpatroninformation
67         &currentissues &getissues &getiteminformation &renewstatus &renewbook
68         &canbookbeissued &issuebook &returnbook &find_reserves &transferbook &decode
69         &calc_charges &listitemsforinventory &itemseen &fixdate);
70
71 # &getbranches &getprinters &getbranch &getprinter => moved to C4::Koha.pm
72
73 =head2 itemseen
74
75 &itemseen($itemnum)
76 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking
77 C<$itemnum> is the item number
78
79 =cut
80
81 sub itemseen {
82         my ($itemnum) = @_;
83         my $dbh = C4::Context->dbh;
84         my $sth = $dbh->prepare("update items set itemlost=0, datelastseen  = now() where items.itemnumber = ?");
85         $sth->execute($itemnum);
86         return;
87 }
88
89 sub listitemsforinventory {
90         my ($minlocation,$maxlocation,$datelastseen,$offset,$size) = @_;
91         my $dbh = C4::Context->dbh;
92         my $sth = $dbh->prepare("select itemnumber,barcode,itemcallnumber,title,author from items,biblio where items.biblionumber=biblio.biblionumber and itemcallnumber>= ? and itemcallnumber <=? and (datelastseen< ? or datelastseen is null) order by itemcallnumber,title");
93         $sth->execute($minlocation,$maxlocation,$datelastseen);
94         my @results;
95         while (my $row = $sth->fetchrow_hashref) {
96                 $offset-- if ($offset);
97                 if ((!$offset) && $size) {
98                         push @results,$row;
99                         $size--;
100                 }
101         }
102         return \@results;
103 }
104
105 =head2 getpatroninformation
106
107   ($borrower, $flags) = &getpatroninformation($env, $borrowernumber, $cardnumber);
108
109 Looks up a patron and returns information about him or her. If
110 C<$borrowernumber> is true (nonzero), C<&getpatroninformation> looks
111 up the borrower by number; otherwise, it looks up the borrower by card
112 number.
113
114 C<$env> is effectively ignored, but should be a reference-to-hash.
115
116 C<$borrower> is a reference-to-hash whose keys are the fields of the
117 borrowers table in the Koha database. In addition,
118 C<$borrower-E<gt>{flags}> is a hash giving more detailed information
119 about the patron. Its keys act as flags :
120
121         if $borrower->{flags}->{LOST} {
122                 # Patron's card was reported lost
123         }
124
125 Each flag has a C<message> key, giving a human-readable explanation of
126 the flag. If the state of a flag means that the patron should not be
127 allowed to borrow any more books, then it will have a C<noissues> key
128 with a true value.
129
130 The possible flags are:
131
132 =head3 CHARGES
133
134 =over 4
135
136 Shows the patron's credit or debt, if any.
137
138 =back
139
140 =head3 GNA
141
142 =over 4
143
144 (Gone, no address.) Set if the patron has left without giving a
145 forwarding address.
146
147 =back
148
149 =head3 LOST
150
151 =over 4
152
153 Set if the patron's card has been reported as lost.
154
155 =back
156
157 =head3 DBARRED
158
159 =over 4
160
161 Set if the patron has been debarred.
162
163 =back
164
165 =head3 NOTES
166
167 =over 4
168
169 Any additional notes about the patron.
170
171 =back
172
173 =head3 ODUES
174
175 =over 4
176
177 Set if the patron has overdue items. This flag has several keys:
178
179 C<$flags-E<gt>{ODUES}{itemlist}> is a reference-to-array listing the
180 overdue items. Its elements are references-to-hash, each describing an
181 overdue item. The keys are selected fields from the issues, biblio,
182 biblioitems, and items tables of the Koha database.
183
184 C<$flags-E<gt>{ODUES}{itemlist}> is a string giving a text listing of
185 the overdue items, one per line.
186
187 =back
188
189 =head3 WAITING
190
191 =over 4
192
193 Set if any items that the patron has reserved are available.
194
195 C<$flags-E<gt>{WAITING}{itemlist}> is a reference-to-array listing the
196 available items. Each element is a reference-to-hash whose keys are
197 fields from the reserves table of the Koha database.
198
199 =back
200
201 =back
202
203 =cut
204
205
206 sub getpatroninformation {
207 # returns
208         my ($env, $borrowernumber,$cardnumber) = @_;
209         my $dbh = C4::Context->dbh;
210         my $query;
211         my $sth;
212         if ($borrowernumber) {
213                 $sth = $dbh->prepare("select * from borrowers where borrowernumber=?");
214                 $sth->execute($borrowernumber);
215         } elsif ($cardnumber) {
216                 $sth = $dbh->prepare("select * from borrowers where cardnumber=?");
217                 $sth->execute($cardnumber);
218         } else {
219                 $env->{'apierror'} = "invalid borrower information passed to getpatroninformation subroutine";
220                 return();
221         }
222         my $borrower = $sth->fetchrow_hashref;
223         my $amount = checkaccount($env, $borrowernumber, $dbh);
224         $borrower->{'amountoutstanding'} = $amount;
225         my $flags = patronflags($env, $borrower, $dbh);
226         my $accessflagshash;
227  
228         $sth=$dbh->prepare("select bit,flag from userflags");
229         $sth->execute;
230         while (my ($bit, $flag) = $sth->fetchrow) {
231                 if ($borrower->{'flags'} & 2**$bit) {
232                 $accessflagshash->{$flag}=1;
233                 }
234         }
235         $sth->finish;
236         $borrower->{'flags'}=$flags;
237         $borrower->{'authflags'} = $accessflagshash;
238         return ($borrower); #, $flags, $accessflagshash);
239 }
240
241 =head2 decode
242
243 =over 4
244
245 =head3 $str = &decode($chunk);
246
247 =over 4
248
249 Decodes a segment of a string emitted by a CueCat barcode scanner and
250 returns it.
251
252 =back
253
254 =back
255
256 =cut
257
258 # FIXME - At least, I'm pretty sure this is for decoding CueCat stuff.
259 sub decode {
260         my ($encoded) = @_;
261         my $seq = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-';
262         my @s = map { index($seq,$_); } split(//,$encoded);
263         my $l = ($#s+1) % 4;
264         if ($l)
265         {
266                 if ($l == 1)
267                 {
268                         print "Error!";
269                         return;
270                 }
271                 $l = 4-$l;
272                 $#s += $l;
273         }
274         my $r = '';
275         while ($#s >= 0)
276         {
277                 my $n = (($s[0] << 6 | $s[1]) << 6 | $s[2]) << 6 | $s[3];
278                 $r .=chr(($n >> 16) ^ 67) .
279                 chr(($n >> 8 & 255) ^ 67) .
280                 chr(($n & 255) ^ 67);
281                 @s = @s[4..$#s];
282         }
283         $r = substr($r,0,length($r)-$l);
284         return $r;
285 }
286
287 =head2 getiteminformation
288
289 =over 4
290
291 $item = &getiteminformation($env, $itemnumber, $barcode);
292
293 Looks up information about an item, given either its item number or
294 its barcode. If C<$itemnumber> is a nonzero value, it is used;
295 otherwise, C<$barcode> is used.
296
297 C<$env> is effectively ignored, but should be a reference-to-hash.
298
299 C<$item> is a reference-to-hash whose keys are fields from the biblio,
300 items, and biblioitems tables of the Koha database. It may also
301 contain the following keys:
302
303 =head3 date_due
304
305 =over 4
306
307 The due date on this item, if it has been borrowed and not returned
308 yet. The date is in YYYY-MM-DD format.
309
310 =back
311
312 =head3 notforloan
313
314 =over 4
315
316 True if the item may not be borrowed.
317
318 =back
319
320 =back
321
322 =cut
323
324
325 sub getiteminformation {
326 # returns a hash of item information given either the itemnumber or the barcode
327         my ($env, $itemnumber, $barcode) = @_;
328         my $dbh = C4::Context->dbh;
329         my $sth;
330         if ($itemnumber) {
331                 $sth=$dbh->prepare("select * from biblio,items,biblioitems where items.itemnumber=? and biblio.biblionumber=items.biblionumber and biblioitems.biblioitemnumber = items.biblioitemnumber");
332                 $sth->execute($itemnumber);
333         } elsif ($barcode) {
334                 $sth=$dbh->prepare("select * from biblio,items,biblioitems where items.barcode=? and biblio.biblionumber=items.biblionumber and biblioitems.biblioitemnumber = items.biblioitemnumber");
335                 $sth->execute($barcode);
336         } else {
337                 $env->{'apierror'}="getiteminformation() subroutine must be called with either an itemnumber or a barcode";
338                 # Error condition.
339                 return();
340         }
341         my $iteminformation=$sth->fetchrow_hashref;
342         $sth->finish;
343         if ($iteminformation) {
344                 $sth=$dbh->prepare("select date_due from issues where itemnumber=? and isnull(returndate)");
345                 $sth->execute($iteminformation->{'itemnumber'});
346                 my ($date_due) = $sth->fetchrow;
347                 $iteminformation->{'date_due'}=$date_due;
348                 $sth->finish;
349                 ($iteminformation->{'dewey'} == 0) && ($iteminformation->{'dewey'}='');
350                 $sth=$dbh->prepare("select * from itemtypes where itemtype=?");
351                 $sth->execute($iteminformation->{'itemtype'});
352                 my $itemtype=$sth->fetchrow_hashref;
353                 # if specific item notforloan, don't use itemtype notforloan field.
354                 # otherwise, use itemtype notforloan value to see if item can be issued.
355                 $iteminformation->{'notforloan'}=$itemtype->{'notforloan'} unless $iteminformation->{'notforloan'};
356                 $sth->finish;
357         }
358         return($iteminformation);
359 }
360
361 =head2 transferbook
362
363 =over 4
364
365 ($dotransfer, $messages, $iteminformation) = &transferbook($newbranch, $barcode, $ignore_reserves);
366
367 Transfers an item to a new branch. If the item is currently on loan, it is automatically returned before the actual transfer.
368
369 C<$newbranch> is the code for the branch to which the item should be transferred.
370
371 C<$barcode> is the barcode of the item to be transferred.
372
373 If C<$ignore_reserves> is true, C<&transferbook> ignores reserves.
374 Otherwise, if an item is reserved, the transfer fails.
375
376 Returns three values:
377
378 =head3 $dotransfer 
379
380 is true if the transfer was successful.
381
382 =head3 $messages
383  
384 is a reference-to-hash which may have any of the following keys:
385
386 =over 4
387
388 C<BadBarcode>
389
390 There is no item in the catalog with the given barcode. The value is C<$barcode>.
391
392 C<IsPermanent>
393
394 The item's home branch is permanent. This doesn't prevent the item from being transferred, though. The value is the code of the item's home branch.
395
396 C<DestinationEqualsHolding>
397
398 The item is already at the branch to which it is being transferred. The transfer is nonetheless considered to have failed. The value should be ignored.
399
400 C<WasReturned>
401
402 The item was on loan, and C<&transferbook> automatically returned it before transferring it. The value is the borrower number of the patron who had the item.
403
404 C<ResFound>
405
406 The item was reserved. The value is a reference-to-hash whose keys are fields from the reserves table of the Koha database, and C<biblioitemnumber>. It also has the key C<ResFound>, whose value is either C<Waiting> or C<Reserved>.
407
408 C<WasTransferred>
409
410 The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
411
412 =back
413
414 =back
415
416 =back
417
418 =cut
419
420 #'
421 # FIXME - This function tries to do too much, and its API is clumsy.
422 # If it didn't also return books, it could be used to change the home
423 # branch of a book while the book is on loan.
424 #
425 # Is there any point in returning the item information? The caller can
426 # look that up elsewhere if ve cares.
427 #
428 # This leaves the ($dotransfer, $messages) tuple. This seems clumsy.
429 # If the transfer succeeds, that's all the caller should need to know.
430 # Thus, this function could simply return 1 or 0 to indicate success
431 # or failure, and set $C4::Circulation::Circ2::errmsg in case of
432 # failure. Or this function could return undef if successful, and an
433 # error message in case of failure (this would feel more like C than
434 # Perl, though).
435 sub transferbook {
436 # transfer book code....
437         my ($tbr, $barcode, $ignoreRs) = @_;
438         my $messages;
439         my %env;
440         my $dotransfer = 1;
441         my $branches = getbranches();
442         my $iteminformation = getiteminformation(\%env, 0, $barcode);
443         # bad barcode..
444         if (not $iteminformation) {
445                 $messages->{'BadBarcode'} = $barcode;
446                 $dotransfer = 0;
447         }
448         # get branches of book...
449         my $hbr = $iteminformation->{'homebranch'};
450         my $fbr = $iteminformation->{'holdingbranch'};
451         # if is permanent...
452         if ($branches->{$hbr}->{'PE'}) {
453                 $messages->{'IsPermanent'} = $hbr;
454         }
455         # can't transfer book if is already there....
456         # FIXME - Why not? Shouldn't it trivially succeed?
457         if ($fbr eq $tbr) {
458                 $messages->{'DestinationEqualsHolding'} = 1;
459                 $dotransfer = 0;
460         }
461         # check if it is still issued to someone, return it...
462         my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
463         if ($currentborrower) {
464                 returnbook($barcode, $fbr);
465                 $messages->{'WasReturned'} = $currentborrower;
466         }
467         # find reserves.....
468         # FIXME - Don't call &CheckReserves unless $ignoreRs is true.
469         # That'll save a database query.
470         my ($resfound, $resrec) = CheckReserves($iteminformation->{'itemnumber'});
471         if ($resfound and not $ignoreRs) {
472                 $resrec->{'ResFound'} = $resfound;
473                 $messages->{'ResFound'} = $resrec;
474                 $dotransfer = 0;
475         }
476         #actually do the transfer....
477         if ($dotransfer) {
478                 dotransfer($iteminformation->{'itemnumber'}, $fbr, $tbr);
479                 $messages->{'WasTransfered'} = 1;
480         }
481         return ($dotransfer, $messages, $iteminformation);
482 }
483
484 # Not exported
485 # FIXME - This is only used in &transferbook. Why bother making it a
486 # separate function?
487 sub dotransfer {
488         my ($itm, $fbr, $tbr) = @_;
489         my $dbh = C4::Context->dbh;
490         $itm = $dbh->quote($itm);
491         $fbr = $dbh->quote($fbr);
492         $tbr = $dbh->quote($tbr);
493         #new entry in branchtransfers....
494         $dbh->do("INSERT INTO   branchtransfers (itemnumber, frombranch, datearrived, tobranch)
495                                         VALUES ($itm, $fbr, now(), $tbr)");
496         #update holdingbranch in items .....
497         $dbh->do("UPDATE items set holdingbranch = $tbr WHERE   items.itemnumber = $itm");
498         &itemseen($itm);
499         return;
500 }
501
502 =head2 canbookbeissued
503
504 Check if a book can be issued.
505
506 my ($issuingimpossible,$needsconfirmation) = canbookbeissued($env,$borrower,$barcode,$year,$month,$day);
507
508 =over 4
509
510 C<$env> Environment variable. Should be empty usually, but used by other subs. Next code cleaning could drop it.
511
512 C<$borrower> hash with borrower informations (from getpatroninformation)
513
514 C<$barcode> is the bar code of the book being issued.
515
516 C<$year> C<$month> C<$day> contains the date of the return (in case it's forced by "stickyduedate".
517
518 =back
519
520 Returns :
521
522 =over 4
523
524 C<$issuingimpossible> a reference to a hash. It contains reasons why issuing is impossible.
525 Possible values are :
526
527 =head3 INVALID_DATE 
528
529 sticky due date is invalid
530
531 =head3 GNA
532
533 borrower gone with no address
534
535 =head3 CARD_LOST
536  
537 borrower declared it's card lost
538
539 =head3 DEBARRED
540
541 borrower debarred
542
543 =head3 UNKNOWN_BARCODE
544
545 barcode unknown
546
547 =head3 NOT_FOR_LOAN
548
549 item is not for loan
550
551 =head3 WTHDRAWN
552
553 item withdrawn.
554
555 =head3 RESTRICTED
556
557 item is restricted (set by ??)
558
559 =back
560
561 C<$issuingimpossible> a reference to a hash. It contains reasons why issuing is impossible.
562 Possible values are :
563
564 =head3 DEBT
565
566 borrower has debts.
567
568 =head3 RENEW_ISSUE
569
570 renewing, not issuing
571
572 =head3 ISSUED_TO_ANOTHER
573
574 issued to someone else.
575
576 =head3 RESERVED
577
578 reserved for someone else.
579
580 =head3 INVALID_DATE
581
582 sticky due date is invalid
583
584 =head3 TOO_MANY
585
586 if the borrower borrows to much things
587
588 =cut
589
590 # check if a book can be issued.
591 # returns an array with errors if any
592
593 sub TooMany ($$){
594         my $borrower = shift;
595         my $iteminformation = shift;
596         my $cat_borrower = $borrower->{'categorycode'};
597         my $branch_borrower = $borrower->{'branchcode'};
598         my $dbh = C4::Context->dbh;
599         
600
601         my $sth = $dbh->prepare('select itemtype from biblioitems where biblionumber = ?');
602         $sth->execute($iteminformation->{'biblionumber'});
603         my $type = $sth->fetchrow;
604         $sth = $dbh->prepare('select * from issuingrules where categorycode = ? and itemtype = ? and branchcode = ?');
605         my $sth2 = $dbh->prepare("select COUNT(*) from issues i, biblioitems s where i.borrowernumber = ? and i.returndate is null and i.itemnumber = s.biblioitemnumber and s.itemtype like ?");
606         my $sth3 = $dbh->prepare('select COUNT(*) from issues where borrowernumber = ? and returndate is null');
607         my $alreadyissued;
608         # check the 3 parameters
609         $sth->execute($cat_borrower, $type, $branch_borrower);
610         my $result = $sth->fetchrow_hashref;
611 #       warn "==>".$result->{maxissueqty};
612         if (defined($result)) {
613                 $sth2->execute($borrower->{'borrowernumber'}, "%$type%");
614                 my $alreadyissued = $sth2->fetchrow;
615                 return ("a $alreadyissued / ".($result->{maxissueqty}+0)) if ($result->{'maxissueqty'} <= $alreadyissued);
616         }
617         # check for branch=*
618         $sth->execute($cat_borrower, $type, "");
619         my $result = $sth->fetchrow_hashref;
620         if (defined($result)) {
621                 $sth2->execute($borrower->{'borrowernumber'}, "%$type%");
622                 my $alreadyissued = $sth2->fetchrow;
623                 return ("b $alreadyissued / ".($result->{maxissueqty}+0)) if ($result->{'maxissueqty'} <= $alreadyissued);
624         }
625         # check for itemtype=*
626         $sth->execute($cat_borrower, "*", $branch_borrower);
627         my $result = $sth->fetchrow_hashref;
628         if (defined($result)) {
629                 $sth3->execute($borrower->{'borrowernumber'});
630                 my $alreadyissued = $sth2->fetchrow;
631                 return ("c $alreadyissued / ".($result->{maxissueqty}+0)) if ($result->{'maxissueqty'} <= $alreadyissued);
632         }
633         #check for borrowertype=*
634         $sth->execute("*", $type, $branch_borrower);
635         my $result = $sth->fetchrow_hashref;
636         if (defined($result)) {
637                 $sth2->execute($borrower->{'borrowernumber'}, "%$type%");
638                 my $alreadyissued = $sth2->fetchrow;
639                 return ("d $alreadyissued / ".($result->{maxissueqty}+0)) if ($result->{'maxissueqty'} <= $alreadyissued);
640         }
641
642         $sth->execute("*", "*", $branch_borrower);
643         my $result = $sth->fetchrow_hashref;
644         if (defined($result)) {
645                 $sth3->execute($borrower->{'borrowernumber'});
646                 my $alreadyissued = $sth2->fetchrow;
647                 return ("e $alreadyissued / ".($result->{maxissueqty}+0)) if ($result->{'maxissueqty'} <= $alreadyissued);
648         }
649
650         $sth->execute("*", $type, "");
651         my $result = $sth->fetchrow_hashref;
652         if (defined($result) && $result->{maxissueqty}>=0) {
653                 $sth2->execute($borrower->{'borrowernumber'}, "%$type%");
654                 my $alreadyissued = $sth2->fetchrow;
655                 return ("f $alreadyissued / ".($result->{maxissueqty}+0)) if ($result->{'maxissueqty'} <= $alreadyissued);
656         }
657
658         $sth->execute($cat_borrower, "*", "");
659         my $result = $sth->fetchrow_hashref;
660         if (defined($result)) {
661                 $sth2->execute($borrower->{'borrowernumber'}, "%$type%");
662                 my $alreadyissued = $sth2->fetchrow;
663                 return ("g $alreadyissued / ".($result->{maxissueqty}+0)) if ($result->{'maxissueqty'} <= $alreadyissued);
664         }
665
666         $sth->execute("*", "*", "");
667         my $result = $sth->fetchrow_hashref;
668         if (defined($result)) {
669                 $sth3->execute($borrower->{'borrowernumber'});
670                 my $alreadyissued = $sth2->fetchrow;
671                 return ("h $alreadyissued / ".($result->{maxissueqty}+0)) if ($result->{'maxissueqty'} <= $alreadyissued);
672         }
673         return;
674 }
675
676
677 sub canbookbeissued {
678         my ($env,$borrower,$barcode,$year,$month,$day) = @_;
679         my %needsconfirmation; # filled with problems that needs confirmations
680         my %issuingimpossible; # filled with problems that causes the issue to be IMPOSSIBLE
681         my $iteminformation = getiteminformation($env, 0, $barcode);
682         my $dbh = C4::Context->dbh;
683 #
684 # DUE DATE is OK ?
685 #
686         my ($duedate, $invalidduedate) = fixdate($year, $month, $day);
687         $issuingimpossible{INVALID_DATE} = 1 if ($invalidduedate);
688
689 #
690 # BORROWER STATUS
691 #
692         if ($borrower->{flags}->{GNA}) {
693                 $issuingimpossible{GNA} = 1;
694         }
695         if ($borrower->{flags}->{'LOST'}) {
696                 $issuingimpossible{CARD_LOST} = 1;
697         }
698         if ($borrower->{flags}->{'DBARRED'}) {
699                 $issuingimpossible{DEBARRED} = 1;
700         }
701 #
702 # BORROWER STATUS
703 #
704
705 # DEBTS
706         my $amount = checkaccount($env,$borrower->{'borrowernumber'}, $dbh,$duedate);
707         if ($amount >0) {
708                 $needsconfirmation{DEBT} = $amount;
709         }
710
711
712 #
713 # JB34 CHECKS IF BORROWERS DONT HAVE ISSUE TOO MANY BOOKS
714 #
715         my $toomany = TooMany($borrower, $iteminformation);
716         $needsconfirmation{TOO_MANY} =  $toomany if $toomany;
717
718 #
719 # ITEM CHECKING
720 #
721         unless ($iteminformation->{barcode}) {
722                 $issuingimpossible{UNKNOWN_BARCODE} = 1;
723         }
724         if ($iteminformation->{'notforloan'} == 1) {
725                 $issuingimpossible{NOT_FOR_LOAN} = 1;
726         }
727         if ($iteminformation->{'itemtype'} eq 'REF') {
728                 $issuingimpossible{NOT_FOR_LOAN} = 1;
729         }
730         if ($iteminformation->{'wthdrawn'} == 1) {
731                 $issuingimpossible{WTHDRAWN} = 1;
732         }
733         if ($iteminformation->{'restricted'} == 1) {
734                 $issuingimpossible{RESTRICTED} = 1;
735         }
736
737
738
739 #
740 # CHECK IF BOOK ALREADY ISSUED TO THIS BORROWER
741 #
742         my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
743         if ($currentborrower eq $borrower->{'borrowernumber'}) {
744 # Already issued to current borrower. Ask whether the loan should
745 # be renewed.
746                 my ($renewstatus) = renewstatus($env,$dbh,$borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
747                 if ($renewstatus == 0) { # no more renewals allowed
748                         $issuingimpossible{NO_MORE_RENEWALS} = 1;
749                 } else {
750                         $needsconfirmation{RENEW_ISSUE} = 1;
751                 }
752         } elsif ($currentborrower) {
753 # issued to someone else
754                 my $currborinfo = getpatroninformation(0,$currentborrower);
755 #               warn "=>.$currborinfo->{'firstname'} $currborinfo->{'surname'} ($currborinfo->{'cardnumber'})";
756                 $needsconfirmation{ISSUED_TO_ANOTHER} = "$currborinfo->{'reservedate'} : $currborinfo->{'firstname'} $currborinfo->{'surname'} ($currborinfo->{'cardnumber'})";
757         }
758 # See if the item is on reserve.
759         my ($restype, $res) = CheckReserves($iteminformation->{'itemnumber'});
760         if ($restype) {
761                 my $resbor = $res->{'borrowernumber'};
762                 if ($resbor ne $borrower->{'borrowernumber'} && $restype eq "Waiting") {
763                         # The item is on reserve and waiting, but has been
764                         # reserved by some other patron.
765                         my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
766                         my $branches = getbranches();
767                         my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
768                         $needsconfirmation{RESERVE_WAITING} = "$resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}, $branchname)";
769                         CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
770                 } elsif ($restype eq "Reserved") {
771                         # The item is on reserve for someone else.
772                         my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
773                         my $branches = getbranches();
774                         my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
775                         $needsconfirmation{RESERVED} = "$res->{'reservedate'} : $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})";
776                 }
777         }
778         return(\%issuingimpossible,\%needsconfirmation);
779 }
780
781 =head2 issuebook
782
783 Issue a book. Does no check, they are done in canbookbeissued. If we reach this sub, it means the user confirmed if needed.
784
785 &issuebook($env,$borrower,$barcode,$date)
786
787 =over 4
788
789 C<$env> Environment variable. Should be empty usually, but used by other subs. Next code cleaning could drop it.
790
791 C<$borrower> hash with borrower informations (from getpatroninformation)
792
793 C<$barcode> is the bar code of the book being issued.
794
795 C<$date> contains the max date of return. calculated if empty.
796
797 =cut
798
799 #
800 # issuing book. We already have checked it can be issued, so, just issue it !
801 #
802 sub issuebook {
803         my ($env,$borrower,$barcode,$date) = @_;
804         my $dbh = C4::Context->dbh;
805 #       my ($borrower, $flags) = &getpatroninformation($env, $borrowernumber, 0);
806         my $iteminformation = getiteminformation($env, 0, $barcode);
807 #               warn "B : ".$borrower->{borrowernumber}." / I : ".$iteminformation->{'itemnumber'};
808 #
809 # check if we just renew the issue.
810 #
811         my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
812         if ($currentborrower eq $borrower->{'borrowernumber'}) {
813                 my ($charge,$itemtype) = calc_charges($env, $iteminformation->{'itemnumber'}, $borrower->{'borrowernumber'});
814                 if ($charge > 0) {
815                         createcharge($env, $dbh, $iteminformation->{'itemnumber'}, $borrower->{'borrowernumber'}, $charge);
816                         $iteminformation->{'charge'} = $charge;
817                 }
818                 &UpdateStats($env,$env->{'branchcode'},'renew',$charge,'',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'},$borrower->{'borrowernumber'});
819                 renewbook($env,$dbh, $borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
820         } else {
821 #
822 # NOT a renewal
823 #
824                 if ($currentborrower ne '') {
825                         # This book is currently on loan, but not to the person
826                         # who wants to borrow it now. mark it returned before issuing to the new borrower
827                         returnbook($iteminformation->{'barcode'}, $env->{'branchcode'});
828                 }
829                 # See if the item is on reserve.
830                 my ($restype, $res) = CheckReserves($iteminformation->{'itemnumber'});
831                 if ($restype) {
832                         my $resbor = $res->{'borrowernumber'};
833                         if ($resbor eq $borrower->{'borrowernumber'}) {
834                                 # The item is on reserve to the current patron
835                                 FillReserve($res);
836                         } elsif ($restype eq "Waiting") {
837                                 # The item is on reserve and waiting, but has been
838                                 # reserved by some other patron.
839                                 my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
840                                 my $branches = getbranches();
841                                 my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
842                                 CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
843                         } elsif ($restype eq "Reserved") {
844                                 # The item is on reserve for someone else.
845                                 my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
846                                 my $branches = getbranches();
847                                 my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
848                                 my $tobrcd = ReserveWaiting($res->{'itemnumber'}, $res->{'borrowernumber'});
849                                 transferbook($tobrcd,$barcode, 1);
850                         }
851                 }
852                 # Record in the database the fact that the book was issued.
853                 my $sth=$dbh->prepare("insert into issues (borrowernumber, itemnumber, date_due, branchcode) values (?,?,?,?)");
854                 my $loanlength = getLoanLength($borrower->{'categorycode'},$iteminformation->{'itemtype'},$borrower->{'branchcode'});
855                 my $datedue=time+($loanlength)*86400;
856                 my @datearr = localtime($datedue);
857                 my $dateduef = (1900+$datearr[5])."-".($datearr[4]+1)."-".$datearr[3];
858                 if ($date) {
859                         $dateduef=$date;
860                 }
861                 $sth->execute($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'}, $dateduef, $env->{'branchcode'});
862                 $sth->finish;
863                 $iteminformation->{'issues'}++;
864                 $sth=$dbh->prepare("update items set issues=? where itemnumber=?");
865                 $sth->execute($iteminformation->{'issues'},$iteminformation->{'itemnumber'});
866                 $sth->finish;
867                 &itemseen($iteminformation->{'itemnumber'});
868                 # If it costs to borrow this book, charge it to the patron's account.
869                 my ($charge,$itemtype)=calc_charges($env, $iteminformation->{'itemnumber'}, $borrower->{'borrowernumber'});
870                 if ($charge > 0) {
871                         createcharge($env, $dbh, $iteminformation->{'itemnumber'}, $borrower->{'borrowernumber'}, $charge);
872                         $iteminformation->{'charge'}=$charge;
873                 }
874                 # Record the fact that this book was issued.
875                 &UpdateStats($env,$env->{'branchcode'},'issue',$charge,'',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'},$borrower->{'borrowernumber'});
876         }
877 }
878
879 =head2 getLoanLength
880
881 Get loan length for an itemtype, a borrower type and a branch
882
883 my $loanlength = &getLoanLength($borrowertype,$itemtype,branchcode)
884
885 =cut
886
887 sub getLoanLength {
888         my ($borrowertype,$itemtype,$branchcode) = @_;
889         my $dbh = C4::Context->dbh;
890         my $sth = $dbh->prepare("select issuelength from issuingrules where categorycode=? and itemtype=? and branchcode=?");
891         # try to find issuelength & return the 1st available.
892         # check with borrowertype, itemtype and branchcode, then without one of those parameters
893         $sth->execute($borrowertype,$itemtype,$branchcode);
894         my $loanlength = $sth->fetchrow_hashref;
895         return $loanlength->{issuelength} if defined($loanlength);
896         
897         $sth->execute($borrowertype,$itemtype,"");
898         my $loanlength = $sth->fetchrow_hashref;
899         return $loanlength->{issuelength} if defined($loanlength);
900         
901         $sth->execute($borrowertype,"*",$branchcode);
902         my $loanlength = $sth->fetchrow_hashref;
903         return $loanlength->{issuelength} if defined($loanlength);
904
905         $sth->execute("*",$itemtype,$branchcode);
906         my $loanlength = $sth->fetchrow_hashref;
907         return $loanlength->{issuelength} if defined($loanlength);
908
909         $sth->execute($borrowertype,"*","");
910         my $loanlength = $sth->fetchrow_hashref;
911         return $loanlength->{issuelength} if defined($loanlength);
912
913         $sth->execute("*","*",$branchcode);
914         my $loanlength = $sth->fetchrow_hashref;
915         return $loanlength->{issuelength} if defined($loanlength);
916
917         $sth->execute("*",$itemtype,"");
918         my $loanlength = $sth->fetchrow_hashref;
919         return $loanlength->{issuelength} if defined($loanlength);
920
921         $sth->execute("*","*","");
922         my $loanlength = $sth->fetchrow_hashref;
923         return $loanlength->{issuelength} if defined($loanlength);
924
925         # if no rule is set => 21 days (hardcoded)
926         return 21;
927 }
928 =head2 returnbook
929
930   ($doreturn, $messages, $iteminformation, $borrower) =
931           &returnbook($barcode, $branch);
932
933 Returns a book.
934
935 C<$barcode> is the bar code of the book being returned. C<$branch> is
936 the code of the branch where the book is being returned.
937
938 C<&returnbook> returns a list of four items:
939
940 C<$doreturn> is true iff the return succeeded.
941
942 C<$messages> is a reference-to-hash giving the reason for failure:
943
944 =over 4
945
946 =item C<BadBarcode>
947
948 No item with this barcode exists. The value is C<$barcode>.
949
950 =item C<NotIssued>
951
952 The book is not currently on loan. The value is C<$barcode>.
953
954 =item C<IsPermanent>
955
956 The book's home branch is a permanent collection. If you have borrowed
957 this book, you are not allowed to return it. The value is the code for
958 the book's home branch.
959
960 =item C<wthdrawn>
961
962 This book has been withdrawn/cancelled. The value should be ignored.
963
964 =item C<ResFound>
965
966 The item was reserved. The value is a reference-to-hash whose keys are
967 fields from the reserves table of the Koha database, and
968 C<biblioitemnumber>. It also has the key C<ResFound>, whose value is
969 either C<Waiting>, C<Reserved>, or 0.
970
971 =back
972
973 C<$borrower> is a reference-to-hash, giving information about the
974 patron who last borrowed the book.
975
976 =cut
977
978 # FIXME - This API is bogus. There's no need to return $borrower and
979 # $iteminformation; the caller can ask about those separately, if it
980 # cares (it'd be inefficient to make two database calls instead of
981 # one, but &getpatroninformation and &getiteminformation can be
982 # memoized if this is an issue).
983 #
984 # The ($doreturn, $messages) tuple is redundant: if the return
985 # succeeded, that's all the caller needs to know. So &returnbook can
986 # return 1 and 0 on success and failure, and set
987 # $C4::Circulation::Circ2::errmsg to indicate the error. Or it can
988 # return undef for success, and an error message on error (though this
989 # is more C-ish than Perl-ish).
990
991 sub returnbook {
992         my ($barcode, $branch) = @_;
993         my %env;
994         my $messages;
995         my $dbh = C4::Context->dbh;
996         my $doreturn = 1;
997         die '$branch not defined' unless defined $branch; # just in case (bug 170)
998         # get information on item
999         my ($iteminformation) = getiteminformation(\%env, 0, $barcode);
1000         if (not $iteminformation) {
1001                 $messages->{'BadBarcode'} = $barcode;
1002                 $doreturn = 0;
1003         }
1004         # find the borrower
1005         my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
1006         if ((not $currentborrower) && $doreturn) {
1007                 $messages->{'NotIssued'} = $barcode;
1008                 $doreturn = 0;
1009         }
1010         # check if the book is in a permanent collection....
1011         my $hbr = $iteminformation->{'homebranch'};
1012         my $branches = getbranches();
1013         if ($branches->{$hbr}->{'PE'}) {
1014                 $messages->{'IsPermanent'} = $hbr;
1015         }
1016         # check that the book has been cancelled
1017         if ($iteminformation->{'wthdrawn'}) {
1018                 $messages->{'wthdrawn'} = 1;
1019                 $doreturn = 0;
1020         }
1021         # update issues, thereby returning book (should push this out into another subroutine
1022         my ($borrower) = getpatroninformation(\%env, $currentborrower, 0);
1023         if ($doreturn) {
1024                 my $sth = $dbh->prepare("update issues set returndate = now() where (borrowernumber = ?) and (itemnumber = ?) and (returndate is null)");
1025                 $sth->execute($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
1026                 $messages->{'WasReturned'} = 1; # FIXME is the "= 1" right?
1027         }
1028         ($borrower) = getpatroninformation(\%env, $currentborrower, 0);
1029         # transfer book to the current branch
1030         my ($transfered, $mess, $item) = transferbook($branch, $barcode, 1);
1031         if ($transfered) {
1032                 $messages->{'WasTransfered'} = 1; # FIXME is the "= 1" right?
1033         }
1034         # fix up the accounts.....
1035         if ($iteminformation->{'itemlost'}) {
1036                 fixaccountforlostandreturned($iteminformation, $borrower);
1037                 $messages->{'WasLost'} = 1; # FIXME is the "= 1" right?
1038         }
1039         # fix up the overdues in accounts...
1040         fixoverduesonreturn($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
1041         # find reserves.....
1042         my ($resfound, $resrec) = CheckReserves($iteminformation->{'itemnumber'});
1043         if ($resfound) {
1044         #       my $tobrcd = ReserveWaiting($resrec->{'itemnumber'}, $resrec->{'borrowernumber'});
1045                 $resrec->{'ResFound'} = $resfound;
1046                 $messages->{'ResFound'} = $resrec;
1047         }
1048         # update stats?
1049         # Record the fact that this book was returned.
1050         UpdateStats(\%env, $branch ,'return','0','',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'},$borrower->{'borrowernumber'});
1051         return ($doreturn, $messages, $iteminformation, $borrower);
1052 }
1053
1054 =head2 fixaccountforlostandreturned
1055
1056         &fixaccountforlostandreturned($iteminfo,$borrower);
1057
1058 Calculates the charge for a book lost and returned (Not exported & used only once)
1059
1060 C<$iteminfo> is a hashref to iteminfo. Only {itemnumber} is used.
1061
1062 C<$borrower> is a hashref to borrower. Only {borrowernumber is used.
1063
1064 =cut
1065
1066 sub fixaccountforlostandreturned {
1067         my ($iteminfo, $borrower) = @_;
1068         my %env;
1069         my $dbh = C4::Context->dbh;
1070         my $itm = $iteminfo->{'itemnumber'};
1071         # check for charge made for lost book
1072         my $sth = $dbh->prepare("select * from accountlines where (itemnumber = ?) and (accounttype='L' or accounttype='Rep') order by date desc");
1073         $sth->execute($itm);
1074         if (my $data = $sth->fetchrow_hashref) {
1075         # writeoff this amount
1076                 my $offset;
1077                 my $amount = $data->{'amount'};
1078                 my $acctno = $data->{'accountno'};
1079                 my $amountleft;
1080                 if ($data->{'amountoutstanding'} == $amount) {
1081                 $offset = $data->{'amount'};
1082                 $amountleft = 0;
1083                 } else {
1084                 $offset = $amount - $data->{'amountoutstanding'};
1085                 $amountleft = $data->{'amountoutstanding'} - $amount;
1086                 }
1087                 my $usth = $dbh->prepare("update accountlines set accounttype = 'LR',amountoutstanding='0'
1088                         where (borrowernumber = ?)
1089                         and (itemnumber = ?) and (accountno = ?) ");
1090                 $usth->execute($data->{'borrowernumber'},$itm,$acctno);
1091                 $usth->finish;
1092         #check if any credit is left if so writeoff other accounts
1093                 my $nextaccntno = getnextacctno(\%env,$data->{'borrowernumber'},$dbh);
1094                 if ($amountleft < 0){
1095                 $amountleft*=-1;
1096                 }
1097                 if ($amountleft > 0){
1098                 my $msth = $dbh->prepare("select * from accountlines where (borrowernumber = ?)
1099                                                         and (amountoutstanding >0) order by date");
1100                 $msth->execute($data->{'borrowernumber'});
1101         # offset transactions
1102                 my $newamtos;
1103                 my $accdata;
1104                 while (($accdata=$msth->fetchrow_hashref) and ($amountleft>0)){
1105                         if ($accdata->{'amountoutstanding'} < $amountleft) {
1106                         $newamtos = 0;
1107                         $amountleft -= $accdata->{'amountoutstanding'};
1108                         }  else {
1109                         $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
1110                         $amountleft = 0;
1111                         }
1112                         my $thisacct = $accdata->{'accountno'};
1113                         my $usth = $dbh->prepare("update accountlines set amountoutstanding= ?
1114                                         where (borrowernumber = ?)
1115                                         and (accountno=?)");
1116                         $usth->execute($newamtos,$data->{'borrowernumber'},'$thisacct');
1117                         $usth->finish;
1118                         $usth = $dbh->prepare("insert into accountoffsets
1119                                 (borrowernumber, accountno, offsetaccount,  offsetamount)
1120                                 values
1121                                 (?,?,?,?)");
1122                         $usth->execute($data->{'borrowernumber'},$accdata->{'accountno'},$nextaccntno,$newamtos);
1123                         $usth->finish;
1124                 }
1125                 $msth->finish;
1126                 }
1127                 if ($amountleft > 0){
1128                         $amountleft*=-1;
1129                 }
1130                 my $desc="Book Returned ".$iteminfo->{'barcode'};
1131                 $usth = $dbh->prepare("insert into accountlines
1132                         (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding)
1133                         values (?,?,now(),?,?,'CR',?)");
1134                 $usth->execute($data->{'borrowernumber'},$nextaccntno,0-$amount,$desc,$amountleft);
1135                 $usth->finish;
1136                 $usth = $dbh->prepare("insert into accountoffsets
1137                         (borrowernumber, accountno, offsetaccount,  offsetamount)
1138                         values (?,?,?,?)");
1139                 $usth->execute($borrower->{'borrowernumber'},$data->{'accountno'},$nextaccntno,$offset);
1140                 $usth->finish;
1141                 $usth = $dbh->prepare("update items set paidfor='' where itemnumber=?");
1142                 $usth->execute($itm);
1143                 $usth->finish;
1144         }
1145         $sth->finish;
1146         return;
1147 }
1148
1149 =head2 fixoverdueonreturn
1150
1151         &fixoverdueonreturn($brn,$itm);
1152
1153 ??
1154
1155 C<$brn> borrowernumber
1156
1157 C<$itm> itemnumber
1158
1159 =cut
1160
1161 sub fixoverduesonreturn {
1162         my ($brn, $itm) = @_;
1163         my $dbh = C4::Context->dbh;
1164         # check for overdue fine
1165         my $sth = $dbh->prepare("select * from accountlines where (borrowernumber = ?) and (itemnumber = ?) and (accounttype='FU' or accounttype='O')");
1166         $sth->execute($brn,$itm);
1167         # alter fine to show that the book has been returned
1168         if (my $data = $sth->fetchrow_hashref) {
1169                 my $usth=$dbh->prepare("update accountlines set accounttype='F' where (borrowernumber = ?) and (itemnumber = ?) and (acccountno = ?)");
1170                 $usth->execute($brn,$itm,$data->{'accountno'});
1171                 $usth->finish();
1172         }
1173         $sth->finish();
1174         return;
1175 }
1176
1177 # Not exported
1178 #
1179 # NOTE!: If you change this function, be sure to update the POD for
1180 # &getpatroninformation.
1181 #
1182 # $flags = &patronflags($env, $patron, $dbh);
1183 #
1184 # $flags->{CHARGES}
1185 #               {message}       Message showing patron's credit or debt
1186 #               {noissues}      Set if patron owes >$5.00
1187 #         {GNA}                 Set if patron gone w/o address
1188 #               {message}       "Borrower has no valid address"
1189 #               {noissues}      Set.
1190 #         {LOST}                Set if patron's card reported lost
1191 #               {message}       Message to this effect
1192 #               {noissues}      Set.
1193 #         {DBARRED}             Set is patron is debarred
1194 #               {message}       Message to this effect
1195 #               {noissues}      Set.
1196 #         {NOTES}               Set if patron has notes
1197 #               {message}       Notes about patron
1198 #         {ODUES}               Set if patron has overdue books
1199 #               {message}       "Yes"
1200 #               {itemlist}      ref-to-array: list of overdue books
1201 #               {itemlisttext}  Text list of overdue items
1202 #         {WAITING}             Set if there are items available that the
1203 #                               patron reserved
1204 #               {message}       Message to this effect
1205 #               {itemlist}      ref-to-array: list of available items
1206 sub patronflags {
1207 # Original subroutine for Circ2.pm
1208         my %flags;
1209         my ($env, $patroninformation, $dbh) = @_;
1210         my $amount = checkaccount($env, $patroninformation->{'borrowernumber'}, $dbh);
1211         if ($amount > 0) {
1212                 my %flaginfo;
1213                 my $noissuescharge = C4::Context->preference("noissuescharge");
1214                 $flaginfo{'message'}= sprintf "Patron owes \$%.02f", $amount;
1215                 if ($amount > $noissuescharge) {
1216                 $flaginfo{'noissues'} = 1;
1217                 }
1218                 $flags{'CHARGES'} = \%flaginfo;
1219         } elsif ($amount < 0){
1220         my %flaginfo;
1221         $flaginfo{'message'} = sprintf "Patron has credit of \$%.02f", -$amount;
1222                 $flags{'CHARGES'} = \%flaginfo;
1223         }
1224         if ($patroninformation->{'gonenoaddress'} == 1) {
1225                 my %flaginfo;
1226                 $flaginfo{'message'} = 'Borrower has no valid address.';
1227                 $flaginfo{'noissues'} = 1;
1228                 $flags{'GNA'} = \%flaginfo;
1229         }
1230         if ($patroninformation->{'lost'} == 1) {
1231                 my %flaginfo;
1232                 $flaginfo{'message'} = 'Borrower\'s card reported lost.';
1233                 $flaginfo{'noissues'} = 1;
1234                 $flags{'LOST'} = \%flaginfo;
1235         }
1236         if ($patroninformation->{'debarred'} == 1) {
1237                 my %flaginfo;
1238                 $flaginfo{'message'} = 'Borrower is Debarred.';
1239                 $flaginfo{'noissues'} = 1;
1240                 $flags{'DBARRED'} = \%flaginfo;
1241         }
1242         if ($patroninformation->{'borrowernotes'}) {
1243                 my %flaginfo;
1244                 $flaginfo{'message'} = "$patroninformation->{'borrowernotes'}";
1245                 $flags{'NOTES'} = \%flaginfo;
1246         }
1247         my ($odues, $itemsoverdue)
1248                         = checkoverdues($env, $patroninformation->{'borrowernumber'}, $dbh);
1249         if ($odues > 0) {
1250                 my %flaginfo;
1251                 $flaginfo{'message'} = "Yes";
1252                 $flaginfo{'itemlist'} = $itemsoverdue;
1253                 foreach (sort {$a->{'date_due'} cmp $b->{'date_due'}} @$itemsoverdue) {
1254                 $flaginfo{'itemlisttext'}.="$_->{'date_due'} $_->{'barcode'} $_->{'title'} \n";
1255                 }
1256                 $flags{'ODUES'} = \%flaginfo;
1257         }
1258         my ($nowaiting, $itemswaiting)
1259                         = CheckWaiting($patroninformation->{'borrowernumber'});
1260         if ($nowaiting > 0) {
1261                 my %flaginfo;
1262                 $flaginfo{'message'} = "Reserved items available";
1263                 $flaginfo{'itemlist'} = $itemswaiting;
1264                 $flags{'WAITING'} = \%flaginfo;
1265         }
1266         return(\%flags);
1267 }
1268
1269
1270 # Not exported
1271 sub checkoverdues {
1272 # From Main.pm, modified to return a list of overdueitems, in addition to a count
1273   #checks whether a borrower has overdue items
1274         my ($env, $bornum, $dbh)=@_;
1275         my @datearr = localtime;
1276         my $today = ($datearr[5] + 1900)."-".($datearr[4]+1)."-".$datearr[3];
1277         my @overdueitems;
1278         my $count = 0;
1279         my $sth = $dbh->prepare("SELECT * FROM issues,biblio,biblioitems,items
1280                         WHERE items.biblioitemnumber = biblioitems.biblioitemnumber
1281                                 AND items.biblionumber     = biblio.biblionumber
1282                                 AND issues.itemnumber      = items.itemnumber
1283                                 AND issues.borrowernumber  = ?
1284                                 AND issues.returndate is NULL
1285                                 AND issues.date_due < ?");
1286         $sth->execute($bornum,$today);
1287         while (my $data = $sth->fetchrow_hashref) {
1288         push (@overdueitems, $data);
1289         $count++;
1290         }
1291         $sth->finish;
1292         return ($count, \@overdueitems);
1293 }
1294
1295 # Not exported
1296 sub currentborrower {
1297 # Original subroutine for Circ2.pm
1298         my ($itemnumber) = @_;
1299         my $dbh = C4::Context->dbh;
1300         my $q_itemnumber = $dbh->quote($itemnumber);
1301         my $sth=$dbh->prepare("select borrowers.borrowernumber from
1302         issues,borrowers where issues.itemnumber=$q_itemnumber and
1303         issues.borrowernumber=borrowers.borrowernumber and issues.returndate is
1304         NULL");
1305         $sth->execute;
1306         my ($borrower) = $sth->fetchrow;
1307         return($borrower);
1308 }
1309
1310 # FIXME - Not exported, but used in 'updateitem.pl' anyway.
1311 sub checkreserve_to_delete {
1312 # Stolen from Main.pm
1313 # Check for reserves for biblio
1314         my ($env,$dbh,$itemnum)=@_;
1315         my $resbor = "";
1316         my $sth = $dbh->prepare("select * from reserves,items
1317         where (items.itemnumber = ?)
1318         and (reserves.cancellationdate is NULL)
1319         and (items.biblionumber = reserves.biblionumber)
1320         and ((reserves.found = 'W')
1321         or (reserves.found is null))
1322         order by priority");
1323         $sth->execute($itemnum);
1324         my $resrec;
1325         my $data=$sth->fetchrow_hashref;
1326         while ($data && $resbor eq '') {
1327         $resrec=$data;
1328         my $const = $data->{'constrainttype'};
1329         if ($const eq "a") {
1330         $resbor = $data->{'borrowernumber'};
1331         } else {
1332         my $found = 0;
1333         my $csth = $dbh->prepare("select * from reserveconstraints,items
1334                 where (borrowernumber=?)
1335                 and reservedate=?
1336                 and reserveconstraints.biblionumber=?
1337                 and (items.itemnumber=? and
1338                 items.biblioitemnumber = reserveconstraints.biblioitemnumber)");
1339         $csth->execute($data->{'borrowernumber'},$data->{'biblionumber'},$data->{'reservedate'},$itemnum);
1340         if (my $cdata=$csth->fetchrow_hashref) {$found = 1;}
1341         if ($const eq 'o') {
1342                 if ($found eq 1) {$resbor = $data->{'borrowernumber'};}
1343         } else {
1344                 if ($found eq 0) {$resbor = $data->{'borrowernumber'};}
1345         }
1346         $csth->finish();
1347         }
1348         $data=$sth->fetchrow_hashref;
1349         }
1350         $sth->finish;
1351         return ($resbor,$resrec);
1352 }
1353
1354 =head2 currentissues
1355
1356   $issues = &currentissues($env, $borrower);
1357
1358 Returns a list of books currently on loan to a patron.
1359
1360 If C<$env-E<gt>{todaysissues}> is set and true, C<&currentissues> only
1361 returns information about books issued today. If
1362 C<$env-E<gt>{nottodaysissues}> is set and true, C<&currentissues> only
1363 returns information about books issued before today. If both are
1364 specified, C<$env-E<gt>{todaysissues}> is ignored. If neither is
1365 specified, C<&currentissues> returns all of the patron's issues.
1366
1367 C<$borrower->{borrowernumber}> is the borrower number of the patron
1368 whose issues we want to list.
1369
1370 C<&currentissues> returns a PHP-style array: C<$issues> is a
1371 reference-to-hash whose keys are integers in the range 1...I<n>, where
1372 I<n> is the number of items on issue (either today or before today).
1373 C<$issues-E<gt>{I<n>}> is a reference-to-hash whose keys are all of
1374 the fields of the biblio, biblioitems, items, and issues fields of the
1375 Koha database for that particular item.
1376
1377 =cut
1378
1379 #'
1380 sub currentissues {
1381 # New subroutine for Circ2.pm
1382         my ($env, $borrower) = @_;
1383         my $dbh = C4::Context->dbh;
1384         my %currentissues;
1385         my $counter=1;
1386         my $borrowernumber = $borrower->{'borrowernumber'};
1387         my $crit='';
1388
1389         # Figure out whether to get the books issued today, or earlier.
1390         # FIXME - $env->{todaysissues} and $env->{nottodaysissues} can
1391         # both be specified, but are mutually-exclusive. This is bogus.
1392         # Make this a flag. Or better yet, return everything in (reverse)
1393         # chronological order and let the caller figure out which books
1394         # were issued today.
1395         if ($env->{'todaysissues'}) {
1396                 # FIXME - Could use
1397                 #       $today = POSIX::strftime("%Y%m%d", localtime);
1398                 # FIXME - Since $today will be used in either case, move it
1399                 # out of the two if-blocks.
1400                 my @datearr = localtime(time());
1401                 my $today = (1900+$datearr[5]).sprintf "%02d", ($datearr[4]+1).sprintf "%02d", $datearr[3];
1402                 # FIXME - MySQL knows about dates. Just use
1403                 #       and issues.timestamp = curdate();
1404                 $crit=" and issues.timestamp like '$today%' ";
1405         }
1406         if ($env->{'nottodaysissues'}) {
1407                 # FIXME - Could use
1408                 #       $today = POSIX::strftime("%Y%m%d", localtime);
1409                 # FIXME - Since $today will be used in either case, move it
1410                 # out of the two if-blocks.
1411                 my @datearr = localtime(time());
1412                 my $today = (1900+$datearr[5]).sprintf "%02d", ($datearr[4]+1).sprintf "%02d", $datearr[3];
1413                 # FIXME - MySQL knows about dates. Just use
1414                 #       and issues.timestamp < curdate();
1415                 $crit=" and !(issues.timestamp like '$today%') ";
1416         }
1417
1418         # FIXME - Does the caller really need every single field from all
1419         # four tables?
1420         my $sth=$dbh->prepare("select * from issues,items,biblioitems,biblio where
1421         borrowernumber=? and issues.itemnumber=items.itemnumber and
1422         items.biblionumber=biblio.biblionumber and
1423         items.biblioitemnumber=biblioitems.biblioitemnumber and returndate is null
1424         $crit order by issues.date_due");
1425         $sth->execute($borrowernumber);
1426         while (my $data = $sth->fetchrow_hashref) {
1427                 # FIXME - The Dewey code is a string, not a number.
1428                 $data->{'dewey'}=~s/0*$//;
1429                 ($data->{'dewey'} == 0) && ($data->{'dewey'}='');
1430                 # FIXME - Could use
1431                 #       $todaysdate = POSIX::strftime("%Y%m%d", localtime)
1432                 # or better yet, just reuse $today which was calculated above.
1433                 # This function isn't going to run until midnight, is it?
1434                 # Alternately, use
1435                 #       $todaysdate = POSIX::strftime("%Y-%m-%d", localtime)
1436                 #       if ($data->{'date_due'} lt $todaysdate)
1437                 #               ...
1438                 # Either way, the date should be be formatted outside of the
1439                 # loop.
1440                 my @datearr = localtime(time());
1441                 my $todaysdate = (1900+$datearr[5]).sprintf ("%0.2d", ($datearr[4]+1)).sprintf ("%0.2d", $datearr[3]);
1442                 my $datedue=$data->{'date_due'};
1443                 $datedue=~s/-//g;
1444                 if ($datedue < $todaysdate) {
1445                         $data->{'overdue'}=1;
1446                 }
1447                 my $itemnumber=$data->{'itemnumber'};
1448                 # FIXME - Consecutive integers as hash keys? You have GOT to
1449                 # be kidding me! Use an array, fercrissakes!
1450                 $currentissues{$counter}=$data;
1451                 $counter++;
1452         }
1453         $sth->finish;
1454         return(\%currentissues);
1455 }
1456
1457 =head2 getissues
1458
1459   $issues = &getissues($borrowernumber);
1460
1461 Returns the set of books currently on loan to a patron.
1462
1463 C<$borrowernumber> is the patron's borrower number.
1464
1465 C<&getissues> returns a PHP-style array: C<$issues> is a
1466 reference-to-hash whose keys are integers in the range 0..I<n>-1,
1467 where I<n> is the number of books the patron currently has on loan.
1468
1469 The values of C<$issues> are references-to-hash whose keys are
1470 selected fields from the issues, items, biblio, and biblioitems tables
1471 of the Koha database.
1472
1473 =cut
1474 #'
1475 sub getissues {
1476 # New subroutine for Circ2.pm
1477         my ($borrower) = @_;
1478         my $dbh = C4::Context->dbh;
1479         my $borrowernumber = $borrower->{'borrowernumber'};
1480         my %currentissues;
1481         my $select = "SELECT items.*,issues.timestamp      AS timestamp,
1482                                 issues.date_due       AS date_due,
1483                                 items.barcode         AS barcode,
1484                                 biblio.title          AS title,
1485                                 biblio.author         AS author,
1486                                 biblioitems.dewey     AS dewey,
1487                                 itemtypes.description AS itemtype,
1488                                 biblioitems.subclass  AS subclass,
1489                                 biblioitems.classification AS classification
1490                         FROM issues,items,biblioitems,biblio, itemtypes
1491                         WHERE issues.borrowernumber  = ?
1492                         AND issues.itemnumber      = items.itemnumber
1493                         AND items.biblionumber     = biblio.biblionumber
1494                         AND items.biblioitemnumber = biblioitems.biblioitemnumber
1495                         AND itemtypes.itemtype     = biblioitems.itemtype
1496                         AND issues.returndate      IS NULL
1497                         ORDER BY issues.date_due";
1498         #    print $select;
1499         my $sth=$dbh->prepare($select);
1500         $sth->execute($borrowernumber);
1501         my $counter = 0;
1502         while (my $data = $sth->fetchrow_hashref) {
1503                 $data->{'dewey'} =~ s/0*$//;
1504                 ($data->{'dewey'} == 0) && ($data->{'dewey'} = '');
1505                         # FIXME - The Dewey code is a string, not a number.
1506                 # FIXME - Use POSIX::strftime to get a text version of today's
1507                 # date. That's what it's for.
1508                 # FIXME - Move the date calculation outside of the loop.
1509                 my @datearr = localtime(time());
1510                 my $todaysdate = (1900+$datearr[5]).sprintf ("%0.2d", ($datearr[4]+1)).sprintf ("%0.2d", $datearr[3]);
1511
1512                 # FIXME - Instead of converting the due date to YYYYMMDD, just
1513                 # use
1514                 #       $todaysdate = POSIX::strftime("%Y-%m-%d", localtime);
1515                 #       ...
1516                 #       if ($date->{date_due} lt $todaysdate)
1517                 my $datedue = $data->{'date_due'};
1518                 $datedue =~ s/-//g;
1519                 if ($datedue < $todaysdate) {
1520                         $data->{'overdue'} = 1;
1521                 }
1522                 $currentissues{$counter} = $data;
1523                 $counter++;
1524                         # FIXME - This is ludicrous. If you want to return an
1525                         # array of values, just use an array. That's what
1526                         # they're there for.
1527         }
1528         $sth->finish;
1529         return(\%currentissues);
1530 }
1531
1532 # Not exported
1533 sub checkwaiting {
1534 #Stolen from Main.pm
1535 # check for reserves waiting
1536         my ($env,$dbh,$bornum)=@_;
1537         my @itemswaiting;
1538         my $sth = $dbh->prepare("select * from reserves where (borrowernumber = ?) and (reserves.found='W') and cancellationdate is NULL");
1539         $sth->execute($bornum);
1540         my $cnt=0;
1541         if (my $data=$sth->fetchrow_hashref) {
1542                 $itemswaiting[$cnt] =$data;
1543                 $cnt ++
1544         }
1545         $sth->finish;
1546         return ($cnt,\@itemswaiting);
1547 }
1548
1549 =head2 renewstatus
1550
1551   $ok = &renewstatus($env, $dbh, $borrowernumber, $itemnumber);
1552
1553 Find out whether a borrowed item may be renewed.
1554
1555 C<$env> is ignored.
1556
1557 C<$dbh> is a DBI handle to the Koha database.
1558
1559 C<$borrowernumber> is the borrower number of the patron who currently
1560 has the item on loan.
1561
1562 C<$itemnumber> is the number of the item to renew.
1563
1564 C<$renewstatus> returns a true value iff the item may be renewed. The
1565 item must currently be on loan to the specified borrower; renewals
1566 must be allowed for the item's type; and the borrower must not have
1567 already renewed the loan.
1568
1569 =cut
1570
1571 sub renewstatus {
1572         # check renewal status
1573         my ($env,$bornum,$itemno)=@_;
1574         my $dbh = C4::Context->dbh;
1575         my $renews = 1;
1576         my $renewokay = 0;
1577         # Look in the issues table for this item, lent to this borrower,
1578         # and not yet returned.
1579         
1580         # FIXME - I think this function could be redone to use only one SQL call.
1581         my $sth1 = $dbh->prepare("select * from issues
1582                                                                 where (borrowernumber = ?)
1583                                                                 and (itemnumber = ?)
1584                                                                 and returndate is null");
1585         $sth1->execute($bornum,$itemno);
1586         if (my $data1 = $sth1->fetchrow_hashref) {
1587                 # Found a matching item
1588         
1589                 # See if this item may be renewed. This query is convoluted
1590                 # because it's a bit messy: given the item number, we need to find
1591                 # the biblioitem, which gives us the itemtype, which tells us
1592                 # whether it may be renewed.
1593                 my $sth2 = $dbh->prepare("select renewalsallowed from items,biblioitems,itemtypes
1594                 where (items.itemnumber = ?)
1595                 and (items.biblioitemnumber = biblioitems.biblioitemnumber)
1596                 and (biblioitems.itemtype = itemtypes.itemtype)");
1597                 $sth2->execute($itemno);
1598                 if (my $data2=$sth2->fetchrow_hashref) {
1599                         $renews = $data2->{'renewalsallowed'};
1600                 }
1601                 if ($renews > $data1->{'renewals'}) {
1602                         $renewokay = 1;
1603                 }
1604                 $sth2->finish;
1605         }
1606         $sth1->finish;
1607         return($renewokay);
1608 }
1609
1610 =head2 renewbook
1611
1612   &renewbook($env, $borrowernumber, $itemnumber, $datedue);
1613
1614 Renews a loan.
1615
1616 C<$env-E<gt>{branchcode}> is the code of the branch where the
1617 renewal is taking place.
1618
1619 C<$env-E<gt>{usercode}> is the value to log in C<statistics.usercode>
1620 in the Koha database.
1621
1622 C<$borrowernumber> is the borrower number of the patron who currently
1623 has the item.
1624
1625 C<$itemnumber> is the number of the item to renew.
1626
1627 C<$datedue> can be used to set the due date. If C<$datedue> is the
1628 empty string, C<&renewbook> will calculate the due date automatically
1629 from the book's item type. If you wish to set the due date manually,
1630 C<$datedue> should be in the form YYYY-MM-DD.
1631
1632 =cut
1633
1634 sub renewbook {
1635         # mark book as renewed
1636         my ($env,$bornum,$itemno,$datedue)=@_;
1637         my $dbh = C4::Context->dbh;
1638
1639         # If the due date wasn't specified, calculate it by adding the
1640         # book's loan length to today's date.
1641         if ($datedue eq "" ) {
1642                 #debug_msg($env, "getting date");
1643                 my $iteminformation = getiteminformation($env, $itemno,0);
1644                 my $borrower = getpatroninformation($env,$bornum,0);
1645                 my $loanlength = getLoanLength($borrower->{'categorycode'},$iteminformation->{'itemtype'},$borrower->{'branchcode'});
1646                 $datedue = UnixDate(DateCalc($iteminformation->{date_due},"$loanlength days"),"%Y-%m-%d");
1647         }
1648
1649         # Find the issues record for this book
1650         my $sth=$dbh->prepare("select * from issues where borrowernumber=? and itemnumber=? and returndate is null");
1651         $sth->execute($bornum,$itemno);
1652         my $issuedata=$sth->fetchrow_hashref;
1653         $sth->finish;
1654
1655         # Update the issues record to have the new due date, and a new count
1656         # of how many times it has been renewed.
1657         my $renews = $issuedata->{'renewals'} +1;
1658         $sth=$dbh->prepare("update issues set date_due = ?, renewals = ?
1659                 where borrowernumber=? and itemnumber=? and returndate is null");
1660         $sth->execute($datedue,$renews,$bornum,$itemno);
1661         $sth->finish;
1662
1663         # Log the renewal
1664         UpdateStats($env,$env->{'branchcode'},'renew','','',$itemno);
1665
1666         # Charge a new rental fee, if applicable?
1667         my ($charge,$type)=calc_charges($env, $itemno, $bornum);
1668         if ($charge > 0){
1669                 my $accountno=getnextacctno($env,$bornum,$dbh);
1670                 my $item=getiteminformation($env, $itemno);
1671                 $sth=$dbh->prepare("Insert into accountlines (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding,itemnumber)
1672                                                         values (?,?,now(),?,?,?,?,?)");
1673                 $sth->execute($bornum,$accountno,$charge,"Renewal of Rental Item $item->{'title'} $item->{'barcode'}",'Rent',$charge,$itemno);
1674                 $sth->finish;
1675         #     print $account;
1676         }
1677         
1678         #  return();
1679 }
1680
1681
1682
1683 =item calc_charges
1684
1685   ($charge, $item_type) = &calc_charges($env, $itemnumber, $borrowernumber);
1686
1687 Calculate how much it would cost for a given patron to borrow a given
1688 item, including any applicable discounts.
1689
1690 C<$env> is ignored.
1691
1692 C<$itemnumber> is the item number of item the patron wishes to borrow.
1693
1694 C<$borrowernumber> is the patron's borrower number.
1695
1696 C<&calc_charges> returns two values: C<$charge> is the rental charge,
1697 and C<$item_type> is the code for the item's item type (e.g., C<VID>
1698 if it's a video).
1699
1700 =cut
1701
1702 sub calc_charges {
1703         # calculate charges due
1704         my ($env, $itemno, $bornum)=@_;
1705         my $charge=0;
1706         my $dbh = C4::Context->dbh;
1707         my $item_type;
1708         
1709         # Get the book's item type and rental charge (via its biblioitem).
1710         my $sth1= $dbh->prepare("select itemtypes.itemtype,rentalcharge from items,biblioitems,itemtypes
1711                                                                 where (items.itemnumber =?)
1712                                                                 and (biblioitems.biblioitemnumber = items.biblioitemnumber)
1713                                                                 and (biblioitems.itemtype = itemtypes.itemtype)");
1714         $sth1->execute($itemno);
1715         my $data1=$sth1->fetchrow_hashref;
1716         $item_type = $data1->{'itemtype'};
1717         $charge = $data1->{'rentalcharge'};
1718         $sth1->finish;
1719         return ($charge,$item_type);
1720 }
1721
1722
1723 # FIXME - A virtually identical function appears in
1724 # C4::Circulation::Issues. Pick one and stick with it.
1725 sub createcharge {
1726 #Stolen from Issues.pm
1727     my ($env,$dbh,$itemno,$bornum,$charge) = @_;
1728     my $nextaccntno = getnextacctno($env,$bornum,$dbh);
1729     my $sth = $dbh->prepare(<<EOT);
1730         INSERT INTO     accountlines
1731                         (borrowernumber, itemnumber, accountno,
1732                          date, amount, description, accounttype,
1733                          amountoutstanding)
1734         VALUES          (?, ?, ?,
1735                          now(), ?, 'Rental', 'Rent',
1736                          ?)
1737 EOT
1738     $sth->execute($bornum, $itemno, $nextaccntno, $charge, $charge);
1739     $sth->finish;
1740 }
1741
1742
1743 sub getnextacctno {
1744 # Stolen from Accounts.pm
1745     my ($env,$bornumber,$dbh)=@_;
1746     my $nextaccntno = 1;
1747     my $sth = $dbh->prepare("select * from accountlines where (borrowernumber = ?) order by accountno desc");
1748     $sth->execute($bornumber);
1749     if (my $accdata=$sth->fetchrow_hashref){
1750         $nextaccntno = $accdata->{'accountno'} + 1;
1751     }
1752     $sth->finish;
1753     return($nextaccntno);
1754 }
1755
1756 =item find_reserves
1757
1758   ($status, $record) = &find_reserves($itemnumber);
1759
1760 Looks up an item in the reserves.
1761
1762 C<$itemnumber> is the itemnumber to look up.
1763
1764 C<$status> is true iff the search was successful.
1765
1766 C<$record> is a reference-to-hash describing the reserve. Its keys are
1767 the fields from the reserves table of the Koha database.
1768
1769 =cut
1770 #'
1771 # FIXME - This API is bogus: just return the record, or undef if none
1772 # was found.
1773 # FIXME - There's also a &C4::Circulation::Returns::find_reserves, but
1774 # that one looks rather different.
1775 sub find_reserves {
1776 # Stolen from Returns.pm
1777     my ($itemno) = @_;
1778     my %env;
1779     my $dbh = C4::Context->dbh;
1780     my ($itemdata) = getiteminformation(\%env, $itemno,0);
1781     my $bibno = $dbh->quote($itemdata->{'biblionumber'});
1782     my $bibitm = $dbh->quote($itemdata->{'biblioitemnumber'});
1783     my $sth = $dbh->prepare("select * from reserves where ((found = 'W') or (found is null)) and biblionumber = ? and cancellationdate is NULL order by priority, reservedate");
1784     $sth->execute($bibno);
1785     my $resfound = 0;
1786     my $resrec;
1787     my $lastrec;
1788 # print $query;
1789
1790     # FIXME - I'm not really sure what's going on here, but since we
1791     # only want one result, wouldn't it be possible (and far more
1792     # efficient) to do something clever in SQL that only returns one
1793     # set of values?
1794     while (($resrec = $sth->fetchrow_hashref) && (not $resfound)) {
1795                 # FIXME - Unlike Pascal, Perl allows you to exit loops
1796                 # early. Take out the "&& (not $resfound)" and just
1797                 # use "last" at the appropriate point in the loop.
1798                 # (Oh, and just in passing: if you'd used "!" instead
1799                 # of "not", you wouldn't have needed the parentheses.)
1800         $lastrec = $resrec;
1801         my $brn = $dbh->quote($resrec->{'borrowernumber'});
1802         my $rdate = $dbh->quote($resrec->{'reservedate'});
1803         my $bibno = $dbh->quote($resrec->{'biblionumber'});
1804         if ($resrec->{'found'} eq "W") {
1805             if ($resrec->{'itemnumber'} eq $itemno) {
1806                 $resfound = 1;
1807             }
1808         } else {
1809             # FIXME - Use 'elsif' to avoid unnecessary indentation.
1810             if ($resrec->{'constrainttype'} eq "a") {
1811                 $resfound = 1;
1812             } else {
1813                         my $consth = $dbh->prepare("select * from reserveconstraints where borrowernumber = ? and reservedate = ? and biblionumber = ? and biblioitemnumber = ?");
1814                         $consth->execute($brn,$rdate,$bibno,$bibitm);
1815                         if (my $conrec = $consth->fetchrow_hashref) {
1816                                 if ($resrec->{'constrainttype'} eq "o") {
1817                                 $resfound = 1;
1818                                 }
1819                         }
1820                 $consth->finish;
1821                 }
1822         }
1823         if ($resfound) {
1824             my $updsth = $dbh->prepare("update reserves set found = 'W', itemnumber = ? where borrowernumber = ? and reservedate = ? and biblionumber = ?");
1825             $updsth->execute($itemno,$brn,$rdate,$bibno);
1826             $updsth->finish;
1827             # FIXME - "last;" here to break out of the loop early.
1828         }
1829     }
1830     $sth->finish;
1831     return ($resfound,$lastrec);
1832 }
1833
1834 sub fixdate {
1835     my ($year, $month, $day) = @_;
1836     my $invalidduedate;
1837     my $date;
1838     if (($year eq 0) && ($month eq 0) && ($year eq 0)) {
1839 #       $env{'datedue'}='';
1840     } else {
1841         if (($year eq 0) || ($month eq 0) || ($year eq 0)) {
1842             $invalidduedate=1;
1843         } else {
1844             if (($day>30) && (($month==4) || ($month==6) || ($month==9) || ($month==11))) {
1845                 $invalidduedate = 1;
1846             } elsif (($day > 29) && ($month == 2)) {
1847                 $invalidduedate=1;
1848             } elsif (($month == 2) && ($day > 28) && (($year%4) && ((!($year%100) || ($year%400))))) {
1849                 $invalidduedate=1;
1850             } else {
1851                 $date="$year-$month-$day";
1852             }
1853         }
1854     }
1855     return ($date, $invalidduedate);
1856 }
1857
1858 1;
1859 __END__
1860
1861 =back
1862
1863 =head1 AUTHOR
1864
1865 Koha Developement team <info@koha.org>
1866
1867 =cut