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