1 package C4::Circulation::Circ2;
5 #package to deal with Returns
6 #written 3/11/99 by olwen@katipo.co.nz
9 # Copyright 2000-2002 Katipo Communications
11 # This file is part of Koha.
13 # Koha is free software; you can redistribute it and/or modify it under the
14 # terms of the GNU General Public License as published by the Free Software
15 # Foundation; either version 2 of the License, or (at your option) any later
18 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
19 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
20 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License along with
23 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
24 # Suite 330, Boston, MA 02111-1307 USA
32 #use C4::InterfaceCDK;
33 #use C4::Circulation::Main;
34 #use C4::Circulation::Renewals;
41 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
43 # set the version for version checking
48 C4::Circulation::Circ2 - Koha circulation module
52 use C4::Circulation::Circ2;
56 The functions in this module deal with circulation, issues, and
57 returns, as well as general information about the library.
66 @EXPORT = qw(&getbranches &getprinters &getpatroninformation
67 ¤tissues &getissues &getiteminformation &findborrower
68 &issuebook &returnbook &find_reserves &transferbook &decode
73 $branches = &getbranches();
74 @branch_codes = keys %$branches;
75 %main_branch_info = %{$branches->{"MAIN"}};
77 Returns information about existing library branches.
79 C<$branches> is a reference-to-hash. Its keys are the branch codes for
80 all of the existing library branches, and its values are
81 references-to-hash describing that particular branch.
83 In each branch description (C<%main_branch_info>, above), there is a
84 key for each field in the branches table of the Koha database. In
85 addition, there is a key for each branch category code to which the
86 branch belongs (the category codes are taken from the branchrelations
91 # FIXME - This function doesn't feel as if it belongs here. It should
92 # go in some generic or administrative module, not in circulation.
94 # returns a reference to a hash of references to branches...
96 my $dbh = C4::Context->dbh;
97 my $sth=$dbh->prepare("select * from branches");
99 while (my $branch=$sth->fetchrow_hashref) {
100 my $tmp = $branch->{'branchcode'}; my $brc = $dbh->quote($tmp);
101 # FIXME - my $brc = $dbh->quote($branch->{"branchcode"});
102 my $query = "select categorycode from branchrelations where branchcode = $brc";
103 my $nsth = $dbh->prepare($query);
105 while (my ($cat) = $nsth->fetchrow_array) {
106 # FIXME - This seems wrong. It ought to be
107 # $branch->{categorycodes}{$cat} = 1;
108 # otherwise, there's a namespace collision if there's a
109 # category with the same name as a field in the 'branches'
110 # table (i.e., don't create a category called "issuing").
111 # In addition, the current structure doesn't really allow
112 # you to list the categories that a branch belongs to:
113 # you'd have to list keys %$branch, and remove those keys
114 # that aren't fields in the "branches" table.
118 $branches{$branch->{'branchcode'}}=$branch;
125 $printers = &getprinters($env);
126 @queues = keys %$printers;
128 Returns information about existing printer queues.
132 C<$printers> is a reference-to-hash whose keys are the print queues
133 defined in the printers table of the Koha database. The values are
134 references-to-hash, whose keys are the fields in the printers table.
138 # FIXME - Perhaps this really belongs in C4::Print?
142 my $dbh = C4::Context->dbh;
143 my $sth=$dbh->prepare("select * from printers");
145 while (my $printer=$sth->fetchrow_hashref) {
146 $printers{$printer->{'printqueue'}}=$printer;
151 =item getpatroninformation
153 ($borrower, $flags) = &getpatroninformation($env, $borrowernumber,
156 Looks up a patron and returns information about him or her. If
157 C<$borrowernumber> is true (nonzero), C<&getpatroninformation> looks
158 up the borrower by number; otherwise, it looks up the borrower by card
161 C<$env> is effectively ignored, but should be a reference-to-hash.
163 C<$borrower> is a reference-to-hash whose keys are the fields of the
164 borrowers table in the Koha database. In addition,
165 C<$borrower-E<gt>{flags}> is the same as C<$flags>.
167 C<$flags> is a reference-to-hash giving more detailed information
168 about the patron. Its keys act as flags: if they are set, then the key
169 is a reference-to-hash that gives further details:
171 if (exists($flags->{LOST}))
173 # Patron's card was reported lost
174 print $flags->{LOST}{message}, "\n";
177 Each flag has a C<message> key, giving a human-readable explanation of
178 the flag. If the state of a flag means that the patron should not be
179 allowed to borrow any more books, then it will have a C<noissues> key
182 The possible flags are:
188 Shows the patron's credit or debt, if any.
192 (Gone, no address.) Set if the patron has left without giving a
197 Set if the patron's card has been reported as lost.
201 Set if the patron has been debarred.
205 Any additional notes about the patron.
209 Set if the patron has overdue items. This flag has several keys:
211 C<$flags-E<gt>{ODUES}{itemlist}> is a reference-to-array listing the
212 overdue items. Its elements are references-to-hash, each describing an
213 overdue item. The keys are selected fields from the issues, biblio,
214 biblioitems, and items tables of the Koha database.
216 C<$flags-E<gt>{ODUES}{itemlist}> is a string giving a text listing of
217 the overdue items, one per line.
221 Set if any items that the patron has reserved are available.
223 C<$flags-E<gt>{WAITING}{itemlist}> is a reference-to-array listing the
224 available items. Each element is a reference-to-hash whose keys are
225 fields from the reserves table of the Koha database.
231 sub getpatroninformation {
233 my ($env, $borrowernumber,$cardnumber) = @_;
234 my $dbh = C4::Context->dbh;
237 if ($borrowernumber) {
238 $query = "select * from borrowers where borrowernumber=$borrowernumber";
239 } elsif ($cardnumber) {
240 $query = "select * from borrowers where cardnumber=$cardnumber";
242 $env->{'apierror'} = "invalid borrower information passed to getpatroninformation subroutine";
245 $env->{'mess'} = $query;
246 $sth = $dbh->prepare($query);
248 my $borrower = $sth->fetchrow_hashref;
249 my $amount = checkaccount($env, $borrowernumber, $dbh);
250 $borrower->{'amountoutstanding'} = $amount;
251 my $flags = patronflags($env, $borrower, $dbh);
254 $sth=$dbh->prepare("select bit,flag from userflags");
256 while (my ($bit, $flag) = $sth->fetchrow) {
257 if ($borrower->{'flags'} & 2**$bit) {
258 $accessflagshash->{$flag}=1;
262 $borrower->{'flags'}=$flags;
263 return ($borrower, $flags, $accessflagshash);
268 $str = &decode($chunk);
270 Decodes a segment of a string emitted by a CueCat barcode scanner and
275 # FIXME - At least, I'm pretty sure this is for decoding CueCat stuff.
278 my $seq = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-';
279 my @s = map { index($seq,$_); } split(//,$encoded);
294 my $n = (($s[0] << 6 | $s[1]) << 6 | $s[2]) << 6 | $s[3];
295 $r .=chr(($n >> 16) ^ 67) .
296 chr(($n >> 8 & 255) ^ 67) .
297 chr(($n & 255) ^ 67);
300 $r = substr($r,0,length($r)-$l);
304 =item getiteminformation
306 $item = &getiteminformation($env, $itemnumber, $barcode);
308 Looks up information about an item, given either its item number or
309 its barcode. If C<$itemnumber> is a nonzero value, it is used;
310 otherwise, C<$barcode> is used.
312 C<$env> is effectively ignored, but should be a reference-to-hash.
314 C<$item> is a reference-to-hash whose keys are fields from the biblio,
315 items, and biblioitems tables of the Koha database. It may also
316 contain the following keys:
322 The due date on this item, if it has been borrowed and not returned
323 yet. The date is in YYYY-MM-DD format.
327 The length of time for which the item can be borrowed, in days.
331 True if the item may not be borrowed.
337 sub getiteminformation {
338 # returns a hash of item information given either the itemnumber or the barcode
339 my ($env, $itemnumber, $barcode) = @_;
340 my $dbh = C4::Context->dbh;
343 $sth=$dbh->prepare("select * from biblio,items,biblioitems where items.itemnumber=$itemnumber and biblio.biblionumber=items.biblionumber and biblioitems.biblioitemnumber = items.biblioitemnumber");
345 my $q_barcode=$dbh->quote($barcode);
346 $sth=$dbh->prepare("select * from biblio,items,biblioitems where items.barcode=$q_barcode and biblio.biblionumber=items.biblionumber and biblioitems.biblioitemnumber = items.biblioitemnumber");
348 $env->{'apierror'}="getiteminformation() subroutine must be called with either an itemnumber or a barcode";
353 my $iteminformation=$sth->fetchrow_hashref;
355 # FIXME - Style: instead of putting the entire rest of the
356 # function in a block, just say
357 # return undef unless $iteminformation;
358 # That way, the rest of the function needn't be indented as much.
359 if ($iteminformation) {
360 $sth=$dbh->prepare("select date_due from issues where itemnumber=$iteminformation->{'itemnumber'} and isnull(returndate)");
362 my ($date_due) = $sth->fetchrow;
363 $iteminformation->{'date_due'}=$date_due;
365 # FIXME - The Dewey code is a string, not a number. Besides,
366 # "000" is a perfectly valid Dewey code.
367 #$iteminformation->{'dewey'}=~s/0*$//;
368 ($iteminformation->{'dewey'} == 0) && ($iteminformation->{'dewey'}='');
369 # FIXME - fetchrow_hashref is documented as being inefficient.
370 # Perhaps this should be rewritten as
371 # $sth = $dbh->prepare("select loanlength, notforloan ...");
373 # ($iteminformation->{loanlength},
374 # $iteminformation->{notforloan}) = fetchrow_array;
375 $sth=$dbh->prepare("select * from itemtypes where itemtype='$iteminformation->{'itemtype'}'");
377 my $itemtype=$sth->fetchrow_hashref;
378 $iteminformation->{'loanlength'}=$itemtype->{'loanlength'};
379 $iteminformation->{'notforloan'}=$itemtype->{'notforloan'};
382 return($iteminformation);
387 $borrowers = &findborrower($env, $key);
388 print $borrowers->[0]{surname};
390 Looks up patrons and returns information about them.
394 C<$key> is either a card number or a string. C<&findborrower> tries to
395 look it up as a card number first. If that fails, C<&findborrower>
396 looks up all patrons whose surname begins with C<$key>.
398 C<$borrowers> is a reference-to-array. Each element is a
399 reference-to-hash whose keys are the fields of the borrowers table in
404 # If you really want to throw a monkey wrench into the works, change
405 # your last name to "V10000008" :-)
407 # FIXME - This is different from &C4::Borrower::findborrower, but I
408 # think that one's obsolete.
410 # returns an array of borrower hash references, given a cardnumber or a partial
412 my ($env, $key) = @_;
413 my $dbh = C4::Context->dbh;
415 my $q_key=$dbh->quote($key);
416 my $sth=$dbh->prepare("select * from borrowers where cardnumber=$q_key");
419 my ($borrower)=$sth->fetchrow_hashref;
420 push (@borrowers, $borrower);
422 $q_key=$dbh->quote("$key%");
424 $sth=$dbh->prepare("select * from borrowers where surname like $q_key");
426 while (my $borrower = $sth->fetchrow_hashref) {
427 push (@borrowers, $borrower);
437 ($dotransfer, $messages, $iteminformation) =
438 &transferbook($newbranch, $barcode, $ignore_reserves);
440 Transfers an item to a new branch. If the item is currently on loan,
441 it is automatically returned before the actual transfer.
443 C<$newbranch> is the code for the branch to which the item should be
446 C<$barcode> is the barcode of the item to be transferred.
448 If C<$ignore_reserves> is true, C<&transferbook> ignores reserves.
449 Otherwise, if an item is reserved, the transfer fails.
451 Returns three values:
453 C<$dotransfer> is true iff the transfer was successful.
455 C<$messages> is a reference-to-hash which may have any of the
462 There is no item in the catalog with the given barcode. The value is
467 The item's home branch is permanent. This doesn't prevent the item
468 from being transferred, though. The value is the code of the item's
471 =item C<DestinationEqualsHolding>
473 The item is already at the branch to which it is being transferred.
474 The transfer is nonetheless considered to have failed. The value
479 The item was on loan, and C<&transferbook> automatically returned it
480 before transferring it. The value is the borrower number of the patron
485 The item was reserved. The value is a reference-to-hash whose keys are
486 fields from the reserves table of the Koha database, and
487 C<biblioitemnumber>. It also has the key C<ResFound>, whose value is
488 either C<Waiting> or C<Reserved>.
490 =item C<WasTransferred>
492 The item was eligible to be transferred. Barring problems
493 communicating with the database, the transfer should indeed have
494 succeeded. The value should be ignored.
500 # FIXME - This function tries to do too much, and its API is clumsy.
501 # If it didn't also return books, it could be used to change the home
502 # branch of a book while the book is on loan.
504 # Is there any point in returning the item information? The caller can
505 # look that up elsewhere if ve cares.
507 # This leaves the ($dotransfer, $messages) tuple. This seems clumsy.
508 # If the transfer succeeds, that's all the caller should need to know.
509 # Thus, this function could simply return 1 or 0 to indicate success
510 # or failure, and set $C4::Circulation::Circ2::errmsg in case of
511 # failure. Or this function could return undef if successful, and an
512 # error message in case of failure (this would feel more like C than
515 # transfer book code....
516 my ($tbr, $barcode, $ignoreRs) = @_;
520 my $branches = getbranches();
521 my $iteminformation = getiteminformation(\%env, 0, $barcode);
523 if (not $iteminformation) {
524 $messages->{'BadBarcode'} = $barcode;
527 # get branches of book...
528 my $hbr = $iteminformation->{'homebranch'};
529 my $fbr = $iteminformation->{'holdingbranch'};
531 if ($branches->{$hbr}->{'PE'}) {
532 $messages->{'IsPermanent'} = $hbr;
534 # can't transfer book if is already there....
535 # FIXME - Why not? Shouldn't it trivially succeed?
537 $messages->{'DestinationEqualsHolding'} = 1;
540 # check if it is still issued to someone, return it...
541 my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
542 if ($currentborrower) {
543 returnbook($barcode, $fbr);
544 $messages->{'WasReturned'} = $currentborrower;
547 # FIXME - Don't call &CheckReserves unless $ignoreRs is true.
548 # That'll save a database query.
549 my ($resfound, $resrec) = CheckReserves($iteminformation->{'itemnumber'});
550 if ($resfound and not $ignoreRs) {
551 $resrec->{'ResFound'} = $resfound;
552 $messages->{'ResFound'} = $resrec;
555 #actually do the transfer....
557 dotransfer($iteminformation->{'itemnumber'}, $fbr, $tbr);
558 $messages->{'WasTransfered'} = 1;
560 return ($dotransfer, $messages, $iteminformation);
564 # FIXME - This is only used in &transferbook. Why bother making it a
567 my ($itm, $fbr, $tbr) = @_;
568 my $dbh = C4::Context->dbh;
569 $itm = $dbh->quote($itm);
570 $fbr = $dbh->quote($fbr);
571 $tbr = $dbh->quote($tbr);
572 #new entry in branchtransfers....
574 INSERT INTO branchtransfers
575 (itemnumber, frombranch, datearrived, tobranch)
576 VALUES ($itm, $fbr, now(), $tbr)
579 #update holdingbranch in items .....
582 SET datelastseen = now(),
584 WHERE items.itemnumber = $itm
591 ($iteminformation, $datedue, $rejected, $question, $questionnumber,
592 $defaultanswer, $message) =
593 &issuebook($env, $patroninformation, $barcode, $responses, $date);
595 Issue a book to a patron.
597 C<$env-E<gt>{usercode}> will be used in the usercode field of the
598 statistics table of the Koha database when this transaction is
601 C<$env-E<gt>{datedue}>, if given, specifies the date on which the book
602 is due back. This should be a string of the form "YYYY-MM-DD".
604 C<$env-E<gt>{branchcode}> is the code of the branch where this
605 transaction is taking place.
607 C<$patroninformation> is a reference-to-hash giving information about
608 the person borrowing the book. This is the first value returned by
609 C<&getpatroninformation>.
611 C<$barcode> is the bar code of the book being issued.
613 C<$responses> is a reference-to-hash. It represents the answers to the
614 questions asked by the C<$question>, C<$questionnumber>, and
615 C<$defaultanswer> return values (see below). The keys are numbers, and
616 the values can be "Y" or "N".
618 C<$date> is an optional date in the form "YYYY-MM-DD". If specified,
619 then only fines and charges up to that date will be considered when
620 checking to see whether the patron owes too much money to be lent a
623 C<&issuebook> returns an array of seven values:
625 C<$iteminformation> is a reference-to-hash describing the item just
626 issued. This in a form similar to that returned by
627 C<&getiteminformation>.
629 C<$datedue> is a string giving the date when the book is due, in the
632 C<$rejected> is either a string, or -1. If it is defined and is a
633 string, then the book may not be issued, and C<$rejected> gives the
634 reason for this. If C<$rejected> is -1, then the book may not be
635 issued, but no reason is given.
637 If there is a problem or question (e.g., the book is reserved for
638 another patron), then C<$question>, C<$questionnumber>, and
639 C<$defaultanswer> will be set. C<$questionnumber> indicates the
640 problem. C<$question> is a text string asking how to resolve the
641 problem, as a yes-or-no question, and C<$defaultanswer> is either "Y"
642 or "N", giving the default answer. The questions, their numbers, and
647 =item 1: "Issued to <name>. Mark as returned?" (Y)
649 =item 2: "Waiting for <patron> at <branch>. Allow issue?" (N)
651 =item 3: "Cancel reserve for <patron>?" (N)
653 =item 4: "Book is issued to this borrower. Renew?" (Y)
655 =item 5: "Reserved for <patron> at <branch> since <date>. Allow issue?" (N)
657 =item 6: "Set reserve for <patron> to waiting and transfer to <branch>?" (Y)
659 This is asked if the answer to question 5 was "N".
661 =item 7: "Cancel reserve for <patron>?" (N)
665 C<$message>, if defined, is an additional information message, e.g., a
670 # FIXME - The business with $responses is absurd. For one thing, these
671 # questions should have names, not numbers. For another, it'd be
672 # better to have the last argument be %extras. Then scripts can call
676 # -mark_returned => 0,
677 # -cancel_reserve => 1,
680 # and the script can use
681 # if (defined($extras{"-mark_returned"}) && $extras{"-mark_returned"})
682 # Heck, the $date argument should go in there as well.
684 # Also, there might be several reasons why a book can't be issued, but
685 # this API only supports asking one question at a time. Perhaps it'd
686 # be better to return a ref-to-list of problem IDs. Then the calling
687 # script can display a list of all of the problems at once.
689 # Is it this function's place to decide the default answer to the
690 # various questions? Why not document the various problems and allow
691 # the caller to decide?
693 my ($env, $patroninformation, $barcode, $responses, $date) = @_;
694 my $dbh = C4::Context->dbh;
695 my $iteminformation = getiteminformation($env, 0, $barcode);
697 my ($rejected,$question,$defaultanswer,$questionnumber, $noissue);
700 # See if there's any reason this book shouldn't be issued to this
702 SWITCH: { # FIXME - Yes, we know it's a switch. Tell us what it's for.
703 if ($patroninformation->{'gonenoaddress'}) {
704 $rejected="Patron is gone, with no known address.";
707 if ($patroninformation->{'lost'}) {
708 $rejected="Patron's card has been reported lost.";
711 if ($patroninformation->{'debarred'}) {
712 $rejected="Patron is Debarred";
715 my $amount = checkaccount($env,$patroninformation->{'borrowernumber'}, $dbh,$date);
716 # FIXME - "5" shouldn't be hardcoded. An Italian library might
717 # be generous enough to lend a book to a patron even if he
718 # does still owe them 5 lire.
719 if ($amount > 5 && $patroninformation->{'categorycode'} ne 'L' &&
720 $patroninformation->{'categorycode'} ne 'W' &&
721 $patroninformation->{'categorycode'} ne 'I' &&
722 $patroninformation->{'categorycode'} ne 'B' &&
723 $patroninformation->{'categorycode'} ne 'P') {
724 # FIXME - What do these category codes mean?
725 $rejected = sprintf "Patron owes \$%.02f.", $amount;
728 # FIXME - This sort of error-checking should be placed closer
729 # to the test; in this case, this error-checking should be
730 # done immediately after the call to &getiteminformation.
731 unless ($iteminformation) {
732 $rejected = "$barcode is not a valid barcode.";
735 if ($iteminformation->{'notforloan'} == 1) {
736 $rejected="Item not for loan.";
739 if ($iteminformation->{'wthdrawn'} == 1) {
740 $rejected="Item withdrawn.";
743 if ($iteminformation->{'restricted'} == 1) {
744 $rejected="Restricted item.";
747 if ($iteminformation->{'itemtype'} eq 'REF') {
748 $rejected="Reference item: Not for loan.";
751 my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
752 if ($currentborrower eq $patroninformation->{'borrowernumber'}) {
753 # Already issued to current borrower. Ask whether the loan should
755 my ($renewstatus) = renewstatus($env,$dbh,$patroninformation->{'borrowernumber'}, $iteminformation->{'itemnumber'});
756 if ($renewstatus == 0) {
757 $rejected="No more renewals allowed for this item.";
760 if ($responses->{4} eq '') {
762 $question = "Book is issued to this borrower.\nRenew?";
763 $defaultanswer = 'Y';
765 } elsif ($responses->{4} eq 'Y') {
766 my $charge = calc_charges($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'});
768 createcharge($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'}, $charge);
769 $iteminformation->{'charge'} = $charge;
771 &UpdateStats($env,$env->{'branchcode'},'renew',$charge,'',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'});
772 renewbook($env,$dbh, $patroninformation->{'borrowernumber'}, $iteminformation->{'itemnumber'});
779 } elsif ($currentborrower ne '') {
780 # This book is currently on loan, but not to the person
781 # who wants to borrow it now.
782 my ($currborrower, $cbflags) = getpatroninformation($env,$currentborrower,0);
783 if ($responses->{1} eq '') {
785 $question = "Issued to $currborrower->{'firstname'} $currborrower->{'surname'} ($currborrower->{'cardnumber'}).\nMark as returned?";
788 } elsif ($responses->{1} eq 'Y') {
789 returnbook($iteminformation->{'barcode'}, $env->{'branch'});
796 # See if the item is on reserve.
797 my ($restype, $res) = CheckReserves($iteminformation->{'itemnumber'});
799 my $resbor = $res->{'borrowernumber'};
800 if ($resbor eq $patroninformation->{'borrowernumber'}) {
801 # The item is on reserve to the current patron
803 } elsif ($restype eq "Waiting") {
804 # The item is on reserve and waiting, but has been
805 # reserved by some other patron.
806 my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
807 my $branches = getbranches();
808 my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
809 if ($responses->{2} eq '') {
811 # FIXME - Assumes HTML
812 $question="<font color=red>Waiting</font> for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}) at $branchname \nAllow issue?";
815 } elsif ($responses->{2} eq 'N') {
819 if ($responses->{3} eq '') {
821 $question="Cancel reserve for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})?";
824 } elsif ($responses->{3} eq 'Y') {
825 CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
828 } elsif ($restype eq "Reserved") {
829 # The item is on reserve for someone else.
830 my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
831 my $branches = getbranches();
832 my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
833 if ($responses->{5} eq '') {
835 $question="Reserved for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}) since $res->{'reservedate'} \nAllow issue?";
838 } elsif ($responses->{5} eq 'N') {
839 if ($responses->{6} eq '') {
841 $question="Set reserve for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}) to waiting and transfer to $branchname?";
843 } elsif ($responses->{6} eq 'Y') {
844 my $tobrcd = ReserveWaiting($res->{'itemnumber'}, $res->{'borrowernumber'});
845 transferbook($tobrcd, $barcode, 1);
846 $message = "Item should now be waiting at $branchname";
851 if ($responses->{7} eq '') {
853 $question="Cancel reserve for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})?";
856 } elsif ($responses->{7} eq 'Y') {
857 CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
864 unless (($question) || ($rejected) || ($noissue)) {
865 # There's no reason why the item can't be issued.
866 # FIXME - my $loanlength = $iteminformation->{loanlength} || 21;
868 if ($iteminformation->{'loanlength'}) {
869 $loanlength=$iteminformation->{'loanlength'};
871 my $ti=time; # FIXME - Never used
872 my $datedue=time+($loanlength)*86400;
873 # FIXME - Could just use POSIX::strftime("%Y-%m-%d", localtime);
874 # That's what it's for. Or, in this case:
875 # $dateduef = $env->{datedue} ||
876 # strftime("%Y-%m-%d", localtime(time +
877 # $loanlength * 86400));
878 my @datearr = localtime($datedue);
879 $dateduef = (1900+$datearr[5])."-".($datearr[4]+1)."-".$datearr[3];
880 if ($env->{'datedue'}) {
881 $dateduef=$env->{'datedue'};
883 $dateduef=~ s/2001\-4\-25/2001\-4\-26/;
884 # FIXME - What's this for? Leftover from debugging?
886 # Record in the database the fact that the book was issued.
887 # FIXME - Use $dbh->do();
888 my $sth=$dbh->prepare("insert into issues (borrowernumber, itemnumber, date_due, branchcode) values ($patroninformation->{'borrowernumber'}, $iteminformation->{'itemnumber'}, '$dateduef', '$env->{'branchcode'}')");
891 $iteminformation->{'issues'}++;
892 # FIXME - Use $dbh->do();
893 $sth=$dbh->prepare("update items set issues=$iteminformation->{'issues'},datelastseen=now() where itemnumber=$iteminformation->{'itemnumber'}");
896 # If it costs to borrow this book, charge it to the patron's account.
897 my $charge=calc_charges($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'});
899 createcharge($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'}, $charge);
900 $iteminformation->{'charge'}=$charge;
902 # Record the fact that this book was issued.
903 &UpdateStats($env,$env->{'branchcode'},'issue',$charge,'',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'});
905 if ($iteminformation->{'charge'}) {
906 $message=sprintf "Rental charge of \$%.02f applies.", $iteminformation->{'charge'};
908 return ($iteminformation, $dateduef, $rejected, $question, $questionnumber, $defaultanswer, $message);
915 ($doreturn, $messages, $iteminformation, $borrower) =
916 &returnbook($barcode, $branch);
920 C<$barcode> is the bar code of the book being returned. C<$branch> is
921 the code of the branch where the book is being returned.
923 C<&returnbook> returns a list of four items:
925 C<$doreturn> is true iff the return succeeded.
927 C<$messages> is a reference-to-hash giving the reason for failure:
933 No item with this barcode exists. The value is C<$barcode>.
937 The book is not currently on loan. The value is C<$barcode>.
941 The book's home branch is a permanent collection. If you have borrowed
942 this book, you are not allowed to return it. The value is the code for
943 the book's home branch.
947 This book has been withdrawn/cancelled. The value should be ignored.
951 The item was reserved. The value is a reference-to-hash whose keys are
952 fields from the reserves table of the Koha database, and
953 C<biblioitemnumber>. It also has the key C<ResFound>, whose value is
954 either C<Waiting>, C<Reserved>, or 0.
958 C<$borrower> is a reference-to-hash, giving information about the
959 patron who last borrowed the book.
963 # FIXME - This API is bogus. There's no need to return $borrower and
964 # $iteminformation; the caller can ask about those separately, if it
965 # cares (it'd be inefficient to make two database calls instead of
966 # one, but &getpatroninformation and &getiteminformation can be
967 # memoized if this is an issue).
969 # The ($doreturn, $messages) tuple is redundant: if the return
970 # succeeded, that's all the caller needs to know. So &returnbook can
971 # return 1 and 0 on success and failure, and set
972 # $C4::Circulation::Circ2::errmsg to indicate the error. Or it can
973 # return undef for success, and an error message on error (though this
974 # is more C-ish than Perl-ish).
976 my ($barcode, $branch) = @_;
980 # get information on item
981 my ($iteminformation) = getiteminformation(\%env, 0, $barcode);
982 if (not $iteminformation) {
983 $messages->{'BadBarcode'} = $barcode;
987 my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
988 if ((not $currentborrower) && $doreturn) {
989 $messages->{'NotIssued'} = $barcode;
992 # check if the book is in a permanent collection....
993 my $hbr = $iteminformation->{'homebranch'};
994 my $branches = getbranches();
995 if ($branches->{$hbr}->{'PE'}) {
996 $messages->{'IsPermanent'} = $hbr;
998 # check that the book has been cancelled
999 if ($iteminformation->{'wthdrawn'}) {
1000 $messages->{'wthdrawn'} = 1;
1003 # update issues, thereby returning book (should push this out into another subroutine
1004 my ($borrower) = getpatroninformation(\%env, $currentborrower, 0);
1006 doreturn($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
1007 $messages->{'WasReturned'}; # FIXME - This does nothing
1009 ($borrower) = getpatroninformation(\%env, $currentborrower, 0);
1010 # transfer book to the current branch
1011 my ($transfered, $mess, $item) = transferbook($branch, $barcode, 1);
1012 if ($transfered) { # FIXME - perl -wc complains about this line.
1013 $messages->{'WasTransfered'}; # FIXME - This does nothing
1015 # fix up the accounts.....
1016 if ($iteminformation->{'itemlost'}) {
1017 # Mark the item as not being lost.
1018 updateitemlost($iteminformation->{'itemnumber'});
1019 fixaccountforlostandreturned($iteminformation, $borrower);
1020 $messages->{'WasLost'}; # FIXME - This does nothing
1022 # fix up the overdues in accounts...
1023 fixoverduesonreturn($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
1024 # find reserves.....
1025 my ($resfound, $resrec) = CheckReserves($iteminformation->{'itemnumber'});
1027 my $tobrcd = ReserveWaiting($resrec->{'itemnumber'}, $resrec->{'borrowernumber'});
1028 $resrec->{'ResFound'} = $resfound;
1029 $messages->{'ResFound'} = $resrec;
1032 # Record the fact that this book was returned.
1033 UpdateStats(\%env, $branch ,'return','0','',$iteminformation->{'itemnumber'});
1034 return ($doreturn, $messages, $iteminformation, $borrower);
1038 # Takes a borrowernumber and an itemnuber.
1039 # Updates the 'issues' table to mark the item as returned (assuming
1040 # that it's currently on loan to the given borrower. Otherwise, the
1041 # item remains on loan.
1042 # Updates items.datelastseen for the item.
1044 # FIXME - This is only used in &returnbook. Why make it into a
1045 # separate function?
1047 my ($brn, $itm) = @_;
1048 my $dbh = C4::Context->dbh;
1049 $brn = $dbh->quote($brn);
1050 $itm = $dbh->quote($itm);
1051 my $query = "update issues set returndate = now() where (borrowernumber = $brn)
1052 and (itemnumber = $itm) and (returndate is null)";
1053 my $sth = $dbh->prepare($query);
1056 $query="update items set datelastseen=now() where itemnumber=$itm";
1057 $sth=$dbh->prepare($query);
1064 # Marks an item as not being lost.
1068 my $dbh = C4::Context->dbh;
1073 WHERE itemnumber = $itemno
1078 sub fixaccountforlostandreturned {
1079 my ($iteminfo, $borrower) = @_;
1081 my $dbh = C4::Context->dbh;
1082 my $itm = $dbh->quote($iteminfo->{'itemnumber'});
1083 # check for charge made for lost book
1084 my $query = "select * from accountlines where (itemnumber = $itm)
1085 and (accounttype='L' or accounttype='Rep') order by date desc";
1086 my $sth = $dbh->prepare($query);
1088 if (my $data = $sth->fetchrow_hashref) {
1089 # writeoff this amount
1091 my $amount = $data->{'amount'};
1092 my $acctno = $data->{'accountno'};
1094 if ($data->{'amountoutstanding'} == $amount) {
1095 $offset = $data->{'amount'};
1098 $offset = $amount - $data->{'amountoutstanding'};
1099 $amountleft = $data->{'amountoutstanding'} - $amount;
1101 my $uquery = "update accountlines set accounttype = 'LR',amountoutstanding='0'
1102 where (borrowernumber = '$data->{'borrowernumber'}')
1103 and (itemnumber = $itm) and (accountno = '$acctno') ";
1104 my $usth = $dbh->prepare($uquery);
1107 #check if any credit is left if so writeoff other accounts
1108 my $nextaccntno = getnextacctno(\%env,$data->{'borrowernumber'},$dbh);
1109 if ($amountleft < 0){
1112 if ($amountleft > 0){
1113 my $query = "select * from accountlines where (borrowernumber = '$data->{'borrowernumber'}')
1114 and (amountoutstanding >0) order by date";
1115 my $msth = $dbh->prepare($query);
1117 # offset transactions
1120 while (($accdata=$msth->fetchrow_hashref) and ($amountleft>0)){
1121 if ($accdata->{'amountoutstanding'} < $amountleft) {
1123 $amountleft -= $accdata->{'amountoutstanding'};
1125 $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
1128 my $thisacct = $accdata->{'accountno'};
1129 my $updquery = "update accountlines set amountoutstanding= '$newamtos'
1130 where (borrowernumber = '$data->{'borrowernumber'}')
1131 and (accountno='$thisacct')";
1132 my $usth = $dbh->prepare($updquery);
1135 $updquery = "insert into accountoffsets
1136 (borrowernumber, accountno, offsetaccount, offsetamount)
1138 ('$data->{'borrowernumber'}','$accdata->{'accountno'}','$nextaccntno','$newamtos')";
1139 $usth = $dbh->prepare($updquery);
1145 if ($amountleft > 0){
1148 my $desc="Book Returned ".$iteminfo->{'barcode'};
1149 $uquery = "insert into accountlines
1150 (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding)
1151 values ('$data->{'borrowernumber'}','$nextaccntno',now(),0-$amount,'$desc',
1153 $usth = $dbh->prepare($uquery);
1156 $uquery = "insert into accountoffsets
1157 (borrowernumber, accountno, offsetaccount, offsetamount)
1158 values ($borrower->{'borrowernumber'},$data->{'accountno'},$nextaccntno,$offset)";
1159 $usth = $dbh->prepare($uquery);
1162 $uquery = "update items set paidfor='' where itemnumber=$itm";
1163 $usth = $dbh->prepare($uquery);
1172 sub fixoverduesonreturn {
1173 my ($brn, $itm) = @_;
1174 my $dbh = C4::Context->dbh;
1175 $itm = $dbh->quote($itm);
1176 $brn = $dbh->quote($brn);
1177 # check for overdue fine
1178 my $query = "select * from accountlines where (borrowernumber=$brn)
1179 and (itemnumber = $itm) and (accounttype='FU' or accounttype='O')";
1180 my $sth = $dbh->prepare($query);
1182 # alter fine to show that the book has been returned
1183 if (my $data = $sth->fetchrow_hashref) {
1184 my $query = "update accountlines set accounttype='F' where (borrowernumber = $brn)
1185 and (itemnumber = $itm) and (acccountno='$data->{'accountno'}')";
1186 my $usth=$dbh->prepare($query);
1196 # NOTE!: If you change this function, be sure to update the POD for
1197 # &getpatroninformation.
1199 # $flags = &patronflags($env, $patron, $dbh);
1202 # {message} Message showing patron's credit or debt
1203 # {noissues} Set if patron owes >$5.00
1204 # {GNA} Set if patron gone w/o address
1205 # {message} "Borrower has no valid address"
1207 # {LOST} Set if patron's card reported lost
1208 # {message} Message to this effect
1210 # {DBARRED} Set is patron is debarred
1211 # {message} Message to this effect
1213 # {NOTES} Set if patron has notes
1214 # {message} Notes about patron
1215 # {ODUES} Set if patron has overdue books
1217 # {itemlist} ref-to-array: list of overdue books
1218 # {itemlisttext} Text list of overdue items
1219 # {WAITING} Set if there are items available that the
1221 # {message} Message to this effect
1222 # {itemlist} ref-to-array: list of available items
1224 # Original subroutine for Circ2.pm
1226 my ($env, $patroninformation, $dbh) = @_;
1227 my $amount = checkaccount($env, $patroninformation->{'borrowernumber'}, $dbh);
1230 $flaginfo{'message'}= sprintf "Patron owes \$%.02f", $amount;
1232 $flaginfo{'noissues'} = 1;
1234 $flags{'CHARGES'} = \%flaginfo;
1235 } elsif ($amount < 0){
1237 $flaginfo{'message'} = sprintf "Patron has credit of \$%.02f", -$amount;
1238 $flags{'CHARGES'} = \%flaginfo;
1240 if ($patroninformation->{'gonenoaddress'} == 1) {
1242 $flaginfo{'message'} = 'Borrower has no valid address.';
1243 $flaginfo{'noissues'} = 1;
1244 $flags{'GNA'} = \%flaginfo;
1246 if ($patroninformation->{'lost'} == 1) {
1248 $flaginfo{'message'} = 'Borrower\'s card reported lost.';
1249 $flaginfo{'noissues'} = 1;
1250 $flags{'LOST'} = \%flaginfo;
1252 if ($patroninformation->{'debarred'} == 1) {
1254 $flaginfo{'message'} = 'Borrower is Debarred.';
1255 $flaginfo{'noissues'} = 1;
1256 $flags{'DBARRED'} = \%flaginfo;
1258 if ($patroninformation->{'borrowernotes'}) {
1260 $flaginfo{'message'} = "$patroninformation->{'borrowernotes'}";
1261 $flags{'NOTES'} = \%flaginfo;
1263 my ($odues, $itemsoverdue)
1264 = checkoverdues($env, $patroninformation->{'borrowernumber'}, $dbh);
1267 $flaginfo{'message'} = "Yes";
1268 $flaginfo{'itemlist'} = $itemsoverdue;
1269 foreach (sort {$a->{'date_due'} cmp $b->{'date_due'}} @$itemsoverdue) {
1270 $flaginfo{'itemlisttext'}.="$_->{'date_due'} $_->{'barcode'} $_->{'title'} \n";
1272 $flags{'ODUES'} = \%flaginfo;
1274 my ($nowaiting, $itemswaiting)
1275 = CheckWaiting($patroninformation->{'borrowernumber'});
1276 if ($nowaiting > 0) {
1278 $flaginfo{'message'} = "Reserved items available";
1279 $flaginfo{'itemlist'} = $itemswaiting;
1280 $flags{'WAITING'} = \%flaginfo;
1288 # From Main.pm, modified to return a list of overdueitems, in addition to a count
1289 #checks whether a borrower has overdue items
1290 my ($env, $bornum, $dbh)=@_;
1291 my @datearr = localtime;
1292 my $today = ($datearr[5] + 1900)."-".($datearr[4]+1)."-".$datearr[3];
1295 my $query = "SELECT * FROM issues,biblio,biblioitems,items
1296 WHERE items.biblioitemnumber = biblioitems.biblioitemnumber
1297 AND items.biblionumber = biblio.biblionumber
1298 AND issues.itemnumber = items.itemnumber
1299 AND issues.borrowernumber = $bornum
1300 AND issues.returndate is NULL
1301 AND issues.date_due < '$today'";
1302 my $sth = $dbh->prepare($query);
1304 while (my $data = $sth->fetchrow_hashref) {
1305 push (@overdueitems, $data);
1309 return ($count, \@overdueitems);
1313 sub currentborrower {
1314 # Original subroutine for Circ2.pm
1315 my ($itemnumber) = @_;
1316 my $dbh = C4::Context->dbh;
1317 my $q_itemnumber = $dbh->quote($itemnumber);
1318 my $sth=$dbh->prepare("select borrowers.borrowernumber from
1319 issues,borrowers where issues.itemnumber=$q_itemnumber and
1320 issues.borrowernumber=borrowers.borrowernumber and issues.returndate is
1323 my ($borrower) = $sth->fetchrow;
1327 # FIXME - Not exported, but used in 'updateitem.pl' anyway.
1329 # Stolen from Main.pm
1330 # Check for reserves for biblio
1331 my ($env,$dbh,$itemnum)=@_;
1333 my $query = "select * from reserves,items
1334 where (items.itemnumber = '$itemnum')
1335 and (reserves.cancellationdate is NULL)
1336 and (items.biblionumber = reserves.biblionumber)
1337 and ((reserves.found = 'W')
1338 or (reserves.found is null))
1340 my $sth = $dbh->prepare($query);
1343 my $data=$sth->fetchrow_hashref;
1344 while ($data && $resbor eq '') {
1346 my $const = $data->{'constrainttype'};
1347 if ($const eq "a") {
1348 $resbor = $data->{'borrowernumber'};
1351 my $cquery = "select * from reserveconstraints,items
1352 where (borrowernumber='$data->{'borrowernumber'}')
1353 and reservedate='$data->{'reservedate'}'
1354 and reserveconstraints.biblionumber='$data->{'biblionumber'}'
1355 and (items.itemnumber=$itemnum and
1356 items.biblioitemnumber = reserveconstraints.biblioitemnumber)";
1357 my $csth = $dbh->prepare($cquery);
1359 if (my $cdata=$csth->fetchrow_hashref) {$found = 1;}
1360 if ($const eq 'o') {
1361 if ($found eq 1) {$resbor = $data->{'borrowernumber'};}
1363 if ($found eq 0) {$resbor = $data->{'borrowernumber'};}
1367 $data=$sth->fetchrow_hashref;
1370 return ($resbor,$resrec);
1375 $issues = ¤tissues($env, $borrower);
1377 Returns a list of books currently on loan to a patron.
1379 If C<$env-E<gt>{todaysissues}> is set and true, C<¤tissues> only
1380 returns information about books issued today. If
1381 C<$env-E<gt>{nottodaysissues}> is set and true, C<¤tissues> only
1382 returns information about books issued before today. If both are
1383 specified, C<$env-E<gt>{todaysissues}> is ignored. If neither is
1384 specified, C<¤tissues> returns all of the patron's issues.
1386 C<$borrower->{borrowernumber}> is the borrower number of the patron
1387 whose issues we want to list.
1389 C<¤tissues> returns a PHP-style array: C<$issues> is a
1390 reference-to-hash whose keys are integers in the range 1...I<n>, where
1391 I<n> is the number of items on issue (either today or before today).
1392 C<$issues-E<gt>{I<n>}> is a reference-to-hash whose keys are all of
1393 the fields of the biblio, biblioitems, items, and issues fields of the
1394 Koha database for that particular item.
1399 # New subroutine for Circ2.pm
1400 my ($env, $borrower) = @_;
1401 my $dbh = C4::Context->dbh;
1404 my $borrowernumber = $borrower->{'borrowernumber'};
1407 # Figure out whether to get the books issued today, or earlier.
1408 # FIXME - $env->{todaysissues} and $env->{nottodaysissues} can
1409 # both be specified, but are mutually-exclusive. This is bogus.
1410 # Make this a flag. Or better yet, return everything in (reverse)
1411 # chronological order and let the caller figure out which books
1412 # were issued today.
1413 if ($env->{'todaysissues'}) {
1415 # $today = POSIX::strftime("%Y%m%d", localtime);
1416 # FIXME - Since $today will be used in either case, move it
1417 # out of the two if-blocks.
1418 my @datearr = localtime(time());
1419 my $today = (1900+$datearr[5]).sprintf "%02d", ($datearr[4]+1).sprintf "%02d", $datearr[3];
1420 # FIXME - MySQL knows about dates. Just use
1421 # and issues.timestamp = curdate();
1422 $crit=" and issues.timestamp like '$today%' ";
1424 if ($env->{'nottodaysissues'}) {
1426 # $today = POSIX::strftime("%Y%m%d", localtime);
1427 # FIXME - Since $today will be used in either case, move it
1428 # out of the two if-blocks.
1429 my @datearr = localtime(time());
1430 my $today = (1900+$datearr[5]).sprintf "%02d", ($datearr[4]+1).sprintf "%02d", $datearr[3];
1431 # FIXME - MySQL knows about dates. Just use
1432 # and issues.timestamp < curdate();
1433 $crit=" and !(issues.timestamp like '$today%') ";
1436 # FIXME - Does the caller really need every single field from all
1438 my $select="select * from issues,items,biblioitems,biblio where
1439 borrowernumber='$borrowernumber' and issues.itemnumber=items.itemnumber and
1440 items.biblionumber=biblio.biblionumber and
1441 items.biblioitemnumber=biblioitems.biblioitemnumber and returndate is null
1442 $crit order by issues.date_due";
1444 my $sth=$dbh->prepare($select);
1446 while (my $data = $sth->fetchrow_hashref) {
1447 # FIXME - The Dewey code is a string, not a number.
1448 $data->{'dewey'}=~s/0*$//;
1449 ($data->{'dewey'} == 0) && ($data->{'dewey'}='');
1451 # $todaysdate = POSIX::strftime("%Y%m%d", localtime)
1452 # or better yet, just reuse $today which was calculated above.
1453 # This function isn't going to run until midnight, is it?
1455 # $todaysdate = POSIX::strftime("%Y-%m-%d", localtime)
1456 # if ($data->{'date_due'} lt $todaysdate)
1458 # Either way, the date should be be formatted outside of the
1460 my @datearr = localtime(time());
1461 my $todaysdate = (1900+$datearr[5]).sprintf ("%0.2d", ($datearr[4]
1462 +1)).sprintf ("%0.2d", $datearr[3]);
1463 my $datedue=$data->{'date_due'};
1465 if ($datedue < $todaysdate) {
1466 $data->{'overdue'}=1;
1468 my $itemnumber=$data->{'itemnumber'};
1469 # FIXME - Consecutive integers as hash keys? You have GOT to
1470 # be kidding me! Use an array, fercrissakes!
1471 $currentissues{$counter}=$data;
1475 return(\%currentissues);
1480 $issues = &getissues($borrowernumber);
1482 Returns the set of books currently on loan to a patron.
1484 C<$borrowernumber> is the patron's borrower number.
1486 C<&getissues> returns a PHP-style array: C<$issues> is a
1487 reference-to-hash whose keys are integers in the range 0..I<n>-1,
1488 where I<n> is the number of books the patron currently has on loan.
1490 The values of C<$issues> are references-to-hash whose keys are
1491 selected fields from the issues, items, biblio, and biblioitems tables
1492 of the Koha database.
1497 # New subroutine for Circ2.pm
1498 my ($borrower) = @_;
1499 my $dbh = C4::Context->dbh;
1500 my $borrowernumber = $borrower->{'borrowernumber'};
1502 my $select = "SELECT issues.timestamp AS timestamp,
1503 issues.date_due AS date_due,
1504 items.biblionumber AS biblionumber,
1505 items.itemnumber AS itemnumber,
1506 items.barcode AS barcode,
1507 biblio.title AS title,
1508 biblio.author AS author,
1509 biblioitems.dewey AS dewey,
1510 itemtypes.description AS itemtype,
1511 biblioitems.subclass AS subclass
1512 FROM issues,items,biblioitems,biblio, itemtypes
1513 WHERE issues.borrowernumber = ?
1514 AND issues.itemnumber = items.itemnumber
1515 AND items.biblionumber = biblio.biblionumber
1516 AND items.biblioitemnumber = biblioitems.biblioitemnumber
1517 AND itemtypes.itemtype = biblioitems.itemtype
1518 AND issues.returndate IS NULL
1519 ORDER BY issues.date_due";
1521 my $sth=$dbh->prepare($select);
1522 $sth->execute($borrowernumber);
1524 while (my $data = $sth->fetchrow_hashref) {
1525 $data->{'dewey'} =~ s/0*$//;
1526 ($data->{'dewey'} == 0) && ($data->{'dewey'} = '');
1527 # FIXME - The Dewey code is a string, not a number.
1528 # FIXME - Use POSIX::strftime to get a text version of today's
1529 # date. That's what it's for.
1530 # FIXME - Move the date calculation outside of the loop.
1531 my @datearr = localtime(time());
1532 my $todaysdate = (1900+$datearr[5]).sprintf ("%0.2d", ($datearr[4]+1)).sprintf ("%0.2d", $datearr[3]);
1534 # FIXME - Instead of converting the due date to YYYYMMDD, just
1536 # $todaysdate = POSIX::strftime("%Y-%m-%d", localtime);
1538 # if ($date->{date_due} lt $todaysdate)
1539 my $datedue = $data->{'date_due'};
1541 if ($datedue < $todaysdate) {
1542 $data->{'overdue'} = 1;
1544 $currentissues{$counter} = $data;
1546 # FIXME - This is ludicrous. If you want to return an
1547 # array of values, just use an array. That's what
1548 # they're there for.
1551 return(\%currentissues);
1556 #Stolen from Main.pm
1557 # check for reserves waiting
1558 my ($env,$dbh,$bornum)=@_;
1560 my $query = "select * from reserves
1561 where (borrowernumber = '$bornum')
1562 and (reserves.found='W') and cancellationdate is NULL";
1563 my $sth = $dbh->prepare($query);
1566 if (my $data=$sth->fetchrow_hashref) {
1567 $itemswaiting[$cnt] =$data;
1571 return ($cnt,\@itemswaiting);
1575 # FIXME - This is nearly-identical to &C4::Accounts::checkaccount
1577 # Stolen from Accounts.pm
1578 #take borrower number
1579 #check accounts and list amounts owing
1580 my ($env,$bornumber,$dbh,$date)=@_;
1581 my $select="SELECT SUM(amountoutstanding) AS total
1583 WHERE borrowernumber = $bornumber
1584 AND amountoutstanding<>0";
1586 $select.=" AND date < '$date'";
1589 my $sth=$dbh->prepare($select);
1591 my $data=$sth->fetchrow_hashref;
1592 my $total = $data->{'total'};
1594 # output(1,2,"borrower owes $total");
1596 # # output(1,2,"borrower owes $total");
1598 # reconcileaccount($env,$dbh,$bornumber,$total);
1605 # FIXME - This is identical to &C4::Circulation::Renewals::renewstatus.
1606 # Pick one and stick with it.
1608 # Stolen from Renewals.pm
1609 # check renewal status
1610 my ($env,$dbh,$bornum,$itemno)=@_;
1613 my $q1 = "select * from issues
1614 where (borrowernumber = '$bornum')
1615 and (itemnumber = '$itemno')
1616 and returndate is null";
1617 my $sth1 = $dbh->prepare($q1);
1619 if (my $data1 = $sth1->fetchrow_hashref) {
1620 my $q2 = "select renewalsallowed from items,biblioitems,itemtypes
1621 where (items.itemnumber = '$itemno')
1622 and (items.biblioitemnumber = biblioitems.biblioitemnumber)
1623 and (biblioitems.itemtype = itemtypes.itemtype)";
1624 my $sth2 = $dbh->prepare($q2);
1626 if (my $data2=$sth2->fetchrow_hashref) {
1627 $renews = $data2->{'renewalsallowed'};
1629 if ($renews > $data1->{'renewals'}) {
1639 # Stolen from Renewals.pm
1640 # mark book as renewed
1641 my ($env,$dbh,$bornum,$itemno,$datedue)=@_;
1642 $datedue=$env->{'datedue'};
1643 if ($datedue eq "" ) {
1645 my $query= "Select * from biblioitems,items,itemtypes
1646 where (items.itemnumber = '$itemno')
1647 and (biblioitems.biblioitemnumber = items.biblioitemnumber)
1648 and (biblioitems.itemtype = itemtypes.itemtype)";
1649 my $sth=$dbh->prepare($query);
1651 if (my $data=$sth->fetchrow_hashref) {
1652 $loanlength = $data->{'loanlength'}
1656 my $datedu = time + ($loanlength * 86400);
1657 my @datearr = localtime($datedu);
1658 $datedue = (1900+$datearr[5])."-".($datearr[4]+1)."-".$datearr[3];
1660 my @date = split("-",$datedue);
1661 my $odatedue = ($date[2]+0)."-".($date[1]+0)."-".$date[0];
1662 my $issquery = "select * from issues where borrowernumber='$bornum' and
1663 itemnumber='$itemno' and returndate is null";
1664 my $sth=$dbh->prepare($issquery);
1666 my $issuedata=$sth->fetchrow_hashref;
1668 my $renews = $issuedata->{'renewals'} +1;
1669 my $updquery = "update issues
1670 set date_due = '$datedue', renewals = '$renews'
1671 where borrowernumber='$bornum' and
1672 itemnumber='$itemno' and returndate is null";
1673 $sth=$dbh->prepare($updquery);
1680 # FIXME - This is almost, but not quite, identical to
1681 # &C4::Circulation::Issues::calc_charges and
1682 # &C4::Circulation::Renewals2::calc_charges.
1683 # Pick one and stick with it.
1685 # Stolen from Issues.pm
1686 # calculate charges due
1687 my ($env, $dbh, $itemno, $bornum)=@_;
1692 # open (FILE,">>/tmp/charges");
1694 my $q1 = "select itemtypes.itemtype,rentalcharge from items,biblioitems,itemtypes
1695 where (items.itemnumber ='$itemno')
1696 and (biblioitems.biblioitemnumber = items.biblioitemnumber)
1697 and (biblioitems.itemtype = itemtypes.itemtype)";
1698 my $sth1= $dbh->prepare($q1);
1699 # print FILE "$q1\n";
1701 if (my $data1=$sth1->fetchrow_hashref) {
1702 $item_type = $data1->{'itemtype'};
1703 $charge = $data1->{'rentalcharge'};
1704 # print FILE "charge is $charge\n";
1705 my $q2 = "select rentaldiscount from borrowers,categoryitem
1706 where (borrowers.borrowernumber = '$bornum')
1707 and (borrowers.categorycode = categoryitem.categorycode)
1708 and (categoryitem.itemtype = '$item_type')";
1709 my $sth2=$dbh->prepare($q2);
1712 if (my $data2=$sth2->fetchrow_hashref) {
1713 my $discount = $data2->{'rentaldiscount'};
1714 # print FILE "discount is $discount";
1715 if ($discount eq 'NULL') {
1718 $charge = ($charge *(100 - $discount)) / 100;
1724 return ($charge, $itemtype);
1727 # FIXME - A virtually identical function appears in
1728 # C4::Circulation::Issues. Pick one and stick with it.
1730 #Stolen from Issues.pm
1731 my ($env,$dbh,$itemno,$bornum,$charge) = @_;
1732 my $nextaccntno = getnextacctno($env,$bornum,$dbh);
1733 my $sth = $dbh->prepare(<<EOT);
1734 INSERT INTO accountlines
1735 (borrowernumber, itemnumber, accountno,
1736 date, amount, description, accounttype,
1739 now(), ?, 'Rental', 'Rent',
1742 $sth->execute($bornum, $itemno, $nextaccntno, $charge, $charge);
1748 # Stolen from Accounts.pm
1749 my ($env,$bornumber,$dbh)=@_;
1750 my $nextaccntno = 1;
1751 my $query = "select * from accountlines where (borrowernumber = '$bornumber') order by accountno desc";
1752 my $sth = $dbh->prepare($query);
1754 if (my $accdata=$sth->fetchrow_hashref){
1755 $nextaccntno = $accdata->{'accountno'} + 1;
1758 return($nextaccntno);
1763 ($status, $record) = &find_reserves($itemnumber);
1765 Looks up an item in the reserves.
1767 C<$itemnumber> is the itemnumber to look up.
1769 C<$status> is true iff the search was successful.
1771 C<$record> is a reference-to-hash describing the reserve. Its keys are
1772 the fields from the reserves table of the Koha database.
1776 # FIXME - This API is bogus: just return the record, or undef if none
1778 # FIXME - There's also a &C4::Circulation::Returns::find_reserves, but
1779 # that one looks rather different.
1781 # Stolen from Returns.pm
1784 my $dbh = C4::Context->dbh;
1785 my ($itemdata) = getiteminformation(\%env, $itemno,0);
1786 my $bibno = $dbh->quote($itemdata->{'biblionumber'});
1787 my $bibitm = $dbh->quote($itemdata->{'biblioitemnumber'});
1788 my $query = "select * from reserves where ((found = 'W') or (found is null))
1789 and biblionumber = $bibno and cancellationdate is NULL
1790 order by priority, reservedate ";
1791 my $sth = $dbh->prepare($query);
1798 # FIXME - I'm not really sure what's going on here, but since we
1799 # only want one result, wouldn't it be possible (and far more
1800 # efficient) to do something clever in SQL that only returns one
1802 while (($resrec = $sth->fetchrow_hashref) && (not $resfound)) {
1803 # FIXME - Unlike Pascal, Perl allows you to exit loops
1804 # early. Take out the "&& (not $resfound)" and just
1805 # use "last" at the appropriate point in the loop.
1806 # (Oh, and just in passing: if you'd used "!" instead
1807 # of "not", you wouldn't have needed the parentheses.)
1809 my $brn = $dbh->quote($resrec->{'borrowernumber'});
1810 my $rdate = $dbh->quote($resrec->{'reservedate'});
1811 my $bibno = $dbh->quote($resrec->{'biblionumber'});
1812 if ($resrec->{'found'} eq "W") {
1813 if ($resrec->{'itemnumber'} eq $itemno) {
1817 # FIXME - Use 'elsif' to avoid unnecessary indentation.
1818 if ($resrec->{'constrainttype'} eq "a") {
1821 my $conquery = "select * from reserveconstraints where borrowernumber = $brn
1822 and reservedate = $rdate and biblionumber = $bibno and biblioitemnumber = $bibitm";
1823 my $consth = $dbh->prepare($conquery);
1825 if (my $conrec = $consth->fetchrow_hashref) {
1826 if ($resrec->{'constrainttype'} eq "o") {
1834 my $updquery = "update reserves set found = 'W', itemnumber = '$itemno'
1835 where borrowernumber = $brn and reservedate = $rdate and biblionumber = $bibno";
1836 my $updsth = $dbh->prepare($updquery);
1839 # FIXME - "last;" here to break out of the loop early.
1843 return ($resfound,$lastrec);
1853 Koha Developement team <info@koha.org>