c023076c6b71141ddb01304fe7773f495eb5aae6
[koha-ffzg.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                 } elsif ($restype eq "Reserved") {
770                         # The item is on reserve for someone else.
771                         my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
772                         my $branches = getbranches();
773                         my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
774                         $needsconfirmation{RESERVED} = "$res->{'reservedate'} : $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})";
775                 }
776         }
777         return(\%issuingimpossible,\%needsconfirmation);
778 }
779
780 =head2 issuebook
781
782 Issue a book. Does no check, they are done in canbookbeissued. If we reach this sub, it means the user confirmed if needed.
783
784 &issuebook($env,$borrower,$barcode,$date)
785
786 =over 4
787
788 C<$env> Environment variable. Should be empty usually, but used by other subs. Next code cleaning could drop it.
789
790 C<$borrower> hash with borrower informations (from getpatroninformation)
791
792 C<$barcode> is the bar code of the book being issued.
793
794 C<$date> contains the max date of return. calculated if empty.
795
796 =cut
797
798 #
799 # issuing book. We already have checked it can be issued, so, just issue it !
800 #
801 sub issuebook {
802         my ($env,$borrower,$barcode,$date) = @_;
803         my $dbh = C4::Context->dbh;
804 #       my ($borrower, $flags) = &getpatroninformation($env, $borrowernumber, 0);
805         my $iteminformation = getiteminformation($env, 0, $barcode);
806 #               warn "B : ".$borrower->{borrowernumber}." / I : ".$iteminformation->{'itemnumber'};
807 #
808 # check if we just renew the issue.
809 #
810         my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
811         if ($currentborrower eq $borrower->{'borrowernumber'}) {
812                 my ($charge,$itemtype) = calc_charges($env, $iteminformation->{'itemnumber'}, $borrower->{'borrowernumber'});
813                 if ($charge > 0) {
814                         createcharge($env, $dbh, $iteminformation->{'itemnumber'}, $borrower->{'borrowernumber'}, $charge);
815                         $iteminformation->{'charge'} = $charge;
816                 }
817                 &UpdateStats($env,$env->{'branchcode'},'renew',$charge,'',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'},$borrower->{'borrowernumber'});
818                 renewbook($env,$dbh, $borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
819         } else {
820 #
821 # NOT a renewal
822 #
823                 if ($currentborrower ne '') {
824                         # This book is currently on loan, but not to the person
825                         # who wants to borrow it now. mark it returned before issuing to the new borrower
826                         returnbook($iteminformation->{'barcode'}, $env->{'branchcode'});
827                 }
828                 # See if the item is on reserve.
829                 my ($restype, $res) = CheckReserves($iteminformation->{'itemnumber'});
830                 if ($restype) {
831                         my $resbor = $res->{'borrowernumber'};
832                         if ($resbor eq $borrower->{'borrowernumber'}) {
833                                 # The item is on reserve to the current patron
834                                 FillReserve($res);
835                         } elsif ($restype eq "Waiting") {
836                                 # The item is on reserve and waiting, but has been
837                                 # reserved by some other patron.
838                                 my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
839                                 my $branches = getbranches();
840                                 my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
841                                 CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
842                         } elsif ($restype eq "Reserved") {
843                                 # The item is on reserve for someone else.
844                                 my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
845                                 my $branches = getbranches();
846                                 my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
847                                 my $tobrcd = ReserveWaiting($res->{'itemnumber'}, $res->{'borrowernumber'});
848                                 transferbook($tobrcd,$barcode, 1);
849                         }
850                 }
851                 # Record in the database the fact that the book was issued.
852                 my $sth=$dbh->prepare("insert into issues (borrowernumber, itemnumber, date_due, branchcode) values (?,?,?,?)");
853                 my $loanlength = getLoanLength($borrower->{'categorycode'},$iteminformation->{'itemtype'},$borrower->{'branchcode'});
854                 my $datedue=time+($loanlength)*86400;
855                 my @datearr = localtime($datedue);
856                 my $dateduef = (1900+$datearr[5])."-".($datearr[4]+1)."-".$datearr[3];
857                 if ($date) {
858                         $dateduef=$date;
859                 }
860                 $sth->execute($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'}, $dateduef, $env->{'branchcode'});
861                 $sth->finish;
862                 $iteminformation->{'issues'}++;
863                 $sth=$dbh->prepare("update items set issues=? where itemnumber=?");
864                 $sth->execute($iteminformation->{'issues'},$iteminformation->{'itemnumber'});
865                 $sth->finish;
866                 &itemseen($iteminformation->{'itemnumber'});
867                 # If it costs to borrow this book, charge it to the patron's account.
868                 my ($charge,$itemtype)=calc_charges($env, $iteminformation->{'itemnumber'}, $borrower->{'borrowernumber'});
869                 if ($charge > 0) {
870                         createcharge($env, $dbh, $iteminformation->{'itemnumber'}, $borrower->{'borrowernumber'}, $charge);
871                         $iteminformation->{'charge'}=$charge;
872                 }
873                 # Record the fact that this book was issued.
874                 &UpdateStats($env,$env->{'branchcode'},'issue',$charge,'',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'},$borrower->{'borrowernumber'});
875         }
876 }
877
878 =head2 getLoanLength
879
880 Get loan length for an itemtype, a borrower type and a branch
881
882 my $loanlength = &getLoanLength($borrowertype,$itemtype,branchcode)
883
884 =cut
885
886 sub getLoanLength {
887         my ($borrowertype,$itemtype,$branchcode) = @_;
888         my $dbh = C4::Context->dbh;
889         my $sth = $dbh->prepare("select issuelength from issuingrules where categorycode=? and itemtype=? and branchcode=?");
890         # try to find issuelength & return the 1st available.
891         # check with borrowertype, itemtype and branchcode, then without one of those parameters
892         $sth->execute($borrowertype,$itemtype,$branchcode);
893         my $loanlength = $sth->fetchrow_hashref;
894         return $loanlength->{issuelength} if defined($loanlength);
895         
896         $sth->execute($borrowertype,$itemtype,"");
897         my $loanlength = $sth->fetchrow_hashref;
898         return $loanlength->{issuelength} if defined($loanlength);
899         
900         $sth->execute($borrowertype,"*",$branchcode);
901         my $loanlength = $sth->fetchrow_hashref;
902         return $loanlength->{issuelength} if defined($loanlength);
903
904         $sth->execute("*",$itemtype,$branchcode);
905         my $loanlength = $sth->fetchrow_hashref;
906         return $loanlength->{issuelength} if defined($loanlength);
907
908         $sth->execute($borrowertype,"*","");
909         my $loanlength = $sth->fetchrow_hashref;
910         return $loanlength->{issuelength} if defined($loanlength);
911
912         $sth->execute("*","*",$branchcode);
913         my $loanlength = $sth->fetchrow_hashref;
914         return $loanlength->{issuelength} if defined($loanlength);
915
916         $sth->execute("*",$itemtype,"");
917         my $loanlength = $sth->fetchrow_hashref;
918         return $loanlength->{issuelength} if defined($loanlength);
919
920         $sth->execute("*","*","");
921         my $loanlength = $sth->fetchrow_hashref;
922         return $loanlength->{issuelength} if defined($loanlength);
923
924         # if no rule is set => 21 days (hardcoded)
925         return 21;
926 }
927 =head2 returnbook
928
929   ($doreturn, $messages, $iteminformation, $borrower) =
930           &returnbook($barcode, $branch);
931
932 Returns a book.
933
934 C<$barcode> is the bar code of the book being returned. C<$branch> is
935 the code of the branch where the book is being returned.
936
937 C<&returnbook> returns a list of four items:
938
939 C<$doreturn> is true iff the return succeeded.
940
941 C<$messages> is a reference-to-hash giving the reason for failure:
942
943 =over 4
944
945 =item C<BadBarcode>
946
947 No item with this barcode exists. The value is C<$barcode>.
948
949 =item C<NotIssued>
950
951 The book is not currently on loan. The value is C<$barcode>.
952
953 =item C<IsPermanent>
954
955 The book's home branch is a permanent collection. If you have borrowed
956 this book, you are not allowed to return it. The value is the code for
957 the book's home branch.
958
959 =item C<wthdrawn>
960
961 This book has been withdrawn/cancelled. The value should be ignored.
962
963 =item C<ResFound>
964
965 The item was reserved. The value is a reference-to-hash whose keys are
966 fields from the reserves table of the Koha database, and
967 C<biblioitemnumber>. It also has the key C<ResFound>, whose value is
968 either C<Waiting>, C<Reserved>, or 0.
969
970 =back
971
972 C<$borrower> is a reference-to-hash, giving information about the
973 patron who last borrowed the book.
974
975 =cut
976
977 # FIXME - This API is bogus. There's no need to return $borrower and
978 # $iteminformation; the caller can ask about those separately, if it
979 # cares (it'd be inefficient to make two database calls instead of
980 # one, but &getpatroninformation and &getiteminformation can be
981 # memoized if this is an issue).
982 #
983 # The ($doreturn, $messages) tuple is redundant: if the return
984 # succeeded, that's all the caller needs to know. So &returnbook can
985 # return 1 and 0 on success and failure, and set
986 # $C4::Circulation::Circ2::errmsg to indicate the error. Or it can
987 # return undef for success, and an error message on error (though this
988 # is more C-ish than Perl-ish).
989
990 sub returnbook {
991         my ($barcode, $branch) = @_;
992         my %env;
993         my $messages;
994         my $dbh = C4::Context->dbh;
995         my $doreturn = 1;
996         die '$branch not defined' unless defined $branch; # just in case (bug 170)
997         # get information on item
998         my ($iteminformation) = getiteminformation(\%env, 0, $barcode);
999         if (not $iteminformation) {
1000                 $messages->{'BadBarcode'} = $barcode;
1001                 $doreturn = 0;
1002         }
1003         # find the borrower
1004         my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
1005         if ((not $currentborrower) && $doreturn) {
1006                 $messages->{'NotIssued'} = $barcode;
1007                 $doreturn = 0;
1008         }
1009         # check if the book is in a permanent collection....
1010         my $hbr = $iteminformation->{'homebranch'};
1011         my $branches = getbranches();
1012         if ($branches->{$hbr}->{'PE'}) {
1013                 $messages->{'IsPermanent'} = $hbr;
1014         }
1015         # check that the book has been cancelled
1016         if ($iteminformation->{'wthdrawn'}) {
1017                 $messages->{'wthdrawn'} = 1;
1018                 $doreturn = 0;
1019         }
1020         # update issues, thereby returning book (should push this out into another subroutine
1021         my ($borrower) = getpatroninformation(\%env, $currentborrower, 0);
1022         if ($doreturn) {
1023                 my $sth = $dbh->prepare("update issues set returndate = now() where (borrowernumber = ?) and (itemnumber = ?) and (returndate is null)");
1024                 $sth->execute($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
1025                 $messages->{'WasReturned'} = 1; # FIXME is the "= 1" right?
1026         }
1027         ($borrower) = getpatroninformation(\%env, $currentborrower, 0);
1028         # transfer book to the current branch
1029         my ($transfered, $mess, $item) = transferbook($branch, $barcode, 1);
1030         if ($transfered) {
1031                 $messages->{'WasTransfered'} = 1; # FIXME is the "= 1" right?
1032         }
1033         # fix up the accounts.....
1034         if ($iteminformation->{'itemlost'}) {
1035                 fixaccountforlostandreturned($iteminformation, $borrower);
1036                 $messages->{'WasLost'} = 1; # FIXME is the "= 1" right?
1037         }
1038         # fix up the overdues in accounts...
1039         fixoverduesonreturn($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
1040         # find reserves.....
1041         my ($resfound, $resrec) = CheckReserves($iteminformation->{'itemnumber'});
1042         if ($resfound) {
1043         #       my $tobrcd = ReserveWaiting($resrec->{'itemnumber'}, $resrec->{'borrowernumber'});
1044                 $resrec->{'ResFound'} = $resfound;
1045                 $messages->{'ResFound'} = $resrec;
1046         }
1047         # update stats?
1048         # Record the fact that this book was returned.
1049         UpdateStats(\%env, $branch ,'return','0','',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'},$borrower->{'borrowernumber'});
1050         return ($doreturn, $messages, $iteminformation, $borrower);
1051 }
1052
1053 =head2 fixaccountforlostandreturned
1054
1055         &fixaccountforlostandreturned($iteminfo,$borrower);
1056
1057 Calculates the charge for a book lost and returned (Not exported & used only once)
1058
1059 C<$iteminfo> is a hashref to iteminfo. Only {itemnumber} is used.
1060
1061 C<$borrower> is a hashref to borrower. Only {borrowernumber is used.
1062
1063 =cut
1064
1065 sub fixaccountforlostandreturned {
1066         my ($iteminfo, $borrower) = @_;
1067         my %env;
1068         my $dbh = C4::Context->dbh;
1069         my $itm = $iteminfo->{'itemnumber'};
1070         # check for charge made for lost book
1071         my $sth = $dbh->prepare("select * from accountlines where (itemnumber = ?) and (accounttype='L' or accounttype='Rep') order by date desc");
1072         $sth->execute($itm);
1073         if (my $data = $sth->fetchrow_hashref) {
1074         # writeoff this amount
1075                 my $offset;
1076                 my $amount = $data->{'amount'};
1077                 my $acctno = $data->{'accountno'};
1078                 my $amountleft;
1079                 if ($data->{'amountoutstanding'} == $amount) {
1080                 $offset = $data->{'amount'};
1081                 $amountleft = 0;
1082                 } else {
1083                 $offset = $amount - $data->{'amountoutstanding'};
1084                 $amountleft = $data->{'amountoutstanding'} - $amount;
1085                 }
1086                 my $usth = $dbh->prepare("update accountlines set accounttype = 'LR',amountoutstanding='0'
1087                         where (borrowernumber = ?)
1088                         and (itemnumber = ?) and (accountno = ?) ");
1089                 $usth->execute($data->{'borrowernumber'},$itm,$acctno);
1090                 $usth->finish;
1091         #check if any credit is left if so writeoff other accounts
1092                 my $nextaccntno = getnextacctno(\%env,$data->{'borrowernumber'},$dbh);
1093                 if ($amountleft < 0){
1094                 $amountleft*=-1;
1095                 }
1096                 if ($amountleft > 0){
1097                 my $msth = $dbh->prepare("select * from accountlines where (borrowernumber = ?)
1098                                                         and (amountoutstanding >0) order by date");
1099                 $msth->execute($data->{'borrowernumber'});
1100         # offset transactions
1101                 my $newamtos;
1102                 my $accdata;
1103                 while (($accdata=$msth->fetchrow_hashref) and ($amountleft>0)){
1104                         if ($accdata->{'amountoutstanding'} < $amountleft) {
1105                         $newamtos = 0;
1106                         $amountleft -= $accdata->{'amountoutstanding'};
1107                         }  else {
1108                         $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
1109                         $amountleft = 0;
1110                         }
1111                         my $thisacct = $accdata->{'accountno'};
1112                         my $usth = $dbh->prepare("update accountlines set amountoutstanding= ?
1113                                         where (borrowernumber = ?)
1114                                         and (accountno=?)");
1115                         $usth->execute($newamtos,$data->{'borrowernumber'},'$thisacct');
1116                         $usth->finish;
1117                         $usth = $dbh->prepare("insert into accountoffsets
1118                                 (borrowernumber, accountno, offsetaccount,  offsetamount)
1119                                 values
1120                                 (?,?,?,?)");
1121                         $usth->execute($data->{'borrowernumber'},$accdata->{'accountno'},$nextaccntno,$newamtos);
1122                         $usth->finish;
1123                 }
1124                 $msth->finish;
1125                 }
1126                 if ($amountleft > 0){
1127                         $amountleft*=-1;
1128                 }
1129                 my $desc="Book Returned ".$iteminfo->{'barcode'};
1130                 $usth = $dbh->prepare("insert into accountlines
1131                         (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding)
1132                         values (?,?,now(),?,?,'CR',?)");
1133                 $usth->execute($data->{'borrowernumber'},$nextaccntno,0-$amount,$desc,$amountleft);
1134                 $usth->finish;
1135                 $usth = $dbh->prepare("insert into accountoffsets
1136                         (borrowernumber, accountno, offsetaccount,  offsetamount)
1137                         values (?,?,?,?)");
1138                 $usth->execute($borrower->{'borrowernumber'},$data->{'accountno'},$nextaccntno,$offset);
1139                 $usth->finish;
1140                 $usth = $dbh->prepare("update items set paidfor='' where itemnumber=?");
1141                 $usth->execute($itm);
1142                 $usth->finish;
1143         }
1144         $sth->finish;
1145         return;
1146 }
1147
1148 =head2 fixoverdueonreturn
1149
1150         &fixoverdueonreturn($brn,$itm);
1151
1152 ??
1153
1154 C<$brn> borrowernumber
1155
1156 C<$itm> itemnumber
1157
1158 =cut
1159
1160 sub fixoverduesonreturn {
1161         my ($brn, $itm) = @_;
1162         my $dbh = C4::Context->dbh;
1163         # check for overdue fine
1164         my $sth = $dbh->prepare("select * from accountlines where (borrowernumber = ?) and (itemnumber = ?) and (accounttype='FU' or accounttype='O')");
1165         $sth->execute($brn,$itm);
1166         # alter fine to show that the book has been returned
1167         if (my $data = $sth->fetchrow_hashref) {
1168                 my $usth=$dbh->prepare("update accountlines set accounttype='F' where (borrowernumber = ?) and (itemnumber = ?) and (acccountno = ?)");
1169                 $usth->execute($brn,$itm,$data->{'accountno'});
1170                 $usth->finish();
1171         }
1172         $sth->finish();
1173         return;
1174 }
1175
1176 # Not exported
1177 #
1178 # NOTE!: If you change this function, be sure to update the POD for
1179 # &getpatroninformation.
1180 #
1181 # $flags = &patronflags($env, $patron, $dbh);
1182 #
1183 # $flags->{CHARGES}
1184 #               {message}       Message showing patron's credit or debt
1185 #               {noissues}      Set if patron owes >$5.00
1186 #         {GNA}                 Set if patron gone w/o address
1187 #               {message}       "Borrower has no valid address"
1188 #               {noissues}      Set.
1189 #         {LOST}                Set if patron's card reported lost
1190 #               {message}       Message to this effect
1191 #               {noissues}      Set.
1192 #         {DBARRED}             Set is patron is debarred
1193 #               {message}       Message to this effect
1194 #               {noissues}      Set.
1195 #         {NOTES}               Set if patron has notes
1196 #               {message}       Notes about patron
1197 #         {ODUES}               Set if patron has overdue books
1198 #               {message}       "Yes"
1199 #               {itemlist}      ref-to-array: list of overdue books
1200 #               {itemlisttext}  Text list of overdue items
1201 #         {WAITING}             Set if there are items available that the
1202 #                               patron reserved
1203 #               {message}       Message to this effect
1204 #               {itemlist}      ref-to-array: list of available items
1205 sub patronflags {
1206 # Original subroutine for Circ2.pm
1207         my %flags;
1208         my ($env, $patroninformation, $dbh) = @_;
1209         my $amount = checkaccount($env, $patroninformation->{'borrowernumber'}, $dbh);
1210         if ($amount > 0) {
1211                 my %flaginfo;
1212                 my $noissuescharge = C4::Context->preference("noissuescharge");
1213                 $flaginfo{'message'}= sprintf "Patron owes \$%.02f", $amount;
1214                 if ($amount > $noissuescharge) {
1215                 $flaginfo{'noissues'} = 1;
1216                 }
1217                 $flags{'CHARGES'} = \%flaginfo;
1218         } elsif ($amount < 0){
1219         my %flaginfo;
1220         $flaginfo{'message'} = sprintf "Patron has credit of \$%.02f", -$amount;
1221                 $flags{'CHARGES'} = \%flaginfo;
1222         }
1223         if ($patroninformation->{'gonenoaddress'} == 1) {
1224                 my %flaginfo;
1225                 $flaginfo{'message'} = 'Borrower has no valid address.';
1226                 $flaginfo{'noissues'} = 1;
1227                 $flags{'GNA'} = \%flaginfo;
1228         }
1229         if ($patroninformation->{'lost'} == 1) {
1230                 my %flaginfo;
1231                 $flaginfo{'message'} = 'Borrower\'s card reported lost.';
1232                 $flaginfo{'noissues'} = 1;
1233                 $flags{'LOST'} = \%flaginfo;
1234         }
1235         if ($patroninformation->{'debarred'} == 1) {
1236                 my %flaginfo;
1237                 $flaginfo{'message'} = 'Borrower is Debarred.';
1238                 $flaginfo{'noissues'} = 1;
1239                 $flags{'DBARRED'} = \%flaginfo;
1240         }
1241         if ($patroninformation->{'borrowernotes'}) {
1242                 my %flaginfo;
1243                 $flaginfo{'message'} = "$patroninformation->{'borrowernotes'}";
1244                 $flags{'NOTES'} = \%flaginfo;
1245         }
1246         my ($odues, $itemsoverdue)
1247                         = checkoverdues($env, $patroninformation->{'borrowernumber'}, $dbh);
1248         if ($odues > 0) {
1249                 my %flaginfo;
1250                 $flaginfo{'message'} = "Yes";
1251                 $flaginfo{'itemlist'} = $itemsoverdue;
1252                 foreach (sort {$a->{'date_due'} cmp $b->{'date_due'}} @$itemsoverdue) {
1253                 $flaginfo{'itemlisttext'}.="$_->{'date_due'} $_->{'barcode'} $_->{'title'} \n";
1254                 }
1255                 $flags{'ODUES'} = \%flaginfo;
1256         }
1257         my ($nowaiting, $itemswaiting)
1258                         = CheckWaiting($patroninformation->{'borrowernumber'});
1259         if ($nowaiting > 0) {
1260                 my %flaginfo;
1261                 $flaginfo{'message'} = "Reserved items available";
1262                 $flaginfo{'itemlist'} = $itemswaiting;
1263                 $flags{'WAITING'} = \%flaginfo;
1264         }
1265         return(\%flags);
1266 }
1267
1268
1269 # Not exported
1270 sub checkoverdues {
1271 # From Main.pm, modified to return a list of overdueitems, in addition to a count
1272   #checks whether a borrower has overdue items
1273         my ($env, $bornum, $dbh)=@_;
1274         my @datearr = localtime;
1275         my $today = ($datearr[5] + 1900)."-".($datearr[4]+1)."-".$datearr[3];
1276         my @overdueitems;
1277         my $count = 0;
1278         my $sth = $dbh->prepare("SELECT * FROM issues,biblio,biblioitems,items
1279                         WHERE items.biblioitemnumber = biblioitems.biblioitemnumber
1280                                 AND items.biblionumber     = biblio.biblionumber
1281                                 AND issues.itemnumber      = items.itemnumber
1282                                 AND issues.borrowernumber  = ?
1283                                 AND issues.returndate is NULL
1284                                 AND issues.date_due < ?");
1285         $sth->execute($bornum,$today);
1286         while (my $data = $sth->fetchrow_hashref) {
1287         push (@overdueitems, $data);
1288         $count++;
1289         }
1290         $sth->finish;
1291         return ($count, \@overdueitems);
1292 }
1293
1294 # Not exported
1295 sub currentborrower {
1296 # Original subroutine for Circ2.pm
1297         my ($itemnumber) = @_;
1298         my $dbh = C4::Context->dbh;
1299         my $q_itemnumber = $dbh->quote($itemnumber);
1300         my $sth=$dbh->prepare("select borrowers.borrowernumber from
1301         issues,borrowers where issues.itemnumber=$q_itemnumber and
1302         issues.borrowernumber=borrowers.borrowernumber and issues.returndate is
1303         NULL");
1304         $sth->execute;
1305         my ($borrower) = $sth->fetchrow;
1306         return($borrower);
1307 }
1308
1309 # FIXME - Not exported, but used in 'updateitem.pl' anyway.
1310 sub checkreserve_to_delete {
1311 # Stolen from Main.pm
1312 # Check for reserves for biblio
1313         my ($env,$dbh,$itemnum)=@_;
1314         my $resbor = "";
1315         my $sth = $dbh->prepare("select * from reserves,items
1316         where (items.itemnumber = ?)
1317         and (reserves.cancellationdate is NULL)
1318         and (items.biblionumber = reserves.biblionumber)
1319         and ((reserves.found = 'W')
1320         or (reserves.found is null))
1321         order by priority");
1322         $sth->execute($itemnum);
1323         my $resrec;
1324         my $data=$sth->fetchrow_hashref;
1325         while ($data && $resbor eq '') {
1326         $resrec=$data;
1327         my $const = $data->{'constrainttype'};
1328         if ($const eq "a") {
1329         $resbor = $data->{'borrowernumber'};
1330         } else {
1331         my $found = 0;
1332         my $csth = $dbh->prepare("select * from reserveconstraints,items
1333                 where (borrowernumber=?)
1334                 and reservedate=?
1335                 and reserveconstraints.biblionumber=?
1336                 and (items.itemnumber=? and
1337                 items.biblioitemnumber = reserveconstraints.biblioitemnumber)");
1338         $csth->execute($data->{'borrowernumber'},$data->{'biblionumber'},$data->{'reservedate'},$itemnum);
1339         if (my $cdata=$csth->fetchrow_hashref) {$found = 1;}
1340         if ($const eq 'o') {
1341                 if ($found eq 1) {$resbor = $data->{'borrowernumber'};}
1342         } else {
1343                 if ($found eq 0) {$resbor = $data->{'borrowernumber'};}
1344         }
1345         $csth->finish();
1346         }
1347         $data=$sth->fetchrow_hashref;
1348         }
1349         $sth->finish;
1350         return ($resbor,$resrec);
1351 }
1352
1353 =head2 currentissues
1354
1355   $issues = &currentissues($env, $borrower);
1356
1357 Returns a list of books currently on loan to a patron.
1358
1359 If C<$env-E<gt>{todaysissues}> is set and true, C<&currentissues> only
1360 returns information about books issued today. If
1361 C<$env-E<gt>{nottodaysissues}> is set and true, C<&currentissues> only
1362 returns information about books issued before today. If both are
1363 specified, C<$env-E<gt>{todaysissues}> is ignored. If neither is
1364 specified, C<&currentissues> returns all of the patron's issues.
1365
1366 C<$borrower->{borrowernumber}> is the borrower number of the patron
1367 whose issues we want to list.
1368
1369 C<&currentissues> returns a PHP-style array: C<$issues> is a
1370 reference-to-hash whose keys are integers in the range 1...I<n>, where
1371 I<n> is the number of items on issue (either today or before today).
1372 C<$issues-E<gt>{I<n>}> is a reference-to-hash whose keys are all of
1373 the fields of the biblio, biblioitems, items, and issues fields of the
1374 Koha database for that particular item.
1375
1376 =cut
1377
1378 #'
1379 sub currentissues {
1380 # New subroutine for Circ2.pm
1381         my ($env, $borrower) = @_;
1382         my $dbh = C4::Context->dbh;
1383         my %currentissues;
1384         my $counter=1;
1385         my $borrowernumber = $borrower->{'borrowernumber'};
1386         my $crit='';
1387
1388         # Figure out whether to get the books issued today, or earlier.
1389         # FIXME - $env->{todaysissues} and $env->{nottodaysissues} can
1390         # both be specified, but are mutually-exclusive. This is bogus.
1391         # Make this a flag. Or better yet, return everything in (reverse)
1392         # chronological order and let the caller figure out which books
1393         # were issued today.
1394         if ($env->{'todaysissues'}) {
1395                 # FIXME - Could use
1396                 #       $today = POSIX::strftime("%Y%m%d", localtime);
1397                 # FIXME - Since $today will be used in either case, move it
1398                 # out of the two if-blocks.
1399                 my @datearr = localtime(time());
1400                 my $today = (1900+$datearr[5]).sprintf "%02d", ($datearr[4]+1).sprintf "%02d", $datearr[3];
1401                 # FIXME - MySQL knows about dates. Just use
1402                 #       and issues.timestamp = curdate();
1403                 $crit=" and issues.timestamp like '$today%' ";
1404         }
1405         if ($env->{'nottodaysissues'}) {
1406                 # FIXME - Could use
1407                 #       $today = POSIX::strftime("%Y%m%d", localtime);
1408                 # FIXME - Since $today will be used in either case, move it
1409                 # out of the two if-blocks.
1410                 my @datearr = localtime(time());
1411                 my $today = (1900+$datearr[5]).sprintf "%02d", ($datearr[4]+1).sprintf "%02d", $datearr[3];
1412                 # FIXME - MySQL knows about dates. Just use
1413                 #       and issues.timestamp < curdate();
1414                 $crit=" and !(issues.timestamp like '$today%') ";
1415         }
1416
1417         # FIXME - Does the caller really need every single field from all
1418         # four tables?
1419         my $sth=$dbh->prepare("select * from issues,items,biblioitems,biblio where
1420         borrowernumber=? and issues.itemnumber=items.itemnumber and
1421         items.biblionumber=biblio.biblionumber and
1422         items.biblioitemnumber=biblioitems.biblioitemnumber and returndate is null
1423         $crit order by issues.date_due");
1424         $sth->execute($borrowernumber);
1425         while (my $data = $sth->fetchrow_hashref) {
1426                 # FIXME - The Dewey code is a string, not a number.
1427                 $data->{'dewey'}=~s/0*$//;
1428                 ($data->{'dewey'} == 0) && ($data->{'dewey'}='');
1429                 # FIXME - Could use
1430                 #       $todaysdate = POSIX::strftime("%Y%m%d", localtime)
1431                 # or better yet, just reuse $today which was calculated above.
1432                 # This function isn't going to run until midnight, is it?
1433                 # Alternately, use
1434                 #       $todaysdate = POSIX::strftime("%Y-%m-%d", localtime)
1435                 #       if ($data->{'date_due'} lt $todaysdate)
1436                 #               ...
1437                 # Either way, the date should be be formatted outside of the
1438                 # loop.
1439                 my @datearr = localtime(time());
1440                 my $todaysdate = (1900+$datearr[5]).sprintf ("%0.2d", ($datearr[4]+1)).sprintf ("%0.2d", $datearr[3]);
1441                 my $datedue=$data->{'date_due'};
1442                 $datedue=~s/-//g;
1443                 if ($datedue < $todaysdate) {
1444                         $data->{'overdue'}=1;
1445                 }
1446                 my $itemnumber=$data->{'itemnumber'};
1447                 # FIXME - Consecutive integers as hash keys? You have GOT to
1448                 # be kidding me! Use an array, fercrissakes!
1449                 $currentissues{$counter}=$data;
1450                 $counter++;
1451         }
1452         $sth->finish;
1453         return(\%currentissues);
1454 }
1455
1456 =head2 getissues
1457
1458   $issues = &getissues($borrowernumber);
1459
1460 Returns the set of books currently on loan to a patron.
1461
1462 C<$borrowernumber> is the patron's borrower number.
1463
1464 C<&getissues> returns a PHP-style array: C<$issues> is a
1465 reference-to-hash whose keys are integers in the range 0..I<n>-1,
1466 where I<n> is the number of books the patron currently has on loan.
1467
1468 The values of C<$issues> are references-to-hash whose keys are
1469 selected fields from the issues, items, biblio, and biblioitems tables
1470 of the Koha database.
1471
1472 =cut
1473 #'
1474 sub getissues {
1475 # New subroutine for Circ2.pm
1476         my ($borrower) = @_;
1477         my $dbh = C4::Context->dbh;
1478         my $borrowernumber = $borrower->{'borrowernumber'};
1479         my %currentissues;
1480         my $select = "SELECT items.*,issues.timestamp      AS timestamp,
1481                                 issues.date_due       AS date_due,
1482                                 items.barcode         AS barcode,
1483                                 biblio.title          AS title,
1484                                 biblio.author         AS author,
1485                                 biblioitems.dewey     AS dewey,
1486                                 itemtypes.description AS itemtype,
1487                                 biblioitems.subclass  AS subclass,
1488                                 biblioitems.classification AS classification
1489                         FROM issues,items,biblioitems,biblio, itemtypes
1490                         WHERE issues.borrowernumber  = ?
1491                         AND issues.itemnumber      = items.itemnumber
1492                         AND items.biblionumber     = biblio.biblionumber
1493                         AND items.biblioitemnumber = biblioitems.biblioitemnumber
1494                         AND itemtypes.itemtype     = biblioitems.itemtype
1495                         AND issues.returndate      IS NULL
1496                         ORDER BY issues.date_due";
1497         #    print $select;
1498         my $sth=$dbh->prepare($select);
1499         $sth->execute($borrowernumber);
1500         my $counter = 0;
1501         while (my $data = $sth->fetchrow_hashref) {
1502                 $data->{'dewey'} =~ s/0*$//;
1503                 ($data->{'dewey'} == 0) && ($data->{'dewey'} = '');
1504                         # FIXME - The Dewey code is a string, not a number.
1505                 # FIXME - Use POSIX::strftime to get a text version of today's
1506                 # date. That's what it's for.
1507                 # FIXME - Move the date calculation outside of the loop.
1508                 my @datearr = localtime(time());
1509                 my $todaysdate = (1900+$datearr[5]).sprintf ("%0.2d", ($datearr[4]+1)).sprintf ("%0.2d", $datearr[3]);
1510
1511                 # FIXME - Instead of converting the due date to YYYYMMDD, just
1512                 # use
1513                 #       $todaysdate = POSIX::strftime("%Y-%m-%d", localtime);
1514                 #       ...
1515                 #       if ($date->{date_due} lt $todaysdate)
1516                 my $datedue = $data->{'date_due'};
1517                 $datedue =~ s/-//g;
1518                 if ($datedue < $todaysdate) {
1519                         $data->{'overdue'} = 1;
1520                 }
1521                 $currentissues{$counter} = $data;
1522                 $counter++;
1523                         # FIXME - This is ludicrous. If you want to return an
1524                         # array of values, just use an array. That's what
1525                         # they're there for.
1526         }
1527         $sth->finish;
1528         return(\%currentissues);
1529 }
1530
1531 # Not exported
1532 sub checkwaiting {
1533 #Stolen from Main.pm
1534 # check for reserves waiting
1535         my ($env,$dbh,$bornum)=@_;
1536         my @itemswaiting;
1537         my $sth = $dbh->prepare("select * from reserves where (borrowernumber = ?) and (reserves.found='W') and cancellationdate is NULL");
1538         $sth->execute($bornum);
1539         my $cnt=0;
1540         if (my $data=$sth->fetchrow_hashref) {
1541                 $itemswaiting[$cnt] =$data;
1542                 $cnt ++
1543         }
1544         $sth->finish;
1545         return ($cnt,\@itemswaiting);
1546 }
1547
1548 =head2 renewstatus
1549
1550   $ok = &renewstatus($env, $dbh, $borrowernumber, $itemnumber);
1551
1552 Find out whether a borrowed item may be renewed.
1553
1554 C<$env> is ignored.
1555
1556 C<$dbh> is a DBI handle to the Koha database.
1557
1558 C<$borrowernumber> is the borrower number of the patron who currently
1559 has the item on loan.
1560
1561 C<$itemnumber> is the number of the item to renew.
1562
1563 C<$renewstatus> returns a true value iff the item may be renewed. The
1564 item must currently be on loan to the specified borrower; renewals
1565 must be allowed for the item's type; and the borrower must not have
1566 already renewed the loan.
1567
1568 =cut
1569
1570 sub renewstatus {
1571         # check renewal status
1572         my ($env,$bornum,$itemno)=@_;
1573         my $dbh = C4::Context->dbh;
1574         my $renews = 1;
1575         my $renewokay = 0;
1576         # Look in the issues table for this item, lent to this borrower,
1577         # and not yet returned.
1578         
1579         # FIXME - I think this function could be redone to use only one SQL call.
1580         my $sth1 = $dbh->prepare("select * from issues
1581                                                                 where (borrowernumber = ?)
1582                                                                 and (itemnumber = ?)
1583                                                                 and returndate is null");
1584         $sth1->execute($bornum,$itemno);
1585         if (my $data1 = $sth1->fetchrow_hashref) {
1586                 # Found a matching item
1587         
1588                 # See if this item may be renewed. This query is convoluted
1589                 # because it's a bit messy: given the item number, we need to find
1590                 # the biblioitem, which gives us the itemtype, which tells us
1591                 # whether it may be renewed.
1592                 my $sth2 = $dbh->prepare("select renewalsallowed from items,biblioitems,itemtypes
1593                 where (items.itemnumber = ?)
1594                 and (items.biblioitemnumber = biblioitems.biblioitemnumber)
1595                 and (biblioitems.itemtype = itemtypes.itemtype)");
1596                 $sth2->execute($itemno);
1597                 if (my $data2=$sth2->fetchrow_hashref) {
1598                         $renews = $data2->{'renewalsallowed'};
1599                 }
1600                 if ($renews > $data1->{'renewals'}) {
1601                         $renewokay = 1;
1602                 }
1603                 $sth2->finish;
1604         }
1605         $sth1->finish;
1606         return($renewokay);
1607 }
1608
1609 =head2 renewbook
1610
1611   &renewbook($env, $borrowernumber, $itemnumber, $datedue);
1612
1613 Renews a loan.
1614
1615 C<$env-E<gt>{branchcode}> is the code of the branch where the
1616 renewal is taking place.
1617
1618 C<$env-E<gt>{usercode}> is the value to log in C<statistics.usercode>
1619 in the Koha database.
1620
1621 C<$borrowernumber> is the borrower number of the patron who currently
1622 has the item.
1623
1624 C<$itemnumber> is the number of the item to renew.
1625
1626 C<$datedue> can be used to set the due date. If C<$datedue> is the
1627 empty string, C<&renewbook> will calculate the due date automatically
1628 from the book's item type. If you wish to set the due date manually,
1629 C<$datedue> should be in the form YYYY-MM-DD.
1630
1631 =cut
1632
1633 sub renewbook {
1634         # mark book as renewed
1635         my ($env,$bornum,$itemno,$datedue)=@_;
1636         my $dbh = C4::Context->dbh;
1637
1638         # If the due date wasn't specified, calculate it by adding the
1639         # book's loan length to today's date.
1640         if ($datedue eq "" ) {
1641                 #debug_msg($env, "getting date");
1642                 my $iteminformation = getiteminformation($env, $itemno,0);
1643                 my $borrower = getpatroninformation($env,$bornum,0);
1644                 my $loanlength = getLoanLength($borrower->{'categorycode'},$iteminformation->{'itemtype'},$borrower->{'branchcode'});
1645                 $datedue = UnixDate(DateCalc($iteminformation->{date_due},"$loanlength days"),"%Y-%m-%d");
1646         }
1647
1648         # Find the issues record for this book
1649         my $sth=$dbh->prepare("select * from issues where borrowernumber=? and itemnumber=? and returndate is null");
1650         $sth->execute($bornum,$itemno);
1651         my $issuedata=$sth->fetchrow_hashref;
1652         $sth->finish;
1653
1654         # Update the issues record to have the new due date, and a new count
1655         # of how many times it has been renewed.
1656         my $renews = $issuedata->{'renewals'} +1;
1657         $sth=$dbh->prepare("update issues set date_due = ?, renewals = ?
1658                 where borrowernumber=? and itemnumber=? and returndate is null");
1659         $sth->execute($datedue,$renews,$bornum,$itemno);
1660         $sth->finish;
1661
1662         # Log the renewal
1663         UpdateStats($env,$env->{'branchcode'},'renew','','',$itemno);
1664
1665         # Charge a new rental fee, if applicable?
1666         my ($charge,$type)=calc_charges($env, $itemno, $bornum);
1667         if ($charge > 0){
1668                 my $accountno=getnextacctno($env,$bornum,$dbh);
1669                 my $item=getiteminformation($env, $itemno);
1670                 $sth=$dbh->prepare("Insert into accountlines (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding,itemnumber)
1671                                                         values (?,?,now(),?,?,?,?,?)");
1672                 $sth->execute($bornum,$accountno,$charge,"Renewal of Rental Item $item->{'title'} $item->{'barcode'}",'Rent',$charge,$itemno);
1673                 $sth->finish;
1674         #     print $account;
1675         }
1676         
1677         #  return();
1678 }
1679
1680
1681
1682 =item calc_charges
1683
1684   ($charge, $item_type) = &calc_charges($env, $itemnumber, $borrowernumber);
1685
1686 Calculate how much it would cost for a given patron to borrow a given
1687 item, including any applicable discounts.
1688
1689 C<$env> is ignored.
1690
1691 C<$itemnumber> is the item number of item the patron wishes to borrow.
1692
1693 C<$borrowernumber> is the patron's borrower number.
1694
1695 C<&calc_charges> returns two values: C<$charge> is the rental charge,
1696 and C<$item_type> is the code for the item's item type (e.g., C<VID>
1697 if it's a video).
1698
1699 =cut
1700
1701 sub calc_charges {
1702         # calculate charges due
1703         my ($env, $itemno, $bornum)=@_;
1704         my $charge=0;
1705         my $dbh = C4::Context->dbh;
1706         my $item_type;
1707         
1708         # Get the book's item type and rental charge (via its biblioitem).
1709         my $sth1= $dbh->prepare("select itemtypes.itemtype,rentalcharge from items,biblioitems,itemtypes
1710                                                                 where (items.itemnumber =?)
1711                                                                 and (biblioitems.biblioitemnumber = items.biblioitemnumber)
1712                                                                 and (biblioitems.itemtype = itemtypes.itemtype)");
1713         $sth1->execute($itemno);
1714         my $data1=$sth1->fetchrow_hashref;
1715         $item_type = $data1->{'itemtype'};
1716         $charge = $data1->{'rentalcharge'};
1717         $sth1->finish;
1718         return ($charge,$item_type);
1719 }
1720
1721
1722 # FIXME - A virtually identical function appears in
1723 # C4::Circulation::Issues. Pick one and stick with it.
1724 sub createcharge {
1725 #Stolen from Issues.pm
1726     my ($env,$dbh,$itemno,$bornum,$charge) = @_;
1727     my $nextaccntno = getnextacctno($env,$bornum,$dbh);
1728     my $sth = $dbh->prepare(<<EOT);
1729         INSERT INTO     accountlines
1730                         (borrowernumber, itemnumber, accountno,
1731                          date, amount, description, accounttype,
1732                          amountoutstanding)
1733         VALUES          (?, ?, ?,
1734                          now(), ?, 'Rental', 'Rent',
1735                          ?)
1736 EOT
1737     $sth->execute($bornum, $itemno, $nextaccntno, $charge, $charge);
1738     $sth->finish;
1739 }
1740
1741
1742 sub getnextacctno {
1743 # Stolen from Accounts.pm
1744     my ($env,$bornumber,$dbh)=@_;
1745     my $nextaccntno = 1;
1746     my $sth = $dbh->prepare("select * from accountlines where (borrowernumber = ?) order by accountno desc");
1747     $sth->execute($bornumber);
1748     if (my $accdata=$sth->fetchrow_hashref){
1749         $nextaccntno = $accdata->{'accountno'} + 1;
1750     }
1751     $sth->finish;
1752     return($nextaccntno);
1753 }
1754
1755 =item find_reserves
1756
1757   ($status, $record) = &find_reserves($itemnumber);
1758
1759 Looks up an item in the reserves.
1760
1761 C<$itemnumber> is the itemnumber to look up.
1762
1763 C<$status> is true iff the search was successful.
1764
1765 C<$record> is a reference-to-hash describing the reserve. Its keys are
1766 the fields from the reserves table of the Koha database.
1767
1768 =cut
1769 #'
1770 # FIXME - This API is bogus: just return the record, or undef if none
1771 # was found.
1772 # FIXME - There's also a &C4::Circulation::Returns::find_reserves, but
1773 # that one looks rather different.
1774 sub find_reserves {
1775 # Stolen from Returns.pm
1776     my ($itemno) = @_;
1777     my %env;
1778     my $dbh = C4::Context->dbh;
1779     my ($itemdata) = getiteminformation(\%env, $itemno,0);
1780     my $bibno = $dbh->quote($itemdata->{'biblionumber'});
1781     my $bibitm = $dbh->quote($itemdata->{'biblioitemnumber'});
1782     my $sth = $dbh->prepare("select * from reserves where ((found = 'W') or (found is null)) and biblionumber = ? and cancellationdate is NULL order by priority, reservedate");
1783     $sth->execute($bibno);
1784     my $resfound = 0;
1785     my $resrec;
1786     my $lastrec;
1787 # print $query;
1788
1789     # FIXME - I'm not really sure what's going on here, but since we
1790     # only want one result, wouldn't it be possible (and far more
1791     # efficient) to do something clever in SQL that only returns one
1792     # set of values?
1793     while (($resrec = $sth->fetchrow_hashref) && (not $resfound)) {
1794                 # FIXME - Unlike Pascal, Perl allows you to exit loops
1795                 # early. Take out the "&& (not $resfound)" and just
1796                 # use "last" at the appropriate point in the loop.
1797                 # (Oh, and just in passing: if you'd used "!" instead
1798                 # of "not", you wouldn't have needed the parentheses.)
1799         $lastrec = $resrec;
1800         my $brn = $dbh->quote($resrec->{'borrowernumber'});
1801         my $rdate = $dbh->quote($resrec->{'reservedate'});
1802         my $bibno = $dbh->quote($resrec->{'biblionumber'});
1803         if ($resrec->{'found'} eq "W") {
1804             if ($resrec->{'itemnumber'} eq $itemno) {
1805                 $resfound = 1;
1806             }
1807         } else {
1808             # FIXME - Use 'elsif' to avoid unnecessary indentation.
1809             if ($resrec->{'constrainttype'} eq "a") {
1810                 $resfound = 1;
1811             } else {
1812                         my $consth = $dbh->prepare("select * from reserveconstraints where borrowernumber = ? and reservedate = ? and biblionumber = ? and biblioitemnumber = ?");
1813                         $consth->execute($brn,$rdate,$bibno,$bibitm);
1814                         if (my $conrec = $consth->fetchrow_hashref) {
1815                                 if ($resrec->{'constrainttype'} eq "o") {
1816                                 $resfound = 1;
1817                                 }
1818                         }
1819                 $consth->finish;
1820                 }
1821         }
1822         if ($resfound) {
1823             my $updsth = $dbh->prepare("update reserves set found = 'W', itemnumber = ? where borrowernumber = ? and reservedate = ? and biblionumber = ?");
1824             $updsth->execute($itemno,$brn,$rdate,$bibno);
1825             $updsth->finish;
1826             # FIXME - "last;" here to break out of the loop early.
1827         }
1828     }
1829     $sth->finish;
1830     return ($resfound,$lastrec);
1831 }
1832
1833 sub fixdate {
1834     my ($year, $month, $day) = @_;
1835     my $invalidduedate;
1836     my $date;
1837     if (($year eq 0) && ($month eq 0) && ($year eq 0)) {
1838 #       $env{'datedue'}='';
1839     } else {
1840         if (($year eq 0) || ($month eq 0) || ($year eq 0)) {
1841             $invalidduedate=1;
1842         } else {
1843             if (($day>30) && (($month==4) || ($month==6) || ($month==9) || ($month==11))) {
1844                 $invalidduedate = 1;
1845             } elsif (($day > 29) && ($month == 2)) {
1846                 $invalidduedate=1;
1847             } elsif (($month == 2) && ($day > 28) && (($year%4) && ((!($year%100) || ($year%400))))) {
1848                 $invalidduedate=1;
1849             } else {
1850                 $date="$year-$month-$day";
1851             }
1852         }
1853     }
1854     return ($date, $invalidduedate);
1855 }
1856
1857 1;
1858 __END__
1859
1860 =back
1861
1862 =head1 AUTHOR
1863
1864 Koha Developement team <info@koha.org>
1865
1866 =cut