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
35 #use C4::InterfaceCDK;
36 #use C4::Circulation::Main;
37 #use C4::Circulation::Renewals;
44 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
46 # set the version for version checking
51 C4::Circulation::Circ2 - Koha circulation module
55 use C4::Circulation::Circ2;
59 The functions in this module deal with circulation, issues, and
60 returns, as well as general information about the library.
69 @EXPORT = qw(&getbranches &getprinters &getpatroninformation
70 &getbranch &getprinter
71 ¤tissues &getissues &getiteminformation &findborrower
72 &issuebook &returnbook &find_reserves &transferbook &decode
77 $branches = &getbranches();
78 @branch_codes = keys %$branches;
79 %main_branch_info = %{$branches->{"MAIN"}};
81 Returns information about existing library branches.
83 C<$branches> is a reference-to-hash. Its keys are the branch codes for
84 all of the existing library branches, and its values are
85 references-to-hash describing that particular branch.
87 In each branch description (C<%main_branch_info>, above), there is a
88 key for each field in the branches table of the Koha database. In
89 addition, there is a key for each branch category code to which the
90 branch belongs (the category codes are taken from the branchrelations
95 # FIXME - This function doesn't feel as if it belongs here. It should
96 # go in some generic or administrative module, not in circulation.
98 # returns a reference to a hash of references to branches...
100 my $dbh = C4::Context->dbh;
101 my $sth=$dbh->prepare("select * from branches");
103 while (my $branch=$sth->fetchrow_hashref) {
104 my $tmp = $branch->{'branchcode'}; my $brc = $dbh->quote($tmp);
105 # FIXME - my $brc = $dbh->quote($branch->{"branchcode"});
106 my $query = "select categorycode from branchrelations where branchcode = $brc";
107 my $nsth = $dbh->prepare($query);
109 while (my ($cat) = $nsth->fetchrow_array) {
110 # FIXME - This seems wrong. It ought to be
111 # $branch->{categorycodes}{$cat} = 1;
112 # otherwise, there's a namespace collision if there's a
113 # category with the same name as a field in the 'branches'
114 # table (i.e., don't create a category called "issuing").
115 # In addition, the current structure doesn't really allow
116 # you to list the categories that a branch belongs to:
117 # you'd have to list keys %$branch, and remove those keys
118 # that aren't fields in the "branches" table.
122 $branches{$branch->{'branchcode'}}=$branch;
129 $printers = &getprinters($env);
130 @queues = keys %$printers;
132 Returns information about existing printer queues.
136 C<$printers> is a reference-to-hash whose keys are the print queues
137 defined in the printers table of the Koha database. The values are
138 references-to-hash, whose keys are the fields in the printers table.
142 # FIXME - Perhaps this really belongs in C4::Print?
146 my $dbh = C4::Context->dbh;
147 my $sth=$dbh->prepare("select * from printers");
149 while (my $printer=$sth->fetchrow_hashref) {
150 $printers{$printer->{'printqueue'}}=$printer;
155 # FIXME - This function doesn't feel as if it belongs here. It should
156 # go in some generic or administrative module, not in circulation.
158 my($query, $branches) = @_; # get branch for this query from branches
159 my $branch = $query->param('branch');
160 ($branch) || ($branch = $query->cookie('branch'));
161 ($branches->{$branch}) || ($branch=(keys %$branches)[0]);
165 # FIXME - Perhaps this really belongs in C4::Print?
166 sub getprinter ($$) {
167 my($query, $printers) = @_; # get printer for this query from printers
168 my $printer = $query->param('printer');
169 ($printer) || ($printer = $query->cookie('printer'));
170 ($printers->{$printer}) || ($printer = (keys %$printers)[0]);
174 =item getpatroninformation
176 ($borrower, $flags) = &getpatroninformation($env, $borrowernumber,
179 Looks up a patron and returns information about him or her. If
180 C<$borrowernumber> is true (nonzero), C<&getpatroninformation> looks
181 up the borrower by number; otherwise, it looks up the borrower by card
184 C<$env> is effectively ignored, but should be a reference-to-hash.
186 C<$borrower> is a reference-to-hash whose keys are the fields of the
187 borrowers table in the Koha database. In addition,
188 C<$borrower-E<gt>{flags}> is the same as C<$flags>.
190 C<$flags> is a reference-to-hash giving more detailed information
191 about the patron. Its keys act as flags: if they are set, then the key
192 is a reference-to-hash that gives further details:
194 if (exists($flags->{LOST}))
196 # Patron's card was reported lost
197 print $flags->{LOST}{message}, "\n";
200 Each flag has a C<message> key, giving a human-readable explanation of
201 the flag. If the state of a flag means that the patron should not be
202 allowed to borrow any more books, then it will have a C<noissues> key
205 The possible flags are:
211 Shows the patron's credit or debt, if any.
215 (Gone, no address.) Set if the patron has left without giving a
220 Set if the patron's card has been reported as lost.
224 Set if the patron has been debarred.
228 Any additional notes about the patron.
232 Set if the patron has overdue items. This flag has several keys:
234 C<$flags-E<gt>{ODUES}{itemlist}> is a reference-to-array listing the
235 overdue items. Its elements are references-to-hash, each describing an
236 overdue item. The keys are selected fields from the issues, biblio,
237 biblioitems, and items tables of the Koha database.
239 C<$flags-E<gt>{ODUES}{itemlist}> is a string giving a text listing of
240 the overdue items, one per line.
244 Set if any items that the patron has reserved are available.
246 C<$flags-E<gt>{WAITING}{itemlist}> is a reference-to-array listing the
247 available items. Each element is a reference-to-hash whose keys are
248 fields from the reserves table of the Koha database.
254 sub getpatroninformation {
256 my ($env, $borrowernumber,$cardnumber) = @_;
257 my $dbh = C4::Context->dbh;
260 if ($borrowernumber) {
261 $query = "select * from borrowers where borrowernumber=$borrowernumber";
262 } elsif ($cardnumber) {
263 $query = "select * from borrowers where cardnumber=$cardnumber";
265 $env->{'apierror'} = "invalid borrower information passed to getpatroninformation subroutine";
268 $env->{'mess'} = $query;
269 $sth = $dbh->prepare($query);
271 my $borrower = $sth->fetchrow_hashref;
272 my $amount = checkaccount($env, $borrowernumber, $dbh);
273 $borrower->{'amountoutstanding'} = $amount;
274 my $flags = patronflags($env, $borrower, $dbh);
277 $sth=$dbh->prepare("select bit,flag from userflags");
279 while (my ($bit, $flag) = $sth->fetchrow) {
280 if ($borrower->{'flags'} & 2**$bit) {
281 $accessflagshash->{$flag}=1;
285 $borrower->{'flags'}=$flags;
286 return ($borrower, $flags, $accessflagshash);
291 $str = &decode($chunk);
293 Decodes a segment of a string emitted by a CueCat barcode scanner and
298 # FIXME - At least, I'm pretty sure this is for decoding CueCat stuff.
301 my $seq = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-';
302 my @s = map { index($seq,$_); } split(//,$encoded);
317 my $n = (($s[0] << 6 | $s[1]) << 6 | $s[2]) << 6 | $s[3];
318 $r .=chr(($n >> 16) ^ 67) .
319 chr(($n >> 8 & 255) ^ 67) .
320 chr(($n & 255) ^ 67);
323 $r = substr($r,0,length($r)-$l);
327 =item getiteminformation
329 $item = &getiteminformation($env, $itemnumber, $barcode);
331 Looks up information about an item, given either its item number or
332 its barcode. If C<$itemnumber> is a nonzero value, it is used;
333 otherwise, C<$barcode> is used.
335 C<$env> is effectively ignored, but should be a reference-to-hash.
337 C<$item> is a reference-to-hash whose keys are fields from the biblio,
338 items, and biblioitems tables of the Koha database. It may also
339 contain the following keys:
345 The due date on this item, if it has been borrowed and not returned
346 yet. The date is in YYYY-MM-DD format.
350 The length of time for which the item can be borrowed, in days.
354 True if the item may not be borrowed.
360 sub getiteminformation {
361 # returns a hash of item information given either the itemnumber or the barcode
362 my ($env, $itemnumber, $barcode) = @_;
363 my $dbh = C4::Context->dbh;
366 $sth=$dbh->prepare("select * from biblio,items,biblioitems where items.itemnumber=$itemnumber and biblio.biblionumber=items.biblionumber and biblioitems.biblioitemnumber = items.biblioitemnumber");
368 my $q_barcode=$dbh->quote($barcode);
369 $sth=$dbh->prepare("select * from biblio,items,biblioitems where items.barcode=$q_barcode and biblio.biblionumber=items.biblionumber and biblioitems.biblioitemnumber = items.biblioitemnumber");
371 $env->{'apierror'}="getiteminformation() subroutine must be called with either an itemnumber or a barcode";
376 my $iteminformation=$sth->fetchrow_hashref;
378 # FIXME - Style: instead of putting the entire rest of the
379 # function in a block, just say
380 # return undef unless $iteminformation;
381 # That way, the rest of the function needn't be indented as much.
382 if ($iteminformation) {
383 $sth=$dbh->prepare("select date_due from issues where itemnumber=$iteminformation->{'itemnumber'} and isnull(returndate)");
385 my ($date_due) = $sth->fetchrow;
386 $iteminformation->{'date_due'}=$date_due;
388 # FIXME - The Dewey code is a string, not a number. Besides,
389 # "000" is a perfectly valid Dewey code.
390 #$iteminformation->{'dewey'}=~s/0*$//;
391 ($iteminformation->{'dewey'} == 0) && ($iteminformation->{'dewey'}='');
392 # FIXME - fetchrow_hashref is documented as being inefficient.
393 # Perhaps this should be rewritten as
394 # $sth = $dbh->prepare("select loanlength, notforloan ...");
396 # ($iteminformation->{loanlength},
397 # $iteminformation->{notforloan}) = fetchrow_array;
398 $sth=$dbh->prepare("select * from itemtypes where itemtype='$iteminformation->{'itemtype'}'");
400 my $itemtype=$sth->fetchrow_hashref;
401 $iteminformation->{'loanlength'}=$itemtype->{'loanlength'};
402 $iteminformation->{'notforloan'}=$itemtype->{'notforloan'};
405 return($iteminformation);
410 $borrowers = &findborrower($env, $key);
411 print $borrowers->[0]{surname};
413 Looks up patrons and returns information about them.
417 C<$key> is either a card number or a string. C<&findborrower> tries to
418 look it up as a card number first. If that fails, C<&findborrower>
419 looks up all patrons whose surname begins with C<$key>.
421 C<$borrowers> is a reference-to-array. Each element is a
422 reference-to-hash whose keys are the fields of the borrowers table in
427 # If you really want to throw a monkey wrench into the works, change
428 # your last name to "V10000008" :-)
430 # FIXME - This is different from &C4::Borrower::findborrower, but I
431 # think that one's obsolete.
433 # returns an array of borrower hash references, given a cardnumber or a partial
435 my ($env, $key) = @_;
436 my $dbh = C4::Context->dbh;
438 my $q_key=$dbh->quote($key);
439 my $sth=$dbh->prepare("select * from borrowers where cardnumber=$q_key");
442 my ($borrower)=$sth->fetchrow_hashref;
443 push (@borrowers, $borrower);
445 $q_key=$dbh->quote("$key%");
447 $sth=$dbh->prepare("select * from borrowers where surname like $q_key");
449 while (my $borrower = $sth->fetchrow_hashref) {
450 push (@borrowers, $borrower);
460 ($dotransfer, $messages, $iteminformation) =
461 &transferbook($newbranch, $barcode, $ignore_reserves);
463 Transfers an item to a new branch. If the item is currently on loan,
464 it is automatically returned before the actual transfer.
466 C<$newbranch> is the code for the branch to which the item should be
469 C<$barcode> is the barcode of the item to be transferred.
471 If C<$ignore_reserves> is true, C<&transferbook> ignores reserves.
472 Otherwise, if an item is reserved, the transfer fails.
474 Returns three values:
476 C<$dotransfer> is true iff the transfer was successful.
478 C<$messages> is a reference-to-hash which may have any of the
485 There is no item in the catalog with the given barcode. The value is
490 The item's home branch is permanent. This doesn't prevent the item
491 from being transferred, though. The value is the code of the item's
494 =item C<DestinationEqualsHolding>
496 The item is already at the branch to which it is being transferred.
497 The transfer is nonetheless considered to have failed. The value
502 The item was on loan, and C<&transferbook> automatically returned it
503 before transferring it. The value is the borrower number of the patron
508 The item was reserved. The value is a reference-to-hash whose keys are
509 fields from the reserves table of the Koha database, and
510 C<biblioitemnumber>. It also has the key C<ResFound>, whose value is
511 either C<Waiting> or C<Reserved>.
513 =item C<WasTransferred>
515 The item was eligible to be transferred. Barring problems
516 communicating with the database, the transfer should indeed have
517 succeeded. The value should be ignored.
523 # FIXME - This function tries to do too much, and its API is clumsy.
524 # If it didn't also return books, it could be used to change the home
525 # branch of a book while the book is on loan.
527 # Is there any point in returning the item information? The caller can
528 # look that up elsewhere if ve cares.
530 # This leaves the ($dotransfer, $messages) tuple. This seems clumsy.
531 # If the transfer succeeds, that's all the caller should need to know.
532 # Thus, this function could simply return 1 or 0 to indicate success
533 # or failure, and set $C4::Circulation::Circ2::errmsg in case of
534 # failure. Or this function could return undef if successful, and an
535 # error message in case of failure (this would feel more like C than
538 # transfer book code....
539 my ($tbr, $barcode, $ignoreRs) = @_;
543 my $branches = getbranches();
544 my $iteminformation = getiteminformation(\%env, 0, $barcode);
546 if (not $iteminformation) {
547 $messages->{'BadBarcode'} = $barcode;
550 # get branches of book...
551 my $hbr = $iteminformation->{'homebranch'};
552 my $fbr = $iteminformation->{'holdingbranch'};
554 if ($branches->{$hbr}->{'PE'}) {
555 $messages->{'IsPermanent'} = $hbr;
557 # can't transfer book if is already there....
558 # FIXME - Why not? Shouldn't it trivially succeed?
560 $messages->{'DestinationEqualsHolding'} = 1;
563 # check if it is still issued to someone, return it...
564 my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
565 if ($currentborrower) {
566 returnbook($barcode, $fbr);
567 $messages->{'WasReturned'} = $currentborrower;
570 # FIXME - Don't call &CheckReserves unless $ignoreRs is true.
571 # That'll save a database query.
572 my ($resfound, $resrec) = CheckReserves($iteminformation->{'itemnumber'});
573 if ($resfound and not $ignoreRs) {
574 $resrec->{'ResFound'} = $resfound;
575 $messages->{'ResFound'} = $resrec;
578 #actually do the transfer....
580 dotransfer($iteminformation->{'itemnumber'}, $fbr, $tbr);
581 $messages->{'WasTransfered'} = 1;
583 return ($dotransfer, $messages, $iteminformation);
587 # FIXME - This is only used in &transferbook. Why bother making it a
590 my ($itm, $fbr, $tbr) = @_;
591 my $dbh = C4::Context->dbh;
592 $itm = $dbh->quote($itm);
593 $fbr = $dbh->quote($fbr);
594 $tbr = $dbh->quote($tbr);
595 #new entry in branchtransfers....
597 INSERT INTO branchtransfers
598 (itemnumber, frombranch, datearrived, tobranch)
599 VALUES ($itm, $fbr, now(), $tbr)
602 #update holdingbranch in items .....
605 SET datelastseen = now(),
607 WHERE items.itemnumber = $itm
614 ($iteminformation, $datedue, $rejected, $question, $questionnumber,
615 $defaultanswer, $message) =
616 &issuebook($env, $patroninformation, $barcode, $responses, $date);
618 Issue a book to a patron.
620 C<$env-E<gt>{usercode}> will be used in the usercode field of the
621 statistics table of the Koha database when this transaction is
624 C<$env-E<gt>{datedue}>, if given, specifies the date on which the book
625 is due back. This should be a string of the form "YYYY-MM-DD".
627 C<$env-E<gt>{branchcode}> is the code of the branch where this
628 transaction is taking place.
630 C<$patroninformation> is a reference-to-hash giving information about
631 the person borrowing the book. This is the first value returned by
632 C<&getpatroninformation>.
634 C<$barcode> is the bar code of the book being issued.
636 C<$responses> is a reference-to-hash. It represents the answers to the
637 questions asked by the C<$question>, C<$questionnumber>, and
638 C<$defaultanswer> return values (see below). The keys are numbers, and
639 the values can be "Y" or "N".
641 C<$date> is an optional date in the form "YYYY-MM-DD". If specified,
642 then only fines and charges up to that date will be considered when
643 checking to see whether the patron owes too much money to be lent a
646 C<&issuebook> returns an array of seven values:
648 C<$iteminformation> is a reference-to-hash describing the item just
649 issued. This in a form similar to that returned by
650 C<&getiteminformation>.
652 C<$datedue> is a string giving the date when the book is due, in the
655 C<$rejected> is either a string, or -1. If it is defined and is a
656 string, then the book may not be issued, and C<$rejected> gives the
657 reason for this. If C<$rejected> is -1, then the book may not be
658 issued, but no reason is given.
660 If there is a problem or question (e.g., the book is reserved for
661 another patron), then C<$question>, C<$questionnumber>, and
662 C<$defaultanswer> will be set. C<$questionnumber> indicates the
663 problem. C<$question> is a text string asking how to resolve the
664 problem, as a yes-or-no question, and C<$defaultanswer> is either "Y"
665 or "N", giving the default answer. The questions, their numbers, and
670 =item 1: "Issued to <name>. Mark as returned?" (Y)
672 =item 2: "Waiting for <patron> at <branch>. Allow issue?" (N)
674 =item 3: "Cancel reserve for <patron>?" (N)
676 =item 4: "Book is issued to this borrower. Renew?" (Y)
678 =item 5: "Reserved for <patron> at <branch> since <date>. Allow issue?" (N)
680 =item 6: "Set reserve for <patron> to waiting and transfer to <branch>?" (Y)
682 This is asked if the answer to question 5 was "N".
684 =item 7: "Cancel reserve for <patron>?" (N)
688 C<$message>, if defined, is an additional information message, e.g., a
693 # FIXME - The business with $responses is absurd. For one thing, these
694 # questions should have names, not numbers. For another, it'd be
695 # better to have the last argument be %extras. Then scripts can call
699 # -mark_returned => 0,
700 # -cancel_reserve => 1,
703 # and the script can use
704 # if (defined($extras{"-mark_returned"}) && $extras{"-mark_returned"})
705 # Heck, the $date argument should go in there as well.
707 # Also, there might be several reasons why a book can't be issued, but
708 # this API only supports asking one question at a time. Perhaps it'd
709 # be better to return a ref-to-list of problem IDs. Then the calling
710 # script can display a list of all of the problems at once.
712 # Is it this function's place to decide the default answer to the
713 # various questions? Why not document the various problems and allow
714 # the caller to decide?
716 my ($env, $patroninformation, $barcode, $responses, $date) = @_;
717 my $dbh = C4::Context->dbh;
718 my $iteminformation = getiteminformation($env, 0, $barcode);
720 my ($rejected,$question,$defaultanswer,$questionnumber, $noissue);
723 # See if there's any reason this book shouldn't be issued to this
725 SWITCH: { # FIXME - Yes, we know it's a switch. Tell us what it's for.
726 if ($patroninformation->{'gonenoaddress'}) {
727 $rejected="Patron is gone, with no known address.";
730 if ($patroninformation->{'lost'}) {
731 $rejected="Patron's card has been reported lost.";
734 if ($patroninformation->{'debarred'}) {
735 $rejected="Patron is Debarred";
738 my $amount = checkaccount($env,$patroninformation->{'borrowernumber'}, $dbh,$date);
739 # FIXME - "5" shouldn't be hardcoded. An Italian library might
740 # be generous enough to lend a book to a patron even if he
741 # does still owe them 5 lire.
742 if ($amount > 5 && $patroninformation->{'categorycode'} ne 'L' &&
743 $patroninformation->{'categorycode'} ne 'W' &&
744 $patroninformation->{'categorycode'} ne 'I' &&
745 $patroninformation->{'categorycode'} ne 'B' &&
746 $patroninformation->{'categorycode'} ne 'P') {
747 # FIXME - What do these category codes mean?
748 $rejected = sprintf "Patron owes \$%.02f.", $amount;
751 # FIXME - This sort of error-checking should be placed closer
752 # to the test; in this case, this error-checking should be
753 # done immediately after the call to &getiteminformation.
754 unless ($iteminformation) {
755 $rejected = "$barcode is not a valid barcode.";
758 if ($iteminformation->{'notforloan'} == 1) {
759 $rejected="Item not for loan.";
762 if ($iteminformation->{'wthdrawn'} == 1) {
763 $rejected="Item withdrawn.";
766 if ($iteminformation->{'restricted'} == 1) {
767 $rejected="Restricted item.";
770 if ($iteminformation->{'itemtype'} eq 'REF') {
771 $rejected="Reference item: Not for loan.";
774 my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
775 if ($currentborrower eq $patroninformation->{'borrowernumber'}) {
776 # Already issued to current borrower. Ask whether the loan should
778 my ($renewstatus) = renewstatus($env,$dbh,$patroninformation->{'borrowernumber'}, $iteminformation->{'itemnumber'});
779 if ($renewstatus == 0) {
780 $rejected="No more renewals allowed for this item.";
783 if ($responses->{4} eq '') {
785 $question = "Book is issued to this borrower.\nRenew?";
786 $defaultanswer = 'Y';
788 } elsif ($responses->{4} eq 'Y') {
789 my $charge = calc_charges($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'});
791 createcharge($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'}, $charge);
792 $iteminformation->{'charge'} = $charge;
794 &UpdateStats($env,$env->{'branchcode'},'renew',$charge,'',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'});
795 renewbook($env,$dbh, $patroninformation->{'borrowernumber'}, $iteminformation->{'itemnumber'});
802 } elsif ($currentborrower ne '') {
803 # This book is currently on loan, but not to the person
804 # who wants to borrow it now.
805 my ($currborrower, $cbflags) = getpatroninformation($env,$currentborrower,0);
806 if ($responses->{1} eq '') {
808 $question = "Issued to $currborrower->{'firstname'} $currborrower->{'surname'} ($currborrower->{'cardnumber'}).\nMark as returned?";
811 } elsif ($responses->{1} eq 'Y') {
812 returnbook($iteminformation->{'barcode'}, $env->{'branch'});
819 # See if the item is on reserve.
820 my ($restype, $res) = CheckReserves($iteminformation->{'itemnumber'});
822 my $resbor = $res->{'borrowernumber'};
823 if ($resbor eq $patroninformation->{'borrowernumber'}) {
824 # The item is on reserve to the current patron
826 } elsif ($restype eq "Waiting") {
827 # The item is on reserve and waiting, but has been
828 # reserved by some other patron.
829 my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
830 my $branches = getbranches();
831 my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
832 if ($responses->{2} eq '') {
834 # FIXME - Assumes HTML
835 $question="<font color=red>Waiting</font> for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}) at $branchname \nAllow issue?";
838 } elsif ($responses->{2} eq 'N') {
842 if ($responses->{3} eq '') {
844 $question="Cancel reserve for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})?";
847 } elsif ($responses->{3} eq 'Y') {
848 CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
851 } elsif ($restype eq "Reserved") {
852 # The item is on reserve for someone else.
853 my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
854 my $branches = getbranches();
855 my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
856 if ($responses->{5} eq '') {
858 $question="Reserved for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}) since $res->{'reservedate'} \nAllow issue?";
861 } elsif ($responses->{5} eq 'N') {
862 if ($responses->{6} eq '') {
864 $question="Set reserve for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}) to waiting and transfer to $branchname?";
866 } elsif ($responses->{6} eq 'Y') {
867 my $tobrcd = ReserveWaiting($res->{'itemnumber'}, $res->{'borrowernumber'});
868 transferbook($tobrcd, $barcode, 1);
869 $message = "Item should now be waiting at $branchname";
874 if ($responses->{7} eq '') {
876 $question="Cancel reserve for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})?";
879 } elsif ($responses->{7} eq 'Y') {
880 CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
887 unless (($question) || ($rejected) || ($noissue)) {
888 # There's no reason why the item can't be issued.
889 # FIXME - my $loanlength = $iteminformation->{loanlength} || 21;
891 if ($iteminformation->{'loanlength'}) {
892 $loanlength=$iteminformation->{'loanlength'};
894 my $ti=time; # FIXME - Never used
895 my $datedue=time+($loanlength)*86400;
896 # FIXME - Could just use POSIX::strftime("%Y-%m-%d", localtime);
897 # That's what it's for. Or, in this case:
898 # $dateduef = $env->{datedue} ||
899 # strftime("%Y-%m-%d", localtime(time +
900 # $loanlength * 86400));
901 my @datearr = localtime($datedue);
902 $dateduef = (1900+$datearr[5])."-".($datearr[4]+1)."-".$datearr[3];
903 if ($env->{'datedue'}) {
904 $dateduef=$env->{'datedue'};
906 $dateduef=~ s/2001\-4\-25/2001\-4\-26/;
907 # FIXME - What's this for? Leftover from debugging?
909 # Record in the database the fact that the book was issued.
910 # FIXME - Use $dbh->do();
911 my $sth=$dbh->prepare("insert into issues (borrowernumber, itemnumber, date_due, branchcode) values ($patroninformation->{'borrowernumber'}, $iteminformation->{'itemnumber'}, '$dateduef', '$env->{'branchcode'}')");
914 $iteminformation->{'issues'}++;
915 # FIXME - Use $dbh->do();
916 $sth=$dbh->prepare("update items set issues=$iteminformation->{'issues'},datelastseen=now() where itemnumber=$iteminformation->{'itemnumber'}");
919 # If it costs to borrow this book, charge it to the patron's account.
920 my $charge=calc_charges($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'});
922 createcharge($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'}, $charge);
923 $iteminformation->{'charge'}=$charge;
925 # Record the fact that this book was issued.
926 &UpdateStats($env,$env->{'branchcode'},'issue',$charge,'',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'});
928 if ($iteminformation->{'charge'}) {
929 $message=sprintf "Rental charge of \$%.02f applies.", $iteminformation->{'charge'};
931 return ($iteminformation, $dateduef, $rejected, $question, $questionnumber, $defaultanswer, $message);
938 ($doreturn, $messages, $iteminformation, $borrower) =
939 &returnbook($barcode, $branch);
943 C<$barcode> is the bar code of the book being returned. C<$branch> is
944 the code of the branch where the book is being returned.
946 C<&returnbook> returns a list of four items:
948 C<$doreturn> is true iff the return succeeded.
950 C<$messages> is a reference-to-hash giving the reason for failure:
956 No item with this barcode exists. The value is C<$barcode>.
960 The book is not currently on loan. The value is C<$barcode>.
964 The book's home branch is a permanent collection. If you have borrowed
965 this book, you are not allowed to return it. The value is the code for
966 the book's home branch.
970 This book has been withdrawn/cancelled. The value should be ignored.
974 The item was reserved. The value is a reference-to-hash whose keys are
975 fields from the reserves table of the Koha database, and
976 C<biblioitemnumber>. It also has the key C<ResFound>, whose value is
977 either C<Waiting>, C<Reserved>, or 0.
981 C<$borrower> is a reference-to-hash, giving information about the
982 patron who last borrowed the book.
986 # FIXME - This API is bogus. There's no need to return $borrower and
987 # $iteminformation; the caller can ask about those separately, if it
988 # cares (it'd be inefficient to make two database calls instead of
989 # one, but &getpatroninformation and &getiteminformation can be
990 # memoized if this is an issue).
992 # The ($doreturn, $messages) tuple is redundant: if the return
993 # succeeded, that's all the caller needs to know. So &returnbook can
994 # return 1 and 0 on success and failure, and set
995 # $C4::Circulation::Circ2::errmsg to indicate the error. Or it can
996 # return undef for success, and an error message on error (though this
997 # is more C-ish than Perl-ish).
999 my ($barcode, $branch) = @_;
1003 die '$branch not defined' unless defined $branch; # just in case (bug 170)
1004 # get information on item
1005 my ($iteminformation) = getiteminformation(\%env, 0, $barcode);
1006 if (not $iteminformation) {
1007 $messages->{'BadBarcode'} = $barcode;
1011 my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
1012 if ((not $currentborrower) && $doreturn) {
1013 $messages->{'NotIssued'} = $barcode;
1016 # check if the book is in a permanent collection....
1017 my $hbr = $iteminformation->{'homebranch'};
1018 my $branches = getbranches();
1019 if ($branches->{$hbr}->{'PE'}) {
1020 $messages->{'IsPermanent'} = $hbr;
1022 # check that the book has been cancelled
1023 if ($iteminformation->{'wthdrawn'}) {
1024 $messages->{'wthdrawn'} = 1;
1027 # update issues, thereby returning book (should push this out into another subroutine
1028 my ($borrower) = getpatroninformation(\%env, $currentborrower, 0);
1030 doreturn($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
1031 $messages->{'WasReturned'}; # FIXME - This does nothing
1033 ($borrower) = getpatroninformation(\%env, $currentborrower, 0);
1034 # transfer book to the current branch
1035 my ($transfered, $mess, $item) = transferbook($branch, $barcode, 1);
1036 if ($transfered) { # FIXME - perl -wc complains about this line.
1037 $messages->{'WasTransfered'}; # FIXME - This does nothing
1039 # fix up the accounts.....
1040 if ($iteminformation->{'itemlost'}) {
1041 # Mark the item as not being lost.
1042 updateitemlost($iteminformation->{'itemnumber'});
1043 fixaccountforlostandreturned($iteminformation, $borrower);
1044 $messages->{'WasLost'}; # FIXME - This does nothing
1046 # fix up the overdues in accounts...
1047 fixoverduesonreturn($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
1048 # find reserves.....
1049 my ($resfound, $resrec) = CheckReserves($iteminformation->{'itemnumber'});
1051 # my $tobrcd = ReserveWaiting($resrec->{'itemnumber'}, $resrec->{'borrowernumber'});
1052 $resrec->{'ResFound'} = $resfound;
1053 $messages->{'ResFound'} = $resrec;
1056 # Record the fact that this book was returned.
1057 UpdateStats(\%env, $branch ,'return','0','',$iteminformation->{'itemnumber'});
1058 return ($doreturn, $messages, $iteminformation, $borrower);
1062 # Takes a borrowernumber and an itemnuber.
1063 # Updates the 'issues' table to mark the item as returned (assuming
1064 # that it's currently on loan to the given borrower. Otherwise, the
1065 # item remains on loan.
1066 # Updates items.datelastseen for the item.
1068 # FIXME - This is only used in &returnbook. Why make it into a
1069 # separate function? (is this a recognizable step in the return process? - acli)
1071 my ($brn, $itm) = @_;
1072 my $dbh = C4::Context->dbh;
1073 $brn = $dbh->quote($brn);
1074 $itm = $dbh->quote($itm);
1075 my $query = "update issues set returndate = now() where (borrowernumber = $brn)
1076 and (itemnumber = $itm) and (returndate is null)";
1077 my $sth = $dbh->prepare($query);
1080 $query="update items set datelastseen=now() where itemnumber=$itm";
1081 $sth=$dbh->prepare($query);
1088 # Marks an item as not being lost.
1092 my $dbh = C4::Context->dbh;
1097 WHERE itemnumber = $itemno
1102 sub fixaccountforlostandreturned {
1103 my ($iteminfo, $borrower) = @_;
1105 my $dbh = C4::Context->dbh;
1106 my $itm = $dbh->quote($iteminfo->{'itemnumber'});
1107 # check for charge made for lost book
1108 my $query = "select * from accountlines where (itemnumber = $itm)
1109 and (accounttype='L' or accounttype='Rep') order by date desc";
1110 my $sth = $dbh->prepare($query);
1112 if (my $data = $sth->fetchrow_hashref) {
1113 # writeoff this amount
1115 my $amount = $data->{'amount'};
1116 my $acctno = $data->{'accountno'};
1118 if ($data->{'amountoutstanding'} == $amount) {
1119 $offset = $data->{'amount'};
1122 $offset = $amount - $data->{'amountoutstanding'};
1123 $amountleft = $data->{'amountoutstanding'} - $amount;
1125 my $uquery = "update accountlines set accounttype = 'LR',amountoutstanding='0'
1126 where (borrowernumber = '$data->{'borrowernumber'}')
1127 and (itemnumber = $itm) and (accountno = '$acctno') ";
1128 my $usth = $dbh->prepare($uquery);
1131 #check if any credit is left if so writeoff other accounts
1132 my $nextaccntno = getnextacctno(\%env,$data->{'borrowernumber'},$dbh);
1133 if ($amountleft < 0){
1136 if ($amountleft > 0){
1137 my $query = "select * from accountlines where (borrowernumber = '$data->{'borrowernumber'}')
1138 and (amountoutstanding >0) order by date";
1139 my $msth = $dbh->prepare($query);
1141 # offset transactions
1144 while (($accdata=$msth->fetchrow_hashref) and ($amountleft>0)){
1145 if ($accdata->{'amountoutstanding'} < $amountleft) {
1147 $amountleft -= $accdata->{'amountoutstanding'};
1149 $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
1152 my $thisacct = $accdata->{'accountno'};
1153 my $updquery = "update accountlines set amountoutstanding= '$newamtos'
1154 where (borrowernumber = '$data->{'borrowernumber'}')
1155 and (accountno='$thisacct')";
1156 my $usth = $dbh->prepare($updquery);
1159 $updquery = "insert into accountoffsets
1160 (borrowernumber, accountno, offsetaccount, offsetamount)
1162 ('$data->{'borrowernumber'}','$accdata->{'accountno'}','$nextaccntno','$newamtos')";
1163 $usth = $dbh->prepare($updquery);
1169 if ($amountleft > 0){
1172 my $desc="Book Returned ".$iteminfo->{'barcode'};
1173 $uquery = "insert into accountlines
1174 (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding)
1175 values ('$data->{'borrowernumber'}','$nextaccntno',now(),0-$amount,'$desc',
1177 $usth = $dbh->prepare($uquery);
1180 $uquery = "insert into accountoffsets
1181 (borrowernumber, accountno, offsetaccount, offsetamount)
1182 values ($borrower->{'borrowernumber'},$data->{'accountno'},$nextaccntno,$offset)";
1183 $usth = $dbh->prepare($uquery);
1186 $uquery = "update items set paidfor='' where itemnumber=$itm";
1187 $usth = $dbh->prepare($uquery);
1196 sub fixoverduesonreturn {
1197 my ($brn, $itm) = @_;
1198 my $dbh = C4::Context->dbh;
1199 $itm = $dbh->quote($itm);
1200 $brn = $dbh->quote($brn);
1201 # check for overdue fine
1202 my $query = "select * from accountlines where (borrowernumber=$brn)
1203 and (itemnumber = $itm) and (accounttype='FU' or accounttype='O')";
1204 my $sth = $dbh->prepare($query);
1206 # alter fine to show that the book has been returned
1207 if (my $data = $sth->fetchrow_hashref) {
1208 my $query = "update accountlines set accounttype='F' where (borrowernumber = $brn)
1209 and (itemnumber = $itm) and (acccountno='$data->{'accountno'}')";
1210 my $usth=$dbh->prepare($query);
1220 # NOTE!: If you change this function, be sure to update the POD for
1221 # &getpatroninformation.
1223 # $flags = &patronflags($env, $patron, $dbh);
1226 # {message} Message showing patron's credit or debt
1227 # {noissues} Set if patron owes >$5.00
1228 # {GNA} Set if patron gone w/o address
1229 # {message} "Borrower has no valid address"
1231 # {LOST} Set if patron's card reported lost
1232 # {message} Message to this effect
1234 # {DBARRED} Set is patron is debarred
1235 # {message} Message to this effect
1237 # {NOTES} Set if patron has notes
1238 # {message} Notes about patron
1239 # {ODUES} Set if patron has overdue books
1241 # {itemlist} ref-to-array: list of overdue books
1242 # {itemlisttext} Text list of overdue items
1243 # {WAITING} Set if there are items available that the
1245 # {message} Message to this effect
1246 # {itemlist} ref-to-array: list of available items
1248 # Original subroutine for Circ2.pm
1250 my ($env, $patroninformation, $dbh) = @_;
1251 my $amount = checkaccount($env, $patroninformation->{'borrowernumber'}, $dbh);
1254 $flaginfo{'message'}= sprintf "Patron owes \$%.02f", $amount;
1256 $flaginfo{'noissues'} = 1;
1258 $flags{'CHARGES'} = \%flaginfo;
1259 } elsif ($amount < 0){
1261 $flaginfo{'message'} = sprintf "Patron has credit of \$%.02f", -$amount;
1262 $flags{'CHARGES'} = \%flaginfo;
1264 if ($patroninformation->{'gonenoaddress'} == 1) {
1266 $flaginfo{'message'} = 'Borrower has no valid address.';
1267 $flaginfo{'noissues'} = 1;
1268 $flags{'GNA'} = \%flaginfo;
1270 if ($patroninformation->{'lost'} == 1) {
1272 $flaginfo{'message'} = 'Borrower\'s card reported lost.';
1273 $flaginfo{'noissues'} = 1;
1274 $flags{'LOST'} = \%flaginfo;
1276 if ($patroninformation->{'debarred'} == 1) {
1278 $flaginfo{'message'} = 'Borrower is Debarred.';
1279 $flaginfo{'noissues'} = 1;
1280 $flags{'DBARRED'} = \%flaginfo;
1282 if ($patroninformation->{'borrowernotes'}) {
1284 $flaginfo{'message'} = "$patroninformation->{'borrowernotes'}";
1285 $flags{'NOTES'} = \%flaginfo;
1287 my ($odues, $itemsoverdue)
1288 = checkoverdues($env, $patroninformation->{'borrowernumber'}, $dbh);
1291 $flaginfo{'message'} = "Yes";
1292 $flaginfo{'itemlist'} = $itemsoverdue;
1293 foreach (sort {$a->{'date_due'} cmp $b->{'date_due'}} @$itemsoverdue) {
1294 $flaginfo{'itemlisttext'}.="$_->{'date_due'} $_->{'barcode'} $_->{'title'} \n";
1296 $flags{'ODUES'} = \%flaginfo;
1298 my ($nowaiting, $itemswaiting)
1299 = CheckWaiting($patroninformation->{'borrowernumber'});
1300 if ($nowaiting > 0) {
1302 $flaginfo{'message'} = "Reserved items available";
1303 $flaginfo{'itemlist'} = $itemswaiting;
1304 $flags{'WAITING'} = \%flaginfo;
1312 # From Main.pm, modified to return a list of overdueitems, in addition to a count
1313 #checks whether a borrower has overdue items
1314 my ($env, $bornum, $dbh)=@_;
1315 my @datearr = localtime;
1316 my $today = ($datearr[5] + 1900)."-".($datearr[4]+1)."-".$datearr[3];
1319 my $query = "SELECT * FROM issues,biblio,biblioitems,items
1320 WHERE items.biblioitemnumber = biblioitems.biblioitemnumber
1321 AND items.biblionumber = biblio.biblionumber
1322 AND issues.itemnumber = items.itemnumber
1323 AND issues.borrowernumber = $bornum
1324 AND issues.returndate is NULL
1325 AND issues.date_due < '$today'";
1326 my $sth = $dbh->prepare($query);
1328 while (my $data = $sth->fetchrow_hashref) {
1329 push (@overdueitems, $data);
1333 return ($count, \@overdueitems);
1337 sub currentborrower {
1338 # Original subroutine for Circ2.pm
1339 my ($itemnumber) = @_;
1340 my $dbh = C4::Context->dbh;
1341 my $q_itemnumber = $dbh->quote($itemnumber);
1342 my $sth=$dbh->prepare("select borrowers.borrowernumber from
1343 issues,borrowers where issues.itemnumber=$q_itemnumber and
1344 issues.borrowernumber=borrowers.borrowernumber and issues.returndate is
1347 my ($borrower) = $sth->fetchrow;
1351 # FIXME - Not exported, but used in 'updateitem.pl' anyway.
1353 # Stolen from Main.pm
1354 # Check for reserves for biblio
1355 my ($env,$dbh,$itemnum)=@_;
1357 my $query = "select * from reserves,items
1358 where (items.itemnumber = '$itemnum')
1359 and (reserves.cancellationdate is NULL)
1360 and (items.biblionumber = reserves.biblionumber)
1361 and ((reserves.found = 'W')
1362 or (reserves.found is null))
1364 my $sth = $dbh->prepare($query);
1367 my $data=$sth->fetchrow_hashref;
1368 while ($data && $resbor eq '') {
1370 my $const = $data->{'constrainttype'};
1371 if ($const eq "a") {
1372 $resbor = $data->{'borrowernumber'};
1375 my $cquery = "select * from reserveconstraints,items
1376 where (borrowernumber='$data->{'borrowernumber'}')
1377 and reservedate='$data->{'reservedate'}'
1378 and reserveconstraints.biblionumber='$data->{'biblionumber'}'
1379 and (items.itemnumber=$itemnum and
1380 items.biblioitemnumber = reserveconstraints.biblioitemnumber)";
1381 my $csth = $dbh->prepare($cquery);
1383 if (my $cdata=$csth->fetchrow_hashref) {$found = 1;}
1384 if ($const eq 'o') {
1385 if ($found eq 1) {$resbor = $data->{'borrowernumber'};}
1387 if ($found eq 0) {$resbor = $data->{'borrowernumber'};}
1391 $data=$sth->fetchrow_hashref;
1394 return ($resbor,$resrec);
1399 $issues = ¤tissues($env, $borrower);
1401 Returns a list of books currently on loan to a patron.
1403 If C<$env-E<gt>{todaysissues}> is set and true, C<¤tissues> only
1404 returns information about books issued today. If
1405 C<$env-E<gt>{nottodaysissues}> is set and true, C<¤tissues> 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<¤tissues> returns all of the patron's issues.
1410 C<$borrower->{borrowernumber}> is the borrower number of the patron
1411 whose issues we want to list.
1413 C<¤tissues> 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.
1423 # New subroutine for Circ2.pm
1424 my ($env, $borrower) = @_;
1425 my $dbh = C4::Context->dbh;
1428 my $borrowernumber = $borrower->{'borrowernumber'};
1431 # Figure out whether to get the books issued today, or earlier.
1432 # FIXME - $env->{todaysissues} and $env->{nottodaysissues} can
1433 # both be specified, but are mutually-exclusive. This is bogus.
1434 # Make this a flag. Or better yet, return everything in (reverse)
1435 # chronological order and let the caller figure out which books
1436 # were issued today.
1437 if ($env->{'todaysissues'}) {
1439 # $today = POSIX::strftime("%Y%m%d", localtime);
1440 # FIXME - Since $today will be used in either case, move it
1441 # out of the two if-blocks.
1442 my @datearr = localtime(time());
1443 my $today = (1900+$datearr[5]).sprintf "%02d", ($datearr[4]+1).sprintf "%02d", $datearr[3];
1444 # FIXME - MySQL knows about dates. Just use
1445 # and issues.timestamp = curdate();
1446 $crit=" and issues.timestamp like '$today%' ";
1448 if ($env->{'nottodaysissues'}) {
1450 # $today = POSIX::strftime("%Y%m%d", localtime);
1451 # FIXME - Since $today will be used in either case, move it
1452 # out of the two if-blocks.
1453 my @datearr = localtime(time());
1454 my $today = (1900+$datearr[5]).sprintf "%02d", ($datearr[4]+1).sprintf "%02d", $datearr[3];
1455 # FIXME - MySQL knows about dates. Just use
1456 # and issues.timestamp < curdate();
1457 $crit=" and !(issues.timestamp like '$today%') ";
1460 # FIXME - Does the caller really need every single field from all
1462 my $select="select * from issues,items,biblioitems,biblio where
1463 borrowernumber='$borrowernumber' and issues.itemnumber=items.itemnumber and
1464 items.biblionumber=biblio.biblionumber and
1465 items.biblioitemnumber=biblioitems.biblioitemnumber and returndate is null
1466 $crit order by issues.date_due";
1468 my $sth=$dbh->prepare($select);
1470 while (my $data = $sth->fetchrow_hashref) {
1471 # FIXME - The Dewey code is a string, not a number.
1472 $data->{'dewey'}=~s/0*$//;
1473 ($data->{'dewey'} == 0) && ($data->{'dewey'}='');
1475 # $todaysdate = POSIX::strftime("%Y%m%d", localtime)
1476 # or better yet, just reuse $today which was calculated above.
1477 # This function isn't going to run until midnight, is it?
1479 # $todaysdate = POSIX::strftime("%Y-%m-%d", localtime)
1480 # if ($data->{'date_due'} lt $todaysdate)
1482 # Either way, the date should be be formatted outside of the
1484 my @datearr = localtime(time());
1485 my $todaysdate = (1900+$datearr[5]).sprintf ("%0.2d", ($datearr[4]
1486 +1)).sprintf ("%0.2d", $datearr[3]);
1487 my $datedue=$data->{'date_due'};
1489 if ($datedue < $todaysdate) {
1490 $data->{'overdue'}=1;
1492 my $itemnumber=$data->{'itemnumber'};
1493 # FIXME - Consecutive integers as hash keys? You have GOT to
1494 # be kidding me! Use an array, fercrissakes!
1495 $currentissues{$counter}=$data;
1499 return(\%currentissues);
1504 $issues = &getissues($borrowernumber);
1506 Returns the set of books currently on loan to a patron.
1508 C<$borrowernumber> is the patron's borrower number.
1510 C<&getissues> returns a PHP-style array: C<$issues> is a
1511 reference-to-hash whose keys are integers in the range 0..I<n>-1,
1512 where I<n> is the number of books the patron currently has on loan.
1514 The values of C<$issues> are references-to-hash whose keys are
1515 selected fields from the issues, items, biblio, and biblioitems tables
1516 of the Koha database.
1521 # New subroutine for Circ2.pm
1522 my ($borrower) = @_;
1523 my $dbh = C4::Context->dbh;
1524 my $borrowernumber = $borrower->{'borrowernumber'};
1526 my $select = "SELECT issues.timestamp AS timestamp,
1527 issues.date_due AS date_due,
1528 items.biblionumber AS biblionumber,
1529 items.itemnumber AS itemnumber,
1530 items.barcode AS barcode,
1531 biblio.title AS title,
1532 biblio.author AS author,
1533 biblioitems.dewey AS dewey,
1534 itemtypes.description AS itemtype,
1535 biblioitems.subclass AS subclass
1536 FROM issues,items,biblioitems,biblio, itemtypes
1537 WHERE issues.borrowernumber = ?
1538 AND issues.itemnumber = items.itemnumber
1539 AND items.biblionumber = biblio.biblionumber
1540 AND items.biblioitemnumber = biblioitems.biblioitemnumber
1541 AND itemtypes.itemtype = biblioitems.itemtype
1542 AND issues.returndate IS NULL
1543 ORDER BY issues.date_due";
1545 my $sth=$dbh->prepare($select);
1546 $sth->execute($borrowernumber);
1548 while (my $data = $sth->fetchrow_hashref) {
1549 $data->{'dewey'} =~ s/0*$//;
1550 ($data->{'dewey'} == 0) && ($data->{'dewey'} = '');
1551 # FIXME - The Dewey code is a string, not a number.
1552 # FIXME - Use POSIX::strftime to get a text version of today's
1553 # date. That's what it's for.
1554 # FIXME - Move the date calculation outside of the loop.
1555 my @datearr = localtime(time());
1556 my $todaysdate = (1900+$datearr[5]).sprintf ("%0.2d", ($datearr[4]+1)).sprintf ("%0.2d", $datearr[3]);
1558 # FIXME - Instead of converting the due date to YYYYMMDD, just
1560 # $todaysdate = POSIX::strftime("%Y-%m-%d", localtime);
1562 # if ($date->{date_due} lt $todaysdate)
1563 my $datedue = $data->{'date_due'};
1565 if ($datedue < $todaysdate) {
1566 $data->{'overdue'} = 1;
1568 $currentissues{$counter} = $data;
1570 # FIXME - This is ludicrous. If you want to return an
1571 # array of values, just use an array. That's what
1572 # they're there for.
1575 return(\%currentissues);
1580 #Stolen from Main.pm
1581 # check for reserves waiting
1582 my ($env,$dbh,$bornum)=@_;
1584 my $query = "select * from reserves
1585 where (borrowernumber = '$bornum')
1586 and (reserves.found='W') and cancellationdate is NULL";
1587 my $sth = $dbh->prepare($query);
1590 if (my $data=$sth->fetchrow_hashref) {
1591 $itemswaiting[$cnt] =$data;
1595 return ($cnt,\@itemswaiting);
1599 # FIXME - This is nearly-identical to &C4::Accounts::checkaccount
1601 # Stolen from Accounts.pm
1602 #take borrower number
1603 #check accounts and list amounts owing
1604 my ($env,$bornumber,$dbh,$date)=@_;
1605 my $select="SELECT SUM(amountoutstanding) AS total
1607 WHERE borrowernumber = $bornumber
1608 AND amountoutstanding<>0";
1610 $select.=" AND date < '$date'";
1613 my $sth=$dbh->prepare($select);
1615 my $data=$sth->fetchrow_hashref;
1616 my $total = $data->{'total'};
1618 # output(1,2,"borrower owes $total");
1620 # # output(1,2,"borrower owes $total");
1622 # reconcileaccount($env,$dbh,$bornumber,$total);
1629 # FIXME - This is identical to &C4::Circulation::Renewals::renewstatus.
1630 # Pick one and stick with it.
1632 # Stolen from Renewals.pm
1633 # check renewal status
1634 my ($env,$dbh,$bornum,$itemno)=@_;
1637 my $q1 = "select * from issues
1638 where (borrowernumber = '$bornum')
1639 and (itemnumber = '$itemno')
1640 and returndate is null";
1641 my $sth1 = $dbh->prepare($q1);
1643 if (my $data1 = $sth1->fetchrow_hashref) {
1644 my $q2 = "select renewalsallowed from items,biblioitems,itemtypes
1645 where (items.itemnumber = '$itemno')
1646 and (items.biblioitemnumber = biblioitems.biblioitemnumber)
1647 and (biblioitems.itemtype = itemtypes.itemtype)";
1648 my $sth2 = $dbh->prepare($q2);
1650 if (my $data2=$sth2->fetchrow_hashref) {
1651 $renews = $data2->{'renewalsallowed'};
1653 if ($renews > $data1->{'renewals'}) {
1663 # Stolen from Renewals.pm
1664 # mark book as renewed
1665 my ($env,$dbh,$bornum,$itemno,$datedue)=@_;
1666 $datedue=$env->{'datedue'};
1667 if ($datedue eq "" ) {
1669 my $query= "Select * from biblioitems,items,itemtypes
1670 where (items.itemnumber = '$itemno')
1671 and (biblioitems.biblioitemnumber = items.biblioitemnumber)
1672 and (biblioitems.itemtype = itemtypes.itemtype)";
1673 my $sth=$dbh->prepare($query);
1675 if (my $data=$sth->fetchrow_hashref) {
1676 $loanlength = $data->{'loanlength'}
1680 my $datedu = time + ($loanlength * 86400);
1681 my @datearr = localtime($datedu);
1682 $datedue = (1900+$datearr[5])."-".($datearr[4]+1)."-".$datearr[3];
1684 my @date = split("-",$datedue);
1685 my $odatedue = ($date[2]+0)."-".($date[1]+0)."-".$date[0];
1686 my $issquery = "select * from issues where borrowernumber='$bornum' and
1687 itemnumber='$itemno' and returndate is null";
1688 my $sth=$dbh->prepare($issquery);
1690 my $issuedata=$sth->fetchrow_hashref;
1692 my $renews = $issuedata->{'renewals'} +1;
1693 my $updquery = "update issues
1694 set date_due = '$datedue', renewals = '$renews'
1695 where borrowernumber='$bornum' and
1696 itemnumber='$itemno' and returndate is null";
1697 $sth=$dbh->prepare($updquery);
1704 # FIXME - This is almost, but not quite, identical to
1705 # &C4::Circulation::Issues::calc_charges and
1706 # &C4::Circulation::Renewals2::calc_charges.
1707 # Pick one and stick with it.
1709 # Stolen from Issues.pm
1710 # calculate charges due
1711 my ($env, $dbh, $itemno, $bornum)=@_;
1716 # open (FILE,">>/tmp/charges");
1718 my $q1 = "select itemtypes.itemtype,rentalcharge from items,biblioitems,itemtypes
1719 where (items.itemnumber ='$itemno')
1720 and (biblioitems.biblioitemnumber = items.biblioitemnumber)
1721 and (biblioitems.itemtype = itemtypes.itemtype)";
1722 my $sth1= $dbh->prepare($q1);
1723 # print FILE "$q1\n";
1725 if (my $data1=$sth1->fetchrow_hashref) {
1726 $item_type = $data1->{'itemtype'};
1727 $charge = $data1->{'rentalcharge'};
1728 # print FILE "charge is $charge\n";
1729 my $q2 = "select rentaldiscount from borrowers,categoryitem
1730 where (borrowers.borrowernumber = '$bornum')
1731 and (borrowers.categorycode = categoryitem.categorycode)
1732 and (categoryitem.itemtype = '$item_type')";
1733 my $sth2=$dbh->prepare($q2);
1736 if (my $data2=$sth2->fetchrow_hashref) {
1737 my $discount = $data2->{'rentaldiscount'};
1738 # print FILE "discount is $discount";
1739 if ($discount eq 'NULL') {
1742 $charge = ($charge *(100 - $discount)) / 100;
1748 return ($charge, $item_type);
1751 # FIXME - A virtually identical function appears in
1752 # C4::Circulation::Issues. Pick one and stick with it.
1754 #Stolen from Issues.pm
1755 my ($env,$dbh,$itemno,$bornum,$charge) = @_;
1756 my $nextaccntno = getnextacctno($env,$bornum,$dbh);
1757 my $sth = $dbh->prepare(<<EOT);
1758 INSERT INTO accountlines
1759 (borrowernumber, itemnumber, accountno,
1760 date, amount, description, accounttype,
1763 now(), ?, 'Rental', 'Rent',
1766 $sth->execute($bornum, $itemno, $nextaccntno, $charge, $charge);
1772 # Stolen from Accounts.pm
1773 my ($env,$bornumber,$dbh)=@_;
1774 my $nextaccntno = 1;
1775 my $query = "select * from accountlines where (borrowernumber = '$bornumber') order by accountno desc";
1776 my $sth = $dbh->prepare($query);
1778 if (my $accdata=$sth->fetchrow_hashref){
1779 $nextaccntno = $accdata->{'accountno'} + 1;
1782 return($nextaccntno);
1787 ($status, $record) = &find_reserves($itemnumber);
1789 Looks up an item in the reserves.
1791 C<$itemnumber> is the itemnumber to look up.
1793 C<$status> is true iff the search was successful.
1795 C<$record> is a reference-to-hash describing the reserve. Its keys are
1796 the fields from the reserves table of the Koha database.
1800 # FIXME - This API is bogus: just return the record, or undef if none
1802 # FIXME - There's also a &C4::Circulation::Returns::find_reserves, but
1803 # that one looks rather different.
1805 # Stolen from Returns.pm
1808 my $dbh = C4::Context->dbh;
1809 my ($itemdata) = getiteminformation(\%env, $itemno,0);
1810 my $bibno = $dbh->quote($itemdata->{'biblionumber'});
1811 my $bibitm = $dbh->quote($itemdata->{'biblioitemnumber'});
1812 my $query = "select * from reserves where ((found = 'W') or (found is null))
1813 and biblionumber = $bibno and cancellationdate is NULL
1814 order by priority, reservedate ";
1815 my $sth = $dbh->prepare($query);
1822 # FIXME - I'm not really sure what's going on here, but since we
1823 # only want one result, wouldn't it be possible (and far more
1824 # efficient) to do something clever in SQL that only returns one
1826 while (($resrec = $sth->fetchrow_hashref) && (not $resfound)) {
1827 # FIXME - Unlike Pascal, Perl allows you to exit loops
1828 # early. Take out the "&& (not $resfound)" and just
1829 # use "last" at the appropriate point in the loop.
1830 # (Oh, and just in passing: if you'd used "!" instead
1831 # of "not", you wouldn't have needed the parentheses.)
1833 my $brn = $dbh->quote($resrec->{'borrowernumber'});
1834 my $rdate = $dbh->quote($resrec->{'reservedate'});
1835 my $bibno = $dbh->quote($resrec->{'biblionumber'});
1836 if ($resrec->{'found'} eq "W") {
1837 if ($resrec->{'itemnumber'} eq $itemno) {
1841 # FIXME - Use 'elsif' to avoid unnecessary indentation.
1842 if ($resrec->{'constrainttype'} eq "a") {
1845 my $conquery = "select * from reserveconstraints where borrowernumber = $brn
1846 and reservedate = $rdate and biblionumber = $bibno and biblioitemnumber = $bibitm";
1847 my $consth = $dbh->prepare($conquery);
1849 if (my $conrec = $consth->fetchrow_hashref) {
1850 if ($resrec->{'constrainttype'} eq "o") {
1858 my $updquery = "update reserves set found = 'W', itemnumber = '$itemno'
1859 where borrowernumber = $brn and reservedate = $rdate and biblionumber = $bibno";
1860 my $updsth = $dbh->prepare($updquery);
1863 # FIXME - "last;" here to break out of the loop early.
1867 return ($resfound,$lastrec);
1877 Koha Developement team <info@koha.org>