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