2 # Please use 8-character tabs for this file (indents are every 4 characters)
4 package C4::Circulation::Circ2;
8 #package to deal with Returns
9 #written 3/11/99 by olwen@katipo.co.nz
12 # Copyright 2000-2002 Katipo Communications
14 # This file is part of Koha.
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
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.
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
38 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
40 # set the version for version checking
45 C4::Circulation::Circ2 - Koha circulation module
49 use C4::Circulation::Circ2;
53 The functions in this module deal with circulation, issues, and
54 returns, as well as general information about the library.
63 @EXPORT = qw(&getpatroninformation
64 ¤tissues &getissues &getiteminformation
65 &issuebook &returnbook &find_reserves &transferbook &decode
68 # &getbranches &getprinters &getbranch &getprinter => moved to C4::Koha.pm
70 =item getpatroninformation
72 ($borrower, $flags) = &getpatroninformation($env, $borrowernumber,
75 Looks up a patron and returns information about him or her. If
76 C<$borrowernumber> is true (nonzero), C<&getpatroninformation> looks
77 up the borrower by number; otherwise, it looks up the borrower by card
80 C<$env> is effectively ignored, but should be a reference-to-hash.
82 C<$borrower> is a reference-to-hash whose keys are the fields of the
83 borrowers table in the Koha database. In addition,
84 C<$borrower-E<gt>{flags}> is the same as C<$flags>.
86 C<$flags> is a reference-to-hash giving more detailed information
87 about the patron. Its keys act as flags: if they are set, then the key
88 is a reference-to-hash that gives further details:
90 if (exists($flags->{LOST}))
92 # Patron's card was reported lost
93 print $flags->{LOST}{message}, "\n";
96 Each flag has a C<message> key, giving a human-readable explanation of
97 the flag. If the state of a flag means that the patron should not be
98 allowed to borrow any more books, then it will have a C<noissues> key
101 The possible flags are:
107 Shows the patron's credit or debt, if any.
111 (Gone, no address.) Set if the patron has left without giving a
116 Set if the patron's card has been reported as lost.
120 Set if the patron has been debarred.
124 Any additional notes about the patron.
128 Set if the patron has overdue items. This flag has several keys:
130 C<$flags-E<gt>{ODUES}{itemlist}> is a reference-to-array listing the
131 overdue items. Its elements are references-to-hash, each describing an
132 overdue item. The keys are selected fields from the issues, biblio,
133 biblioitems, and items tables of the Koha database.
135 C<$flags-E<gt>{ODUES}{itemlist}> is a string giving a text listing of
136 the overdue items, one per line.
140 Set if any items that the patron has reserved are available.
142 C<$flags-E<gt>{WAITING}{itemlist}> is a reference-to-array listing the
143 available items. Each element is a reference-to-hash whose keys are
144 fields from the reserves table of the Koha database.
150 sub getpatroninformation {
152 my ($env, $borrowernumber,$cardnumber) = @_;
153 my $dbh = C4::Context->dbh;
156 if ($borrowernumber) {
157 $sth = $dbh->prepare("select * from borrowers where borrowernumber=?");
158 $sth->execute($borrowernumber);
159 } elsif ($cardnumber) {
160 $sth = $dbh->prepare("select * from borrowers where cardnumber=?");
161 $sth->execute($cardnumber);
163 $env->{'apierror'} = "invalid borrower information passed to getpatroninformation subroutine";
166 $env->{'mess'} = $query;
167 my $borrower = $sth->fetchrow_hashref;
168 my $amount = checkaccount($env, $borrowernumber, $dbh);
169 $borrower->{'amountoutstanding'} = $amount;
170 my $flags = patronflags($env, $borrower, $dbh);
173 $sth=$dbh->prepare("select bit,flag from userflags");
175 while (my ($bit, $flag) = $sth->fetchrow) {
176 if ($borrower->{'flags'} & 2**$bit) {
177 $accessflagshash->{$flag}=1;
181 $borrower->{'flags'}=$flags;
182 return ($borrower, $flags, $accessflagshash);
187 $str = &decode($chunk);
189 Decodes a segment of a string emitted by a CueCat barcode scanner and
194 # FIXME - At least, I'm pretty sure this is for decoding CueCat stuff.
197 my $seq = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-';
198 my @s = map { index($seq,$_); } split(//,$encoded);
213 my $n = (($s[0] << 6 | $s[1]) << 6 | $s[2]) << 6 | $s[3];
214 $r .=chr(($n >> 16) ^ 67) .
215 chr(($n >> 8 & 255) ^ 67) .
216 chr(($n & 255) ^ 67);
219 $r = substr($r,0,length($r)-$l);
223 =item getiteminformation
225 $item = &getiteminformation($env, $itemnumber, $barcode);
227 Looks up information about an item, given either its item number or
228 its barcode. If C<$itemnumber> is a nonzero value, it is used;
229 otherwise, C<$barcode> is used.
231 C<$env> is effectively ignored, but should be a reference-to-hash.
233 C<$item> is a reference-to-hash whose keys are fields from the biblio,
234 items, and biblioitems tables of the Koha database. It may also
235 contain the following keys:
241 The due date on this item, if it has been borrowed and not returned
242 yet. The date is in YYYY-MM-DD format.
246 The length of time for which the item can be borrowed, in days.
250 True if the item may not be borrowed.
256 sub getiteminformation {
257 # returns a hash of item information given either the itemnumber or the barcode
258 my ($env, $itemnumber, $barcode) = @_;
259 my $dbh = C4::Context->dbh;
262 $sth=$dbh->prepare("select * from biblio,items,biblioitems where items.itemnumber=? and biblio.biblionumber=items.biblionumber and biblioitems.biblioitemnumber = items.biblioitemnumber");
263 $sth->execute($itemnumber);
265 $sth=$dbh->prepare("select * from biblio,items,biblioitems where items.barcode=? and biblio.biblionumber=items.biblionumber and biblioitems.biblioitemnumber = items.biblioitemnumber");
266 $sth->execute($barcode);
268 $env->{'apierror'}="getiteminformation() subroutine must be called with either an itemnumber or a barcode";
272 my $iteminformation=$sth->fetchrow_hashref;
274 # FIXME - Style: instead of putting the entire rest of the
275 # function in a block, just say
276 # return undef unless $iteminformation;
277 # That way, the rest of the function needn't be indented as much.
278 if ($iteminformation) {
279 $sth=$dbh->prepare("select date_due from issues where itemnumber=? and isnull(returndate)");
280 $sth->execute($iteminformation->{'itemnumber'});
281 my ($date_due) = $sth->fetchrow;
282 $iteminformation->{'date_due'}=$date_due;
284 # FIXME - The Dewey code is a string, not a number. Besides,
285 # "000" is a perfectly valid Dewey code.
286 #$iteminformation->{'dewey'}=~s/0*$//;
287 ($iteminformation->{'dewey'} == 0) && ($iteminformation->{'dewey'}='');
288 # FIXME - fetchrow_hashref is documented as being inefficient.
289 # Perhaps this should be rewritten as
290 # $sth = $dbh->prepare("select loanlength, notforloan ...");
292 # ($iteminformation->{loanlength},
293 # $iteminformation->{notforloan}) = fetchrow_array;
294 $sth=$dbh->prepare("select * from itemtypes where itemtype=?");
295 $sth->execute($iteminformation->{'itemtype'});
296 my $itemtype=$sth->fetchrow_hashref;
297 $iteminformation->{'loanlength'}=$itemtype->{'loanlength'};
298 # if specific item notforloan, don't use itemtype notforloan field.
299 # otherwise, use itemtype notforloan value to see if item can be issued.
300 $iteminformation->{'notforloan'}=$itemtype->{'notforloan'} unless $iteminformation->{'notforloan'};
303 return($iteminformation);
308 ($dotransfer, $messages, $iteminformation) =
309 &transferbook($newbranch, $barcode, $ignore_reserves);
311 Transfers an item to a new branch. If the item is currently on loan,
312 it is automatically returned before the actual transfer.
314 C<$newbranch> is the code for the branch to which the item should be
317 C<$barcode> is the barcode of the item to be transferred.
319 If C<$ignore_reserves> is true, C<&transferbook> ignores reserves.
320 Otherwise, if an item is reserved, the transfer fails.
322 Returns three values:
324 C<$dotransfer> is true iff the transfer was successful.
326 C<$messages> is a reference-to-hash which may have any of the
333 There is no item in the catalog with the given barcode. The value is
338 The item's home branch is permanent. This doesn't prevent the item
339 from being transferred, though. The value is the code of the item's
342 =item C<DestinationEqualsHolding>
344 The item is already at the branch to which it is being transferred.
345 The transfer is nonetheless considered to have failed. The value
350 The item was on loan, and C<&transferbook> automatically returned it
351 before transferring it. The value is the borrower number of the patron
356 The item was reserved. The value is a reference-to-hash whose keys are
357 fields from the reserves table of the Koha database, and
358 C<biblioitemnumber>. It also has the key C<ResFound>, whose value is
359 either C<Waiting> or C<Reserved>.
361 =item C<WasTransferred>
363 The item was eligible to be transferred. Barring problems
364 communicating with the database, the transfer should indeed have
365 succeeded. The value should be ignored.
371 # FIXME - This function tries to do too much, and its API is clumsy.
372 # If it didn't also return books, it could be used to change the home
373 # branch of a book while the book is on loan.
375 # Is there any point in returning the item information? The caller can
376 # look that up elsewhere if ve cares.
378 # This leaves the ($dotransfer, $messages) tuple. This seems clumsy.
379 # If the transfer succeeds, that's all the caller should need to know.
380 # Thus, this function could simply return 1 or 0 to indicate success
381 # or failure, and set $C4::Circulation::Circ2::errmsg in case of
382 # failure. Or this function could return undef if successful, and an
383 # error message in case of failure (this would feel more like C than
386 # transfer book code....
387 my ($tbr, $barcode, $ignoreRs) = @_;
391 my $branches = getbranches();
392 my $iteminformation = getiteminformation(\%env, 0, $barcode);
394 if (not $iteminformation) {
395 $messages->{'BadBarcode'} = $barcode;
398 # get branches of book...
399 my $hbr = $iteminformation->{'homebranch'};
400 my $fbr = $iteminformation->{'holdingbranch'};
402 if ($branches->{$hbr}->{'PE'}) {
403 $messages->{'IsPermanent'} = $hbr;
405 # can't transfer book if is already there....
406 # FIXME - Why not? Shouldn't it trivially succeed?
408 $messages->{'DestinationEqualsHolding'} = 1;
411 # check if it is still issued to someone, return it...
412 my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
413 if ($currentborrower) {
414 returnbook($barcode, $fbr);
415 $messages->{'WasReturned'} = $currentborrower;
418 # FIXME - Don't call &CheckReserves unless $ignoreRs is true.
419 # That'll save a database query.
420 my ($resfound, $resrec) = CheckReserves($iteminformation->{'itemnumber'});
421 if ($resfound and not $ignoreRs) {
422 $resrec->{'ResFound'} = $resfound;
423 $messages->{'ResFound'} = $resrec;
426 #actually do the transfer....
428 dotransfer($iteminformation->{'itemnumber'}, $fbr, $tbr);
429 $messages->{'WasTransfered'} = 1;
431 return ($dotransfer, $messages, $iteminformation);
435 # FIXME - This is only used in &transferbook. Why bother making it a
438 my ($itm, $fbr, $tbr) = @_;
439 my $dbh = C4::Context->dbh;
440 $itm = $dbh->quote($itm);
441 $fbr = $dbh->quote($fbr);
442 $tbr = $dbh->quote($tbr);
443 #new entry in branchtransfers....
444 $dbh->do("INSERT INTO branchtransfers (itemnumber, frombranch, datearrived, tobranch)
445 VALUES ($itm, $fbr, now(), $tbr)");
446 #update holdingbranch in items .....
447 $dbh->do("UPDATE items SET datelastseen = now(), holdingbranch = $tbr WHERE items.itemnumber = $itm");
453 ($iteminformation, $datedue, $rejected, $question, $questionnumber,
454 $defaultanswer, $message) =
455 &issuebook($env, $patroninformation, $barcode, $responses, $date);
457 Issue a book to a patron.
459 C<$env-E<gt>{usercode}> will be used in the usercode field of the
460 statistics table of the Koha database when this transaction is
463 C<$env-E<gt>{datedue}>, if given, specifies the date on which the book
464 is due back. This should be a string of the form "YYYY-MM-DD".
466 C<$env-E<gt>{branchcode}> is the code of the branch where this
467 transaction is taking place.
469 C<$patroninformation> is a reference-to-hash giving information about
470 the person borrowing the book. This is the first value returned by
471 C<&getpatroninformation>.
473 C<$barcode> is the bar code of the book being issued.
475 C<$responses> is a reference-to-hash. It represents the answers to the
476 questions asked by the C<$question>, C<$questionnumber>, and
477 C<$defaultanswer> return values (see below). The keys are numbers, and
478 the values can be "Y" or "N".
480 C<$date> is an optional date in the form "YYYY-MM-DD". If specified,
481 then only fines and charges up to that date will be considered when
482 checking to see whether the patron owes too much money to be lent a
485 C<&issuebook> returns an array of seven values:
487 C<$iteminformation> is a reference-to-hash describing the item just
488 issued. This in a form similar to that returned by
489 C<&getiteminformation>.
491 C<$datedue> is a string giving the date when the book is due, in the
494 C<$rejected> is either a string, or -1. If it is defined and is a
495 string, then the book may not be issued, and C<$rejected> gives the
496 reason for this. If C<$rejected> is -1, then the book may not be
497 issued, but no reason is given.
499 If there is a problem or question (e.g., the book is reserved for
500 another patron), then C<$question>, C<$questionnumber>, and
501 C<$defaultanswer> will be set. C<$questionnumber> indicates the
502 problem. C<$question> is a text string asking how to resolve the
503 problem, as a yes-or-no question, and C<$defaultanswer> is either "Y"
504 or "N", giving the default answer. The questions, their numbers, and
509 =item 1: "Issued to <name>. Mark as returned?" (Y)
511 =item 2: "Waiting for <patron> at <branch>. Allow issue?" (N)
513 =item 3: "Cancel reserve for <patron>?" (N)
515 =item 4: "Book is issued to this borrower. Renew?" (Y)
517 =item 5: "Reserved for <patron> at <branch> since <date>. Allow issue?" (N)
519 =item 6: "Set reserve for <patron> to waiting and transfer to <branch>?" (Y)
521 This is asked if the answer to question 5 was "N".
523 =item 7: "Cancel reserve for <patron>?" (N)
527 C<$message>, if defined, is an additional information message, e.g., a
532 # FIXME - The business with $responses is absurd. For one thing, these
533 # questions should have names, not numbers. For another, it'd be
534 # better to have the last argument be %extras. Then scripts can call
538 # -mark_returned => 0,
539 # -cancel_reserve => 1,
542 # and the script can use
543 # if (defined($extras{"-mark_returned"}) && $extras{"-mark_returned"})
544 # Heck, the $date argument should go in there as well.
546 # Also, there might be several reasons why a book can't be issued, but
547 # this API only supports asking one question at a time. Perhaps it'd
548 # be better to return a ref-to-list of problem IDs. Then the calling
549 # script can display a list of all of the problems at once.
551 # Is it this function's place to decide the default answer to the
552 # various questions? Why not document the various problems and allow
553 # the caller to decide?
555 my ($env, $patroninformation, $barcode, $responses, $date) = @_;
556 my $dbh = C4::Context->dbh;
557 my $iteminformation = getiteminformation($env, 0, $barcode);
559 my ($rejected,$question,$defaultanswer,$questionnumber, $noissue);
562 # See if there's any reason this book shouldn't be issued to this
564 SWITCH: { # FIXME - Yes, we know it's a switch. Tell us what it's for.
565 if ($patroninformation->{'gonenoaddress'}) {
566 $rejected="Patron is gone, with no known address.";
569 if ($patroninformation->{'lost'}) {
570 $rejected="Patron's card has been reported lost.";
573 if ($patroninformation->{'debarred'}) {
574 $rejected="Patron is Debarred";
577 my $amount = checkaccount($env,$patroninformation->{'borrowernumber'}, $dbh,$date);
578 # FIXME - "5" shouldn't be hardcoded. An Italian library might
579 # be generous enough to lend a book to a patron even if he
580 # does still owe them 5 lire.
581 if ($amount > 5 && $patroninformation->{'categorycode'} ne 'L' &&
582 $patroninformation->{'categorycode'} ne 'W' &&
583 $patroninformation->{'categorycode'} ne 'I' &&
584 $patroninformation->{'categorycode'} ne 'B' &&
585 $patroninformation->{'categorycode'} ne 'P') {
586 # FIXME - What do these category codes mean?
587 $rejected = sprintf "Patron owes \$%.02f.", $amount;
590 # FIXME - This sort of error-checking should be placed closer
591 # to the test; in this case, this error-checking should be
592 # done immediately after the call to &getiteminformation.
593 unless ($iteminformation) {
594 $rejected = "$barcode is not a valid barcode.";
597 if ($iteminformation->{'notforloan'} == 1) {
598 $rejected="Item not for loan.";
601 if ($iteminformation->{'wthdrawn'} == 1) {
602 $rejected="Item withdrawn.";
605 if ($iteminformation->{'restricted'} == 1) {
606 $rejected="Restricted item.";
609 if ($iteminformation->{'itemtype'} eq 'REF') {
610 $rejected="Reference item: Not for loan.";
613 my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
614 if ($currentborrower eq $patroninformation->{'borrowernumber'}) {
615 # Already issued to current borrower. Ask whether the loan should
617 my ($renewstatus) = renewstatus($env,$dbh,$patroninformation->{'borrowernumber'}, $iteminformation->{'itemnumber'});
618 if ($renewstatus == 0) {
619 $rejected="No more renewals allowed for this item.";
622 if ($responses->{4} eq '') {
624 $question = "Book is issued to this borrower.\nRenew?";
625 $defaultanswer = 'Y';
627 } elsif ($responses->{4} eq 'Y') {
628 my ($charge,$itemtype) = calc_charges($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'});
630 createcharge($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'}, $charge);
631 $iteminformation->{'charge'} = $charge;
633 &UpdateStats($env,$env->{'branchcode'},'renew',$charge,'',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'},$patroninformation->{'borrowernumber'});
634 renewbook($env,$dbh, $patroninformation->{'borrowernumber'}, $iteminformation->{'itemnumber'});
637 $rejected="Item on issue to this borrower, and you have chosen not to renew";
641 } elsif ($currentborrower ne '') {
642 # This book is currently on loan, but not to the person
643 # who wants to borrow it now.
644 my ($currborrower, $cbflags) = getpatroninformation($env,$currentborrower,0);
645 if ($responses->{1} eq '') {
647 $question = "Issued to $currborrower->{'firstname'} $currborrower->{'surname'} ($currborrower->{'cardnumber'}).\nMark as returned?";
650 } elsif ($responses->{1} eq 'Y') {
651 returnbook($iteminformation->{'barcode'}, $env->{'branchcode'});
658 # See if the item is on reserve.
659 my ($restype, $res) = CheckReserves($iteminformation->{'itemnumber'});
661 my $resbor = $res->{'borrowernumber'};
662 if ($resbor eq $patroninformation->{'borrowernumber'}) {
663 # The item is on reserve to the current patron
665 } elsif ($restype eq "Waiting") {
666 # The item is on reserve and waiting, but has been
667 # reserved by some other patron.
668 my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
669 my $branches = getbranches();
670 my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
671 if ($responses->{2} eq '' && $responses->{3} eq '') {
673 # FIXME - Assumes HTML
674 $question="<font color=red>Waiting</font> for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}) at $branchname \nAllow issue?";
677 } elsif ($responses->{2} eq 'N') {
678 $rejected="Issue cancelled";
681 if ($responses->{3} eq '') {
683 $question="Cancel reserve for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})?";
686 } elsif ($responses->{3} eq 'Y') {
687 CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
691 } elsif ($restype eq "Reserved") {
692 # The item is on reserve for someone else.
693 my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
694 my $branches = getbranches();
695 my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
696 if ($responses->{5} eq '' && $responses->{7} eq '') {
698 $question="Reserved for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}) since $res->{'reservedate'} \nAllow issue?";
700 if ($responses->{6} eq 'Y') {
701 my $tobrcd = ReserveWaiting($res->{'itemnumber'}, $res->{'borrowernumber'});
702 transferbook($tobrcd,$barcode, 1);
703 $message = "Item should now be waiting at $branchname";
706 } elsif ($responses->{5} eq 'N') {
707 if ($responses->{6} eq '') {
709 $question="Set reserve for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}) to waiting and transfer to $branchname?";
711 } elsif ($responses->{6} eq 'Y') {
712 my $tobrcd = ReserveWaiting($res->{'itemnumber'}, $res->{'borrowernumber'});
713 transferbook($tobrcd, $barcode, 1);
714 $message = "Item should now be waiting at $branchname";
719 if ($responses->{7} eq '') {
721 $question="Cancel reserve for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})?";
724 } elsif ($responses->{7} eq 'Y') {
725 CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
732 unless (($question) || ($rejected) || ($noissue)) {
733 # There's no reason why the item can't be issued.
734 # FIXME - my $loanlength = $iteminformation->{loanlength} || 21;
736 if ($iteminformation->{'loanlength'}) {
737 $loanlength=$iteminformation->{'loanlength'};
739 my $ti=time; # FIXME - Never used
740 my $datedue=time+($loanlength)*86400;
741 # FIXME - Could just use POSIX::strftime("%Y-%m-%d", localtime);
742 # That's what it's for. Or, in this case:
743 # $dateduef = $env->{datedue} ||
744 # strftime("%Y-%m-%d", localtime(time +
745 # $loanlength * 86400));
746 my @datearr = localtime($datedue);
747 $dateduef = (1900+$datearr[5])."-".($datearr[4]+1)."-".$datearr[3];
748 if ($env->{'datedue'}) {
749 $dateduef=$env->{'datedue'};
751 $dateduef=~ s/2001\-4\-25/2001\-4\-26/;
752 # FIXME - What's this for? Leftover from debugging?
754 # Record in the database the fact that the book was issued.
755 my $sth=$dbh->prepare("insert into issues (borrowernumber, itemnumber, date_due, branchcode) values (?,?,?,?)");
756 $sth->execute($patroninformation->{'borrowernumber'}, $iteminformation->{'itemnumber'}, $dateduef, $env->{'branchcode'});
758 $iteminformation->{'issues'}++;
759 $sth=$dbh->prepare("update items set issues=?,datelastseen=now() where itemnumber=?");
760 $sth->execute($iteminformation->{'issues'},$iteminformation->{'itemnumber'});
762 # If it costs to borrow this book, charge it to the patron's account.
763 my ($charge,$itemtype)=calc_charges($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'});
765 createcharge($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'}, $charge);
766 $iteminformation->{'charge'}=$charge;
768 # Record the fact that this book was issued.
769 &UpdateStats($env,$env->{'branchcode'},'issue',$charge,'',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'},$patroninformation->{'borrowernumber'});
772 if ($iteminformation->{'charge'}) {
773 $message=sprintf "Rental charge of \$%.02f applies.", $iteminformation->{'charge'};
775 return ($iteminformation, $dateduef, $rejected, $question, $questionnumber, $defaultanswer, $message);
782 ($doreturn, $messages, $iteminformation, $borrower) =
783 &returnbook($barcode, $branch);
787 C<$barcode> is the bar code of the book being returned. C<$branch> is
788 the code of the branch where the book is being returned.
790 C<&returnbook> returns a list of four items:
792 C<$doreturn> is true iff the return succeeded.
794 C<$messages> is a reference-to-hash giving the reason for failure:
800 No item with this barcode exists. The value is C<$barcode>.
804 The book is not currently on loan. The value is C<$barcode>.
808 The book's home branch is a permanent collection. If you have borrowed
809 this book, you are not allowed to return it. The value is the code for
810 the book's home branch.
814 This book has been withdrawn/cancelled. The value should be ignored.
818 The item was reserved. The value is a reference-to-hash whose keys are
819 fields from the reserves table of the Koha database, and
820 C<biblioitemnumber>. It also has the key C<ResFound>, whose value is
821 either C<Waiting>, C<Reserved>, or 0.
825 C<$borrower> is a reference-to-hash, giving information about the
826 patron who last borrowed the book.
830 # FIXME - This API is bogus. There's no need to return $borrower and
831 # $iteminformation; the caller can ask about those separately, if it
832 # cares (it'd be inefficient to make two database calls instead of
833 # one, but &getpatroninformation and &getiteminformation can be
834 # memoized if this is an issue).
836 # The ($doreturn, $messages) tuple is redundant: if the return
837 # succeeded, that's all the caller needs to know. So &returnbook can
838 # return 1 and 0 on success and failure, and set
839 # $C4::Circulation::Circ2::errmsg to indicate the error. Or it can
840 # return undef for success, and an error message on error (though this
841 # is more C-ish than Perl-ish).
843 my ($barcode, $branch) = @_;
847 die '$branch not defined' unless defined $branch; # just in case (bug 170)
848 # get information on item
849 my ($iteminformation) = getiteminformation(\%env, 0, $barcode);
850 if (not $iteminformation) {
851 $messages->{'BadBarcode'} = $barcode;
855 my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
856 if ((not $currentborrower) && $doreturn) {
857 $messages->{'NotIssued'} = $barcode;
860 # check if the book is in a permanent collection....
861 my $hbr = $iteminformation->{'homebranch'};
862 my $branches = getbranches();
863 if ($branches->{$hbr}->{'PE'}) {
864 $messages->{'IsPermanent'} = $hbr;
866 # check that the book has been cancelled
867 if ($iteminformation->{'wthdrawn'}) {
868 $messages->{'wthdrawn'} = 1;
871 # update issues, thereby returning book (should push this out into another subroutine
872 my ($borrower) = getpatroninformation(\%env, $currentborrower, 0);
874 doreturn($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
875 $messages->{'WasReturned'} = 1; # FIXME is the "= 1" right?
877 ($borrower) = getpatroninformation(\%env, $currentborrower, 0);
878 # transfer book to the current branch
879 my ($transfered, $mess, $item) = transferbook($branch, $barcode, 1);
881 $messages->{'WasTransfered'} = 1; # FIXME is the "= 1" right?
883 # fix up the accounts.....
884 if ($iteminformation->{'itemlost'}) {
885 # Mark the item as not being lost.
886 updateitemlost($iteminformation->{'itemnumber'});
887 fixaccountforlostandreturned($iteminformation, $borrower);
888 $messages->{'WasLost'} = 1; # FIXME is the "= 1" right?
890 # fix up the overdues in accounts...
891 fixoverduesonreturn($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
893 my ($resfound, $resrec) = CheckReserves($iteminformation->{'itemnumber'});
895 # my $tobrcd = ReserveWaiting($resrec->{'itemnumber'}, $resrec->{'borrowernumber'});
896 $resrec->{'ResFound'} = $resfound;
897 $messages->{'ResFound'} = $resrec;
900 # Record the fact that this book was returned.
901 UpdateStats(\%env, $branch ,'return','0','',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'},$borrower->{'borrowernumber'});
902 return ($doreturn, $messages, $iteminformation, $borrower);
906 # Takes a borrowernumber and an itemnuber.
907 # Updates the 'issues' table to mark the item as returned (assuming
908 # that it's currently on loan to the given borrower. Otherwise, the
909 # item remains on loan.
910 # Updates items.datelastseen for the item.
912 # FIXME - This is only used in &returnbook. Why make it into a
913 # separate function? (is this a recognizable step in the return process? - acli)
915 my ($brn, $itm) = @_;
916 my $dbh = C4::Context->dbh;
917 my $sth = $dbh->prepare("update issues set returndate = now() where (borrowernumber = ?)
918 and (itemnumber = ?) and (returndate is null)");
919 $sth->execute($brn,$itm);
921 $sth=$dbh->prepare("update items set datelastseen=now() where itemnumber=?");
928 # Marks an item as not being lost.
932 my $dbh = C4::Context->dbh;
934 my $sth = $dbh->prepare("UPDATE items SET itemlost = 0 WHERE itemnumber =?");
935 $sth->execute($itemno);
940 sub fixaccountforlostandreturned {
941 my ($iteminfo, $borrower) = @_;
943 my $dbh = C4::Context->dbh;
944 my $itm = $iteminfo->{'itemnumber'};
945 # check for charge made for lost book
946 my $sth = $dbh->prepare("select * from accountlines where (itemnumber = ?)
947 and (accounttype='L' or accounttype='Rep') order by date desc");
949 if (my $data = $sth->fetchrow_hashref) {
950 # writeoff this amount
952 my $amount = $data->{'amount'};
953 my $acctno = $data->{'accountno'};
955 if ($data->{'amountoutstanding'} == $amount) {
956 $offset = $data->{'amount'};
959 $offset = $amount - $data->{'amountoutstanding'};
960 $amountleft = $data->{'amountoutstanding'} - $amount;
962 my $usth = $dbh->prepare("update accountlines set accounttype = 'LR',amountoutstanding='0'
963 where (borrowernumber = ?)
964 and (itemnumber = ?) and (accountno = ?) ");
965 $usth->execute($data->{'borrowernumber'},$itm,$acctno);
967 #check if any credit is left if so writeoff other accounts
968 my $nextaccntno = getnextacctno(\%env,$data->{'borrowernumber'},$dbh);
969 if ($amountleft < 0){
972 if ($amountleft > 0){
973 my $msth = $dbh->prepare("select * from accountlines where (borrowernumber = ?)
974 and (amountoutstanding >0) order by date");
975 $msth->execute($data->{'borrowernumber'});
976 # offset transactions
979 while (($accdata=$msth->fetchrow_hashref) and ($amountleft>0)){
980 if ($accdata->{'amountoutstanding'} < $amountleft) {
982 $amountleft -= $accdata->{'amountoutstanding'};
984 $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
987 my $thisacct = $accdata->{'accountno'};
988 my $usth = $dbh->prepare("update accountlines set amountoutstanding= ?
989 where (borrowernumber = ?)
991 $usth->execute($newamtos,$data->{'borrowernumber'},'$thisacct');
993 $usth = $dbh->prepare("insert into accountoffsets
994 (borrowernumber, accountno, offsetaccount, offsetamount)
997 $usth->execute($data->{'borrowernumber'},$accdata->{'accountno'},$nextaccntno,$newamtos);
1002 if ($amountleft > 0){
1005 my $desc="Book Returned ".$iteminfo->{'barcode'};
1006 $usth = $dbh->prepare("insert into accountlines
1007 (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding)
1008 values (?,?,now(),?,?,'CR',?)");
1009 $usth->execute($data->{'borrowernumber'},$nextaccntno,0-$amount,$desc,$amountleft);
1011 $usth = $dbh->prepare("insert into accountoffsets
1012 (borrowernumber, accountno, offsetaccount, offsetamount)
1014 $usth->execute($borrower->{'borrowernumber'},$data->{'accountno'},$nextaccntno,$offset);
1016 $usth = $dbh->prepare("update items set paidfor='' where itemnumber=?");
1017 $usth->execute($itm);
1025 sub fixoverduesonreturn {
1026 my ($brn, $itm) = @_;
1027 my $dbh = C4::Context->dbh;
1028 # check for overdue fine
1029 my $sth = $dbh->prepare("select * from accountlines where (borrowernumber = ?) and (itemnumber = ?) and (accounttype='FU' or accounttype='O')");
1030 $sth->execute($brn,$itm);
1031 # alter fine to show that the book has been returned
1032 if (my $data = $sth->fetchrow_hashref) {
1033 my $usth=$dbh->prepare("update accountlines set accounttype='F' where (borrowernumber = ?) and (itemnumber = ?) and (acccountno = ?)");
1034 $usth->execute($brn,$itm,$data->{'accountno'});
1043 # NOTE!: If you change this function, be sure to update the POD for
1044 # &getpatroninformation.
1046 # $flags = &patronflags($env, $patron, $dbh);
1049 # {message} Message showing patron's credit or debt
1050 # {noissues} Set if patron owes >$5.00
1051 # {GNA} Set if patron gone w/o address
1052 # {message} "Borrower has no valid address"
1054 # {LOST} Set if patron's card reported lost
1055 # {message} Message to this effect
1057 # {DBARRED} Set is patron is debarred
1058 # {message} Message to this effect
1060 # {NOTES} Set if patron has notes
1061 # {message} Notes about patron
1062 # {ODUES} Set if patron has overdue books
1064 # {itemlist} ref-to-array: list of overdue books
1065 # {itemlisttext} Text list of overdue items
1066 # {WAITING} Set if there are items available that the
1068 # {message} Message to this effect
1069 # {itemlist} ref-to-array: list of available items
1071 # Original subroutine for Circ2.pm
1073 my ($env, $patroninformation, $dbh) = @_;
1074 my $amount = checkaccount($env, $patroninformation->{'borrowernumber'}, $dbh);
1077 my $noissuescharge = C4::Context->preference("noissuescharge");
1078 $flaginfo{'message'}= sprintf "Patron owes \$%.02f", $amount;
1079 if ($amount > $noissuescharge) {
1080 $flaginfo{'noissues'} = 1;
1082 $flags{'CHARGES'} = \%flaginfo;
1083 } elsif ($amount < 0){
1085 $flaginfo{'message'} = sprintf "Patron has credit of \$%.02f", -$amount;
1086 $flags{'CHARGES'} = \%flaginfo;
1088 if ($patroninformation->{'gonenoaddress'} == 1) {
1090 $flaginfo{'message'} = 'Borrower has no valid address.';
1091 $flaginfo{'noissues'} = 1;
1092 $flags{'GNA'} = \%flaginfo;
1094 if ($patroninformation->{'lost'} == 1) {
1096 $flaginfo{'message'} = 'Borrower\'s card reported lost.';
1097 $flaginfo{'noissues'} = 1;
1098 $flags{'LOST'} = \%flaginfo;
1100 if ($patroninformation->{'debarred'} == 1) {
1102 $flaginfo{'message'} = 'Borrower is Debarred.';
1103 $flaginfo{'noissues'} = 1;
1104 $flags{'DBARRED'} = \%flaginfo;
1106 if ($patroninformation->{'borrowernotes'}) {
1108 $flaginfo{'message'} = "$patroninformation->{'borrowernotes'}";
1109 $flags{'NOTES'} = \%flaginfo;
1111 my ($odues, $itemsoverdue)
1112 = checkoverdues($env, $patroninformation->{'borrowernumber'}, $dbh);
1115 $flaginfo{'message'} = "Yes";
1116 $flaginfo{'itemlist'} = $itemsoverdue;
1117 foreach (sort {$a->{'date_due'} cmp $b->{'date_due'}} @$itemsoverdue) {
1118 $flaginfo{'itemlisttext'}.="$_->{'date_due'} $_->{'barcode'} $_->{'title'} \n";
1120 $flags{'ODUES'} = \%flaginfo;
1122 my ($nowaiting, $itemswaiting)
1123 = CheckWaiting($patroninformation->{'borrowernumber'});
1124 if ($nowaiting > 0) {
1126 $flaginfo{'message'} = "Reserved items available";
1127 $flaginfo{'itemlist'} = $itemswaiting;
1128 $flags{'WAITING'} = \%flaginfo;
1136 # From Main.pm, modified to return a list of overdueitems, in addition to a count
1137 #checks whether a borrower has overdue items
1138 my ($env, $bornum, $dbh)=@_;
1139 my @datearr = localtime;
1140 my $today = ($datearr[5] + 1900)."-".($datearr[4]+1)."-".$datearr[3];
1143 my $sth = $dbh->prepare("SELECT * FROM issues,biblio,biblioitems,items
1144 WHERE items.biblioitemnumber = biblioitems.biblioitemnumber
1145 AND items.biblionumber = biblio.biblionumber
1146 AND issues.itemnumber = items.itemnumber
1147 AND issues.borrowernumber = ?
1148 AND issues.returndate is NULL
1149 AND issues.date_due < ?");
1150 $sth->execute($bornum,$today);
1151 while (my $data = $sth->fetchrow_hashref) {
1152 push (@overdueitems, $data);
1156 return ($count, \@overdueitems);
1160 sub currentborrower {
1161 # Original subroutine for Circ2.pm
1162 my ($itemnumber) = @_;
1163 my $dbh = C4::Context->dbh;
1164 my $q_itemnumber = $dbh->quote($itemnumber);
1165 my $sth=$dbh->prepare("select borrowers.borrowernumber from
1166 issues,borrowers where issues.itemnumber=$q_itemnumber and
1167 issues.borrowernumber=borrowers.borrowernumber and issues.returndate is
1170 my ($borrower) = $sth->fetchrow;
1174 # FIXME - Not exported, but used in 'updateitem.pl' anyway.
1176 # Stolen from Main.pm
1177 # Check for reserves for biblio
1178 my ($env,$dbh,$itemnum)=@_;
1180 my $sth = $dbh->prepare("select * from reserves,items
1181 where (items.itemnumber = ?)
1182 and (reserves.cancellationdate is NULL)
1183 and (items.biblionumber = reserves.biblionumber)
1184 and ((reserves.found = 'W')
1185 or (reserves.found is null))
1186 order by priority");
1187 $sth->execute($itemnum);
1189 my $data=$sth->fetchrow_hashref;
1190 while ($data && $resbor eq '') {
1192 my $const = $data->{'constrainttype'};
1193 if ($const eq "a") {
1194 $resbor = $data->{'borrowernumber'};
1197 my $csth = $dbh->prepare("select * from reserveconstraints,items
1198 where (borrowernumber=?)
1200 and reserveconstraints.biblionumber=?
1201 and (items.itemnumber=? and
1202 items.biblioitemnumber = reserveconstraints.biblioitemnumber)");
1203 $csth->execute($data->{'borrowernumber'},$data->{'biblionumber'},$data->{'reservedate'},$itemnum);
1204 if (my $cdata=$csth->fetchrow_hashref) {$found = 1;}
1205 if ($const eq 'o') {
1206 if ($found eq 1) {$resbor = $data->{'borrowernumber'};}
1208 if ($found eq 0) {$resbor = $data->{'borrowernumber'};}
1212 $data=$sth->fetchrow_hashref;
1215 return ($resbor,$resrec);
1220 $issues = ¤tissues($env, $borrower);
1222 Returns a list of books currently on loan to a patron.
1224 If C<$env-E<gt>{todaysissues}> is set and true, C<¤tissues> only
1225 returns information about books issued today. If
1226 C<$env-E<gt>{nottodaysissues}> is set and true, C<¤tissues> only
1227 returns information about books issued before today. If both are
1228 specified, C<$env-E<gt>{todaysissues}> is ignored. If neither is
1229 specified, C<¤tissues> returns all of the patron's issues.
1231 C<$borrower->{borrowernumber}> is the borrower number of the patron
1232 whose issues we want to list.
1234 C<¤tissues> returns a PHP-style array: C<$issues> is a
1235 reference-to-hash whose keys are integers in the range 1...I<n>, where
1236 I<n> is the number of items on issue (either today or before today).
1237 C<$issues-E<gt>{I<n>}> is a reference-to-hash whose keys are all of
1238 the fields of the biblio, biblioitems, items, and issues fields of the
1239 Koha database for that particular item.
1244 # New subroutine for Circ2.pm
1245 my ($env, $borrower) = @_;
1246 my $dbh = C4::Context->dbh;
1249 my $borrowernumber = $borrower->{'borrowernumber'};
1252 # Figure out whether to get the books issued today, or earlier.
1253 # FIXME - $env->{todaysissues} and $env->{nottodaysissues} can
1254 # both be specified, but are mutually-exclusive. This is bogus.
1255 # Make this a flag. Or better yet, return everything in (reverse)
1256 # chronological order and let the caller figure out which books
1257 # were issued today.
1258 if ($env->{'todaysissues'}) {
1260 # $today = POSIX::strftime("%Y%m%d", localtime);
1261 # FIXME - Since $today will be used in either case, move it
1262 # out of the two if-blocks.
1263 my @datearr = localtime(time());
1264 my $today = (1900+$datearr[5]).sprintf "%02d", ($datearr[4]+1).sprintf "%02d", $datearr[3];
1265 # FIXME - MySQL knows about dates. Just use
1266 # and issues.timestamp = curdate();
1267 $crit=" and issues.timestamp like '$today%' ";
1269 if ($env->{'nottodaysissues'}) {
1271 # $today = POSIX::strftime("%Y%m%d", localtime);
1272 # FIXME - Since $today will be used in either case, move it
1273 # out of the two if-blocks.
1274 my @datearr = localtime(time());
1275 my $today = (1900+$datearr[5]).sprintf "%02d", ($datearr[4]+1).sprintf "%02d", $datearr[3];
1276 # FIXME - MySQL knows about dates. Just use
1277 # and issues.timestamp < curdate();
1278 $crit=" and !(issues.timestamp like '$today%') ";
1281 # FIXME - Does the caller really need every single field from all
1283 my $sth=$dbh->prepare("select * from issues,items,biblioitems,biblio where
1284 borrowernumber=? and issues.itemnumber=items.itemnumber and
1285 items.biblionumber=biblio.biblionumber and
1286 items.biblioitemnumber=biblioitems.biblioitemnumber and returndate is null
1287 $crit order by issues.date_due");
1288 $sth->execute($borrowernumber);
1289 while (my $data = $sth->fetchrow_hashref) {
1290 # FIXME - The Dewey code is a string, not a number.
1291 $data->{'dewey'}=~s/0*$//;
1292 ($data->{'dewey'} == 0) && ($data->{'dewey'}='');
1294 # $todaysdate = POSIX::strftime("%Y%m%d", localtime)
1295 # or better yet, just reuse $today which was calculated above.
1296 # This function isn't going to run until midnight, is it?
1298 # $todaysdate = POSIX::strftime("%Y-%m-%d", localtime)
1299 # if ($data->{'date_due'} lt $todaysdate)
1301 # Either way, the date should be be formatted outside of the
1303 my @datearr = localtime(time());
1304 my $todaysdate = (1900+$datearr[5]).sprintf ("%0.2d", ($datearr[4]+1)).sprintf ("%0.2d", $datearr[3]);
1305 my $datedue=$data->{'date_due'};
1307 if ($datedue < $todaysdate) {
1308 $data->{'overdue'}=1;
1310 my $itemnumber=$data->{'itemnumber'};
1311 # FIXME - Consecutive integers as hash keys? You have GOT to
1312 # be kidding me! Use an array, fercrissakes!
1313 $currentissues{$counter}=$data;
1317 return(\%currentissues);
1322 $issues = &getissues($borrowernumber);
1324 Returns the set of books currently on loan to a patron.
1326 C<$borrowernumber> is the patron's borrower number.
1328 C<&getissues> returns a PHP-style array: C<$issues> is a
1329 reference-to-hash whose keys are integers in the range 0..I<n>-1,
1330 where I<n> is the number of books the patron currently has on loan.
1332 The values of C<$issues> are references-to-hash whose keys are
1333 selected fields from the issues, items, biblio, and biblioitems tables
1334 of the Koha database.
1339 # New subroutine for Circ2.pm
1340 my ($borrower) = @_;
1341 my $dbh = C4::Context->dbh;
1342 my $borrowernumber = $borrower->{'borrowernumber'};
1344 my $select = "SELECT issues.timestamp AS timestamp,
1345 issues.date_due AS date_due,
1346 items.biblionumber AS biblionumber,
1347 items.itemnumber AS itemnumber,
1348 items.barcode AS barcode,
1349 biblio.title AS title,
1350 biblio.author AS author,
1351 biblioitems.dewey AS dewey,
1352 itemtypes.description AS itemtype,
1353 biblioitems.subclass AS subclass,
1354 biblioitems.classification AS classification
1355 FROM issues,items,biblioitems,biblio, itemtypes
1356 WHERE issues.borrowernumber = ?
1357 AND issues.itemnumber = items.itemnumber
1358 AND items.biblionumber = biblio.biblionumber
1359 AND items.biblioitemnumber = biblioitems.biblioitemnumber
1360 AND itemtypes.itemtype = biblioitems.itemtype
1361 AND issues.returndate IS NULL
1362 ORDER BY issues.date_due";
1364 my $sth=$dbh->prepare($select);
1365 $sth->execute($borrowernumber);
1367 while (my $data = $sth->fetchrow_hashref) {
1368 $data->{'dewey'} =~ s/0*$//;
1369 ($data->{'dewey'} == 0) && ($data->{'dewey'} = '');
1370 # FIXME - The Dewey code is a string, not a number.
1371 # FIXME - Use POSIX::strftime to get a text version of today's
1372 # date. That's what it's for.
1373 # FIXME - Move the date calculation outside of the loop.
1374 my @datearr = localtime(time());
1375 my $todaysdate = (1900+$datearr[5]).sprintf ("%0.2d", ($datearr[4]+1)).sprintf ("%0.2d", $datearr[3]);
1377 # FIXME - Instead of converting the due date to YYYYMMDD, just
1379 # $todaysdate = POSIX::strftime("%Y-%m-%d", localtime);
1381 # if ($date->{date_due} lt $todaysdate)
1382 my $datedue = $data->{'date_due'};
1384 if ($datedue < $todaysdate) {
1385 $data->{'overdue'} = 1;
1387 $currentissues{$counter} = $data;
1389 # FIXME - This is ludicrous. If you want to return an
1390 # array of values, just use an array. That's what
1391 # they're there for.
1394 return(\%currentissues);
1399 #Stolen from Main.pm
1400 # check for reserves waiting
1401 my ($env,$dbh,$bornum)=@_;
1403 my $sth = $dbh->prepare("select * from reserves where (borrowernumber = ?) and (reserves.found='W') and cancellationdate is NULL");
1404 $sth->execute($bornum);
1406 if (my $data=$sth->fetchrow_hashref) {
1407 $itemswaiting[$cnt] =$data;
1411 return ($cnt,\@itemswaiting);
1415 # FIXME - This is nearly-identical to &C4::Accounts::checkaccount
1417 # Stolen from Accounts.pm
1418 #take borrower number
1419 #check accounts and list amounts owing
1420 my ($env,$bornumber,$dbh,$date)=@_;
1421 my $select="SELECT SUM(amountoutstanding) AS total
1423 WHERE borrowernumber = ?
1424 AND amountoutstanding<>0";
1425 my @bind = ($bornumber);
1427 $select.=" AND date < ?";
1431 my $sth=$dbh->prepare($select);
1432 $sth->execute(@bind);
1433 my $data=$sth->fetchrow_hashref;
1434 my $total = $data->{'total'};
1436 # output(1,2,"borrower owes $total");
1438 # # output(1,2,"borrower owes $total");
1440 # reconcileaccount($env,$dbh,$bornumber,$total);
1447 # FIXME - This is identical to &C4::Circulation::Renewals::renewstatus.
1448 # Pick one and stick with it.
1450 # Stolen from Renewals.pm
1451 # check renewal status
1452 my ($env,$dbh,$bornum,$itemno)=@_;
1455 my $sth1 = $dbh->prepare("select * from issues
1456 where (borrowernumber = ?)
1457 and (itemnumber = ?)
1458 and returndate is null");
1459 $sth1->execute($bornum,$itemno);
1460 if (my $data1 = $sth1->fetchrow_hashref) {
1461 my $sth2 = $dbh->prepare("select renewalsallowed from items,biblioitems,itemtypes
1462 where (items.itemnumber = ?)
1463 and (items.biblioitemnumber = biblioitems.biblioitemnumber)
1464 and (biblioitems.itemtype = itemtypes.itemtype)");
1465 $sth2->execute($itemno);
1466 if (my $data2=$sth2->fetchrow_hashref) {
1467 $renews = $data2->{'renewalsallowed'};
1469 if ($renews > $data1->{'renewals'}) {
1479 # Stolen from Renewals.pm
1480 # mark book as renewed
1481 my ($env,$dbh,$bornum,$itemno,$datedue)=@_;
1482 $datedue=$env->{'datedue'};
1483 if ($datedue eq "" ) {
1485 my $sth=$dbh->prepare("Select * from biblioitems,items,itemtypes
1486 where (items.itemnumber = ?)
1487 and (biblioitems.biblioitemnumber = items.biblioitemnumber)
1488 and (biblioitems.itemtype = itemtypes.itemtype)");
1489 $sth->execute($itemno);
1490 if (my $data=$sth->fetchrow_hashref) {
1491 $loanlength = $data->{'loanlength'}
1495 my $datedu = time + ($loanlength * 86400);
1496 my @datearr = localtime($datedu);
1497 $datedue = (1900+$datearr[5])."-".($datearr[4]+1)."-".$datearr[3];
1499 my @date = split("-",$datedue);
1500 my $odatedue = ($date[2]+0)."-".($date[1]+0)."-".$date[0];
1501 my $sth=$dbh->prepare("select * from issues where borrowernumber=? and
1502 itemnumber=? and returndate is null");
1503 $sth->execute($bornum,$itemno);
1504 my $issuedata=$sth->fetchrow_hashref;
1506 my $renews = $issuedata->{'renewals'} +1;
1507 $sth=$dbh->prepare("update issues
1508 set date_due = ?, renewals = ?
1509 where borrowernumber=? and
1510 itemnumber=? and returndate is null");
1512 $sth->execute($datedue,$renews,$bornum,$itemno);
1517 # FIXME - This is almost, but not quite, identical to
1518 # &C4::Circulation::Issues::calc_charges and
1519 # &C4::Circulation::Renewals2::calc_charges.
1520 # Pick one and stick with it.
1522 # Stolen from Issues.pm
1523 # calculate charges due
1524 my ($env, $dbh, $itemno, $bornum)=@_;
1529 # open (FILE,">>/tmp/charges");
1531 my $sth1= $dbh->prepare("select itemtypes.itemtype,rentalcharge from items,biblioitems,itemtypes
1532 where (items.itemnumber =?)
1533 and (biblioitems.biblioitemnumber = items.biblioitemnumber)
1534 and (biblioitems.itemtype = itemtypes.itemtype)");
1535 # print FILE "$q1\n";
1536 $sth1->execute($itemno);
1537 if (my $data1=$sth1->fetchrow_hashref) {
1538 $item_type = $data1->{'itemtype'};
1539 $charge = $data1->{'rentalcharge'};
1540 # print FILE "charge is $charge\n";
1541 my $sth2=$dbh->prepare("select rentaldiscount from borrowers,categoryitem
1542 where (borrowers.borrowernumber = ?)
1543 and (borrowers.categorycode = categoryitem.categorycode)
1544 and (categoryitem.itemtype = ?)");
1546 $sth2->execute($bornum,$item_type);
1547 if (my $data2=$sth2->fetchrow_hashref) {
1548 my $discount = $data2->{'rentaldiscount'};
1549 # print FILE "discount is $discount";
1550 if ($discount eq 'NULL') {
1553 $charge = ($charge *(100 - $discount)) / 100;
1559 return ($charge, $item_type);
1562 # FIXME - A virtually identical function appears in
1563 # C4::Circulation::Issues. Pick one and stick with it.
1565 #Stolen from Issues.pm
1566 my ($env,$dbh,$itemno,$bornum,$charge) = @_;
1567 my $nextaccntno = getnextacctno($env,$bornum,$dbh);
1568 my $sth = $dbh->prepare(<<EOT);
1569 INSERT INTO accountlines
1570 (borrowernumber, itemnumber, accountno,
1571 date, amount, description, accounttype,
1574 now(), ?, 'Rental', 'Rent',
1577 $sth->execute($bornum, $itemno, $nextaccntno, $charge, $charge);
1583 # Stolen from Accounts.pm
1584 my ($env,$bornumber,$dbh)=@_;
1585 my $nextaccntno = 1;
1586 my $sth = $dbh->prepare("select * from accountlines where (borrowernumber = ?) order by accountno desc");
1587 $sth->execute($bornumber);
1588 if (my $accdata=$sth->fetchrow_hashref){
1589 $nextaccntno = $accdata->{'accountno'} + 1;
1592 return($nextaccntno);
1597 ($status, $record) = &find_reserves($itemnumber);
1599 Looks up an item in the reserves.
1601 C<$itemnumber> is the itemnumber to look up.
1603 C<$status> is true iff the search was successful.
1605 C<$record> is a reference-to-hash describing the reserve. Its keys are
1606 the fields from the reserves table of the Koha database.
1610 # FIXME - This API is bogus: just return the record, or undef if none
1612 # FIXME - There's also a &C4::Circulation::Returns::find_reserves, but
1613 # that one looks rather different.
1615 # Stolen from Returns.pm
1618 my $dbh = C4::Context->dbh;
1619 my ($itemdata) = getiteminformation(\%env, $itemno,0);
1620 my $bibno = $dbh->quote($itemdata->{'biblionumber'});
1621 my $bibitm = $dbh->quote($itemdata->{'biblioitemnumber'});
1622 my $sth = $dbh->prepare("select * from reserves where ((found = 'W') or (found is null)) and biblionumber = ? and cancellationdate is NULL order by priority, reservedate");
1623 $sth->execute($bibno);
1629 # FIXME - I'm not really sure what's going on here, but since we
1630 # only want one result, wouldn't it be possible (and far more
1631 # efficient) to do something clever in SQL that only returns one
1633 while (($resrec = $sth->fetchrow_hashref) && (not $resfound)) {
1634 # FIXME - Unlike Pascal, Perl allows you to exit loops
1635 # early. Take out the "&& (not $resfound)" and just
1636 # use "last" at the appropriate point in the loop.
1637 # (Oh, and just in passing: if you'd used "!" instead
1638 # of "not", you wouldn't have needed the parentheses.)
1640 my $brn = $dbh->quote($resrec->{'borrowernumber'});
1641 my $rdate = $dbh->quote($resrec->{'reservedate'});
1642 my $bibno = $dbh->quote($resrec->{'biblionumber'});
1643 if ($resrec->{'found'} eq "W") {
1644 if ($resrec->{'itemnumber'} eq $itemno) {
1648 # FIXME - Use 'elsif' to avoid unnecessary indentation.
1649 if ($resrec->{'constrainttype'} eq "a") {
1652 my $consth = $dbh->prepare("select * from reserveconstraints where borrowernumber = ? and reservedate = ? and biblionumber = ? and biblioitemnumber = ?");
1653 $consth->execute($brn,$rdate,$bibno,$bibitm);
1654 if (my $conrec = $consth->fetchrow_hashref) {
1655 if ($resrec->{'constrainttype'} eq "o") {
1663 my $updsth = $dbh->prepare("update reserves set found = 'W', itemnumber = ? where borrowernumber = ? and reservedate = ? and biblionumber = ?");
1664 $updsth->execute($itemno,$brn,$rdate,$bibno);
1666 # FIXME - "last;" here to break out of the loop early.
1670 return ($resfound,$lastrec);
1680 Koha Developement team <info@koha.org>