minor changes to comments etc....
[koha.git] / C4 / Circulation / Circ2.pm
1 package C4::Circulation::Circ2;
2
3 #package to deal with Returns
4 #written 3/11/99 by olwen@katipo.co.nz
5
6
7 # Copyright 2000-2002 Katipo Communications
8 #
9 # This file is part of Koha.
10 #
11 # Koha is free software; you can redistribute it and/or modify it under the
12 # terms of the GNU General Public License as published by the Free Software
13 # Foundation; either version 2 of the License, or (at your option) any later
14 # version.
15 #
16 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
17 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License along with
21 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
22 # Suite 330, Boston, MA  02111-1307 USA
23
24 use strict;
25 # use warnings;
26 require Exporter;
27 use DBI;
28 use C4::Database;
29 #use C4::Accounts;
30 #use C4::InterfaceCDK;
31 #use C4::Circulation::Main;
32 #use C4::Format;
33 #use C4::Circulation::Renewals;
34 #use C4::Scan;
35 use C4::Stats;
36 use C4::Reserves2;
37 #use C4::Search;
38 #use C4::Print;
39
40 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
41   
42 # set the version for version checking
43 $VERSION = 0.01;
44     
45 @ISA = qw(Exporter);
46 @EXPORT = qw(&getbranches &getprinters &getpatroninformation &currentissues &getissues &getiteminformation &findborrower &issuebook &returnbook &find_reserves &transferbook &decode
47 calc_charges);
48 %EXPORT_TAGS = ( );     # eg: TAG => [ qw!name1 name2! ],
49                   
50 # your exported package globals go here,
51 # as well as any optionally exported functions
52
53 @EXPORT_OK   = qw($Var1 %Hashit);
54
55
56 # non-exported package globals go here
57 #use vars qw(@more $stuff);
58         
59 # initalize package globals, first exported ones
60
61 my $Var1   = '';
62 my %Hashit = ();
63                     
64 # then the others (which are still accessible as $Some::Module::stuff)
65 my $stuff  = '';
66 my @more   = ();
67         
68 # all file-scoped lexicals must be created before
69 # the functions below that use them.
70                 
71 # file-private lexicals go here
72 my $priv_var    = '';
73 my %secret_hash = ();
74                             
75 # here's a file-private function as a closure,
76 # callable as &$priv_func;  it cannot be prototyped.
77 my $priv_func = sub {
78   # stuff goes here.
79 };
80                                                     
81 # make all your functions, whether exported or not;
82
83
84 sub getbranches {
85 # returns a reference to a hash of references to branches...
86     my %branches;
87     my $dbh=&C4Connect;  
88     my $sth=$dbh->prepare("select * from branches");
89     $sth->execute;
90     while (my $branch=$sth->fetchrow_hashref) {
91         my $tmp = $branch->{'branchcode'}; my $brc = $dbh->quote($tmp);
92         my $query = "select categorycode from branchrelations where branchcode = $brc";
93         my $nsth = $dbh->prepare($query);
94         $nsth->execute;
95         while (my ($cat) = $nsth->fetchrow_array) {
96             $branch->{$cat} = 1;
97         }
98         $nsth->finish;
99         $branches{$branch->{'branchcode'}}=$branch;
100     }
101     $dbh->disconnect;
102     return (\%branches);
103 }
104
105
106 sub getprinters {
107     my ($env) = @_;
108     my %printers;
109     my $dbh=&C4Connect;  
110     my $sth=$dbh->prepare("select * from printers");
111     $sth->execute;
112     while (my $printer=$sth->fetchrow_hashref) {
113         $printers{$printer->{'printqueue'}}=$printer;
114     }
115     $dbh->disconnect;
116     return (\%printers);
117 }
118
119
120
121 sub getpatroninformation {
122 # returns 
123     my ($env, $borrowernumber,$cardnumber) = @_;
124     my $dbh=&C4Connect;  
125     my $query;
126     my $sth;
127     if ($borrowernumber) {
128         $query = "select * from borrowers where borrowernumber=$borrowernumber";
129     } elsif ($cardnumber) {
130         $query = "select * from borrowers where cardnumber=$cardnumber";
131     } else {
132         $env->{'apierror'} = "invalid borrower information passed to getpatroninformation subroutine";
133         return();
134     }
135     $env->{'mess'} = $query;
136     $sth = $dbh->prepare($query);
137     $sth->execute;
138     my $borrower = $sth->fetchrow_hashref;
139     my $flags = patronflags($env, $borrower, $dbh);
140     $sth->finish;
141     $dbh->disconnect;
142     $borrower->{'flags'}=$flags;
143     return($borrower, $flags);
144 }
145
146 sub decode {
147     my ($encoded) = @_;
148     my $seq = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-';
149     my @s = map { index($seq,$_); } split(//,$encoded);
150     my $l = ($#s+1) % 4;
151     if ($l)
152     {
153         if ($l == 1)
154         {
155             print "Error!";
156             return;
157         }
158         $l = 4-$l;
159         $#s += $l;
160     }
161     my $r = '';
162     while ($#s >= 0)
163     {
164         my $n = (($s[0] << 6 | $s[1]) << 6 | $s[2]) << 6 | $s[3];
165         $r .=chr(($n >> 16) ^ 67) .
166              chr(($n >> 8 & 255) ^ 67) .
167              chr(($n & 255) ^ 67);
168         @s = @s[4..$#s];
169     }
170     $r = substr($r,0,length($r)-$l);
171     return $r;
172 }
173
174
175
176
177 sub getiteminformation {
178 # returns a hash of item information given either the itemnumber or the barcode
179     my ($env, $itemnumber, $barcode) = @_;
180     my $dbh=&C4Connect;
181     my $sth;
182     if ($itemnumber) {
183         $sth=$dbh->prepare("select * from biblio,items,biblioitems where items.itemnumber=$itemnumber and biblio.biblionumber=items.biblionumber and biblioitems.biblioitemnumber = items.biblioitemnumber");
184     } elsif ($barcode) {
185         my $q_barcode=$dbh->quote($barcode);
186         $sth=$dbh->prepare("select * from biblio,items,biblioitems where items.barcode=$q_barcode and biblio.biblionumber=items.biblionumber and biblioitems.biblioitemnumber = items.biblioitemnumber");
187     } else {
188         $env->{'apierror'}="getiteminformation() subroutine must be called with either an itemnumber or a barcode";
189         # Error condition.  
190         return();
191     }
192     $sth->execute;
193     my $iteminformation=$sth->fetchrow_hashref;
194     $sth->finish;
195     if ($iteminformation) {
196         $sth=$dbh->prepare("select date_due from issues where itemnumber=$iteminformation->{'itemnumber'} and isnull(returndate)");
197         $sth->execute;
198         my ($date_due) = $sth->fetchrow;
199         $iteminformation->{'date_due'}=$date_due;
200         $sth->finish;
201         #$iteminformation->{'dewey'}=~s/0*$//;
202         ($iteminformation->{'dewey'} == 0) && ($iteminformation->{'dewey'}='');
203         $sth=$dbh->prepare("select * from itemtypes where itemtype='$iteminformation->{'itemtype'}'");
204         $sth->execute;
205         my $itemtype=$sth->fetchrow_hashref;
206         $iteminformation->{'loanlength'}=$itemtype->{'loanlength'};
207         $iteminformation->{'notforloan'}=$itemtype->{'notforloan'};
208         $sth->finish;
209     }
210     $dbh->disconnect;
211     return($iteminformation);
212 }
213
214 sub findborrower {
215 # returns an array of borrower hash references, given a cardnumber or a partial
216 # surname 
217     my ($env, $key) = @_;
218     my $dbh=&C4Connect;
219     my @borrowers;
220     my $q_key=$dbh->quote($key);
221     my $sth=$dbh->prepare("select * from borrowers where cardnumber=$q_key");
222     $sth->execute;
223     if ($sth->rows) {
224         my ($borrower)=$sth->fetchrow_hashref;
225         push (@borrowers, $borrower);
226     } else {
227         $q_key=$dbh->quote("$key%");
228         $sth->finish;
229         $sth=$dbh->prepare("select * from borrowers where surname like $q_key");
230         $sth->execute;
231         while (my $borrower = $sth->fetchrow_hashref) {
232             push (@borrowers, $borrower);
233         }
234     }
235     $sth->finish;
236     $dbh->disconnect;
237     return(\@borrowers);
238 }
239
240
241 sub transferbook {
242 # transfer book code....
243     my ($tbr, $barcode, $ignoreRs) = @_;
244     my $messages;
245     my %env;
246     my $dotransfer = 1;
247     my $branches = getbranches();
248     my $iteminformation = getiteminformation(\%env, 0, $barcode);
249 # bad barcode..
250     if (not $iteminformation) {
251         $messages->{'BadBarcode'} = $barcode;
252         $dotransfer = 0;
253     }
254 # get branches of book...
255     my $hbr = $iteminformation->{'homebranch'};
256     my $fbr = $iteminformation->{'holdingbranch'};
257 # if is permanent...
258     if ($branches->{$hbr}->{'PE'}) {
259         $messages->{'IsPermanent'} = $hbr;
260     }
261 # cant transfer book if is already there....
262     if ($fbr eq $tbr) {
263         $messages->{'DestinationEqualsHolding'} = 1;
264         $dotransfer = 0;
265     }
266 # check if it is still issued to someone, return it...
267     my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
268     if ($currentborrower) {
269         returnbook($barcode, $fbr);
270         $messages->{'WasReturned'} = $currentborrower;
271     }
272 # find reserves.....
273     my ($resfound, $resrec) = CheckReserves($iteminformation->{'itemnumber'});
274     if ($resfound and not $ignoreRs) {
275         $resrec->{'ResFound'} = $resfound;
276         $messages->{'ResFound'} = $resrec;
277         $dotransfer = 0;
278     }
279 #actually do the transfer....
280     if ($dotransfer) {
281         dotransfer($iteminformation->{'itemnumber'}, $fbr, $tbr);
282         $messages->{'WasTransfered'} = 1;
283     } 
284     return ($dotransfer, $messages, $iteminformation);
285 }
286
287 sub dotransfer {
288     my ($itm, $fbr, $tbr) = @_;
289     my $dbh = &C4Connect;
290     $itm = $dbh->quote($itm);
291     $fbr = $dbh->quote($fbr);
292     $tbr = $dbh->quote($tbr);
293     #new entry in branchtransfers....
294     my $query = "insert into branchtransfers (itemnumber, frombranch, datearrived, tobranch) 
295                                       values($itm, $fbr, now(), $tbr)";
296     my $sth = $dbh->prepare($query);
297     $sth->execute; 
298     $sth->finish;
299     #update holdingbranch in items .....
300     $query = "update items set datelastseen = now(), holdingbranch=$tbr where items.itemnumber=$itm";
301     $sth = $dbh->prepare($query);
302     $sth->execute; 
303     $sth->finish;
304     $dbh->disconnect;
305     return;
306 }
307
308
309 sub issuebook {
310     my ($env, $patroninformation, $barcode, $responses, $date) = @_;
311     my $dbh=&C4Connect;
312     my $iteminformation = getiteminformation($env, 0, $barcode);
313     my ($datedue);
314     my ($rejected,$question,$defaultanswer,$questionnumber, $noissue);
315     my $message;
316     SWITCH: {
317         if ($patroninformation->{'gonenoaddress'}) {
318             $rejected="Patron is gone, with no known address.";
319             last SWITCH;
320         }
321         if ($patroninformation->{'lost'}) {
322             $rejected="Patron's card has been reported lost.";
323             last SWITCH;
324         }
325         if ($patroninformation->{'debarred'}) {
326             $rejected="Patron is Debarred";
327             last SWITCH;
328         }
329         my $amount = checkaccount($env,$patroninformation->{'borrowernumber'}, $dbh,$date);
330         if ($amount > 5 && $patroninformation->{'categorycode'} ne 'L' &&
331                            $patroninformation->{'categorycode'} ne 'W' &&
332                            $patroninformation->{'categorycode'} ne 'I' && 
333                            $patroninformation->{'categorycode'} ne 'B' &&
334                            $patroninformation->{'categorycode'} ne 'P') {
335             $rejected = sprintf "Patron owes \$%.02f.", $amount;
336             last SWITCH;
337         }
338         unless ($iteminformation) {
339             $rejected = "$barcode is not a valid barcode.";
340             last SWITCH;
341         }
342         if ($iteminformation->{'notforloan'} == 1) {
343             $rejected="Reference item: not for loan.";
344             last SWITCH;
345         }
346         if ($iteminformation->{'wthdrawn'} == 1) {
347             $rejected="Item withdrawn.";
348             last SWITCH;
349         }
350         if ($iteminformation->{'restricted'} == 1) {
351             $rejected="Restricted item.";
352             last SWITCH;
353         }
354         my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
355         if ($currentborrower eq $patroninformation->{'borrowernumber'}) {
356 # Already issued to current borrower
357             my ($renewstatus) = renewstatus($env,$dbh,$patroninformation->{'borrowernumber'}, $iteminformation->{'itemnumber'});
358             if ($renewstatus == 0) {
359                 $rejected="No more renewals allowed for this item.";
360                 last SWITCH;
361             } else {
362                 if ($responses->{4} eq '') {
363                     $questionnumber = 4;
364                     $question = "Book is issued to this borrower.\nRenew?";
365                     $defaultanswer = 'Y';
366                     last SWITCH;
367                 } elsif ($responses->{4} eq 'Y') {
368                     my $charge = calc_charges($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'});
369                     if ($charge > 0) {
370                         createcharge($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'}, $charge);
371                         $iteminformation->{'charge'} = $charge;
372                     }
373                     &UpdateStats($env,$env->{'branchcode'},'renew',$charge,'',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'});
374                     renewbook($env,$dbh, $patroninformation->{'borrowernumber'}, $iteminformation->{'itemnumber'});
375                     $noissue=1;
376                 } else {
377                     $rejected=-1;
378                     last SWITCH;
379                 }
380             }
381         } elsif ($currentborrower ne '') {
382             my ($currborrower, $cbflags) = getpatroninformation($env,$currentborrower,0);
383             if ($responses->{1} eq '') {
384                 $questionnumber=1;
385                 $question = "Issued to $currborrower->{'firstname'} $currborrower->{'surname'} ($currborrower->{'cardnumber'}).\nMark as returned?";
386                 $defaultanswer='Y';
387                 last SWITCH;
388             } elsif ($responses->{1} eq 'Y') {
389                 returnbook($iteminformation->{'barcode'}, $env->{'branch'});
390             } else {
391                 $rejected=-1;
392                 last SWITCH;
393             }
394         }
395
396         my ($restype, $res) = CheckReserves($iteminformation->{'itemnumber'});
397         if ($restype) {
398             my $resbor = $res->{'borrowernumber'};
399             if ($resbor eq $patroninformation->{'borrowernumber'}) {
400                 FillReserve($res);
401             } elsif ($restype eq "Waiting") {
402                 my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
403                 my $branches = getbranches();
404                 my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
405                 if ($responses->{2} eq '') {
406                     $questionnumber=2;
407                     $question="<font color=red>Waiting</font> for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}) at $branchname \nAllow issue?";
408                     $defaultanswer='N';
409                     last SWITCH;
410                 } elsif ($responses->{2} eq 'N') {
411                     $rejected=-1;
412                     last SWITCH;
413                 } else {
414                     if ($responses->{3} eq '') {
415                         $questionnumber=3;
416                         $question="Cancel reserve for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})?";
417                         $defaultanswer='N';
418                         last SWITCH;
419                     } elsif ($responses->{3} eq 'Y') {
420                         CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
421                     }
422                 }
423             } elsif ($restype eq "Reserved") {
424                 my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
425                 my $branches = getbranches();
426                 my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
427                 if ($responses->{5} eq '') {
428                     $questionnumber=5;
429                     $question="Reserved for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}) since $res->{'reservedate'} \nAllow issue?";
430                     $defaultanswer='N';
431                     last SWITCH;
432                 } elsif ($responses->{5} eq 'N') {
433                     if ($responses->{6} eq '') {
434                         $questionnumber=6;
435                         $question="Set reserve for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}) to waiting and transfer to $branchname?";
436                         $defaultanswer='N';
437                     } elsif ($responses->{6} eq 'Y') {
438                         my $tobrcd = ReserveWaiting($res->{'itemnumber'}, $res->{'borrowernumber'});
439                         transferbook($tobrcd, $barcode, 1);
440                         $message = "Item should now be waiting at $branchname";
441                     }
442                     $rejected=-1;
443                     last SWITCH;
444                 } else {
445                     if ($responses->{7} eq '') {
446                         $questionnumber=7;
447                         $question="Cancel reserve for $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})?";
448                         $defaultanswer='N';
449                         last SWITCH;
450                     } elsif ($responses->{7} eq 'Y') {
451                         CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
452                     }
453                 }
454             }
455         }
456     }
457     my $dateduef;
458     unless (($question) || ($rejected) || ($noissue)) {
459         my $loanlength=21;
460         if ($iteminformation->{'loanlength'}) {
461             $loanlength=$iteminformation->{'loanlength'};
462         }
463         my $ti=time;
464         my $datedue=time+($loanlength)*86400;
465         my @datearr = localtime($datedue);
466         $dateduef = (1900+$datearr[5])."-".($datearr[4]+1)."-".$datearr[3];
467         if ($env->{'datedue'}) {
468             $dateduef=$env->{'datedue'};
469         }
470         $dateduef=~ s/2001\-4\-25/2001\-4\-26/;
471         my $sth=$dbh->prepare("insert into issues (borrowernumber, itemnumber, date_due, branchcode) values ($patroninformation->{'borrowernumber'}, $iteminformation->{'itemnumber'}, '$dateduef', '$env->{'branchcode'}')");
472         $sth->execute;
473         $sth->finish;
474         $iteminformation->{'issues'}++;
475         $sth=$dbh->prepare("update items set issues=$iteminformation->{'issues'},datelastseen=now() where itemnumber=$iteminformation->{'itemnumber'}");
476         $sth->execute;
477         $sth->finish;
478         my $charge=calc_charges($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'});
479         if ($charge > 0) {
480             createcharge($env, $dbh, $iteminformation->{'itemnumber'}, $patroninformation->{'borrowernumber'}, $charge);
481             $iteminformation->{'charge'}=$charge;
482         }
483         &UpdateStats($env,$env->{'branchcode'},'issue',$charge,'',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'});
484     }
485     if ($iteminformation->{'charge'}) {
486         $message=sprintf "Rental charge of \$%.02f applies.", $iteminformation->{'charge'};
487     }
488     $dbh->disconnect;
489     return ($iteminformation, $dateduef, $rejected, $question, $questionnumber, $defaultanswer, $message);
490 }
491
492
493
494 sub returnbook {
495     my ($barcode, $branch) = @_;
496     my %env;
497     my $messages;
498     my $doreturn = 1;
499 # get information on item
500     my ($iteminformation) = getiteminformation(\%env, 0, $barcode);
501     if (not $iteminformation) {
502         $messages->{'BadBarcode'} = $barcode;
503         $doreturn = 0;
504     }
505 # find the borrower
506     my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
507     if ((not $currentborrower) && $doreturn) {
508         $messages->{'NotIssued'} = $barcode;
509         $doreturn = 0;
510     }
511 # check if the book is in a permanent collection....
512     my $hbr = $iteminformation->{'homebranch'};
513     my $branches = getbranches();
514     if ($branches->{$hbr}->{'PE'}) {
515         $messages->{'IsPermanent'} = $hbr;
516     }
517 # check that the book has been cancelled
518     if ($iteminformation->{'wthdrawn'}) {
519         $messages->{'wthdrawn'} = 1;
520         $doreturn = 0;
521     }
522 # update issues, thereby returning book (should push this out into another subroutine
523     my ($borrower) = getpatroninformation(\%env, $currentborrower, 0);
524     if ($doreturn) {
525         doreturn($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
526         $messages->{'WasReturned'};
527     }
528     ($borrower) = getpatroninformation(\%env, $currentborrower, 0);
529 # transfer book
530     my ($transfered, $mess, $item) = transferbook($branch, $barcode);
531     if ($transfered) {
532         $messages->{'WasTransfered'};
533     }
534 # fix up the accounts.....
535     if ($iteminformation->{'itemlost'}) {
536         updateitemlost($iteminformation->{'itemnumber'});
537         fixaccountforlostandreturned($iteminformation, $borrower);
538         $messages->{'WasLost'};
539     }
540 # fix up the overdues in accounts...
541     fixoverduesonreturn($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
542 # find reserves.....
543     my ($resfound, $resrec) = CheckReserves($iteminformation->{'itemnumber'});
544     if ($resfound) {
545         $resrec->{'ResFound'} = $resfound;
546         $messages->{'ResFound'} = $resrec;
547     }
548 # update stats?
549     UpdateStats(\%env, $branch ,'return','0','',$iteminformation->{'itemnumber'});
550     return ($doreturn, $messages, $iteminformation, $borrower);
551 }
552
553
554 sub doreturn {
555     my ($brn, $itm) = @_;
556     my $dbh=&C4Connect;
557     $brn = $dbh->quote($brn);
558     $itm = $dbh->quote($itm);
559     my $query = "update issues set returndate = now() where (borrowernumber = $brn) 
560         and (itemnumber = $itm) and (returndate is null)";
561     my $sth = $dbh->prepare($query);
562     $sth->execute;
563     $sth->finish;
564     $query="update items set datelastseen=now() where itemnumber=$itm";
565     $sth=$dbh->prepare($query);
566     $sth->execute;
567     $sth->finish;
568     $dbh->disconnect;
569     return;
570 }
571
572 sub updateitemlost{
573   my ($itemno)=@_;
574   my $dbh=&C4Connect;
575   my $query="update items set itemlost=0 where itemnumber=$itemno";
576   my $sth=$dbh->prepare($query);
577   $sth->execute;
578   $sth->finish;
579 }
580
581 sub fixaccountforlostandreturned {
582     my ($iteminfo, $borrower) = @_;
583     my %env;
584     my $dbh=&C4Connect;
585     my $itm = $dbh->quote($iteminfo->{'itemnumber'});
586 # check for charge made for lost book
587     my $query = "select * from accountlines where (itemnumber = $itm) 
588                           and (accounttype='L' or accounttype='Rep') order by date desc";
589     my $sth = $dbh->prepare($query);
590     $sth->execute;
591     if (my $data = $sth->fetchrow_hashref) {
592 # writeoff this amount 
593         my $offset;
594         my $amount = $data->{'amount'};
595         my $acctno = $data->{'accountno'};
596         my $amountleft;
597         if ($data->{'amountoutstanding'} == $amount) {
598             $offset = $data->{'amount'};
599             $amountleft = 0;
600         } else {
601             $offset = $amount - $data->{'amountoutstanding'};
602             $amountleft = $data->{'amountoutstanding'} - $amount;
603         }
604         my $uquery = "update accountlines set accounttype = 'LR',amountoutstanding='0'
605                   where (borrowernumber = '$data->{'borrowernumber'}')
606                   and (itemnumber = $itm) and (accountno = '$acctno') ";
607         my $usth = $dbh->prepare($uquery);
608         $usth->execute;
609         $usth->finish;
610 #check if any credit is left if so writeoff other accounts
611         my $nextaccntno = getnextacctno(\%env,$data->{'borrowernumber'},$dbh);
612         if ($amountleft < 0){
613             $amountleft*=-1;
614         }
615         if ($amountleft > 0){
616             my $query = "select * from accountlines where (borrowernumber = '$data->{'borrowernumber'}') 
617                                                       and (amountoutstanding >0) order by date";
618             my $msth = $dbh->prepare($query);
619             $msth->execute;
620       # offset transactions
621             my $newamtos;
622             my $accdata;
623             while (($accdata=$msth->fetchrow_hashref) and ($amountleft>0)){
624                 if ($accdata->{'amountoutstanding'} < $amountleft) {
625                     $newamtos = 0;
626                     $amountleft = $amountleft - $accdata->{'amountoutstanding'};
627                 }  else {
628                     $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
629                     $amountleft = 0;
630                 }
631                 my $thisacct = $accdata->{'accountno'};
632                 my $updquery = "update accountlines set amountoutstanding= '$newamtos'
633                                  where (borrowernumber = '$data->{'borrowernumber'}') 
634                                    and (accountno='$thisacct')";
635                 my $usth = $dbh->prepare($updquery);
636                 $usth->execute;
637                 $usth->finish;
638                 $updquery = "insert into accountoffsets 
639                           (borrowernumber, accountno, offsetaccount,  offsetamount)
640                           values
641                           ('$data->{'borrowernumber'}','$accdata->{'accountno'}','$nextaccntno','$newamtos')";
642                 $usth = $dbh->prepare($updquery);
643                 $usth->execute;
644                 $usth->finish;
645             }
646             $msth->finish;
647         }
648         if ($amountleft > 0){
649             $amountleft*=-1;
650         }
651         my $desc="Book Returned ".$iteminfo->{'barcode'};
652         $uquery = "insert into accountlines
653                   (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding)
654                   values ('$data->{'borrowernumber'}','$nextaccntno',now(),0-$amount,'$desc',
655                   'CR',$amountleft)";
656         $usth = $dbh->prepare($uquery);
657         $usth->execute;     
658         $usth->finish;
659         $uquery = "insert into accountoffsets
660                   (borrowernumber, accountno, offsetaccount,  offsetamount)
661                   values ($borrower->{'borrowernumber'},$data->{'accountno'},$nextaccntno,$offset)";
662         $usth = $dbh->prepare($uquery);
663         $usth->execute;
664         $usth->finish;
665         $uquery = "update items set paidfor='' where itemnumber=$itm";
666         $usth = $dbh->prepare($uquery);
667         $usth->execute;
668         $usth->finish;
669     }
670     $sth->finish;
671     return;
672 }
673
674 sub fixoverduesonreturn {
675     my ($brn, $itm) = @_;
676     my $dbh=&C4Connect;
677     $itm = $dbh->quote($itm);
678     $brn = $dbh->quote($brn);
679 # check for overdue fine
680     my $query = "select * from accountlines where (borrowernumber=$brn) 
681                            and (itemnumber = $itm) and (accounttype='FU' or accounttype='O')";
682     my $sth = $dbh->prepare($query);
683     $sth->execute;
684 # alter fine to show that the book has been returned
685     if (my $data = $sth->fetchrow_hashref) {
686         my $query = "update accountlines set accounttype='F' where (borrowernumber = $brn) 
687                            and (itemnumber = $itm) and (acccountno='$data->{'accountno'}')";
688         my $usth=$dbh->prepare($query);
689         $usth->execute();
690         $usth->finish();
691     }
692     $sth->finish;
693     return;
694 }
695
696 sub patronflags {
697 # Original subroutine for Circ2.pm
698     my %flags;
699     my ($env, $patroninformation, $dbh) = @_;
700     my $amount = checkaccount($env, $patroninformation->{'borrowernumber'}, $dbh);
701     if ($amount > 0) { 
702         my %flaginfo;
703         $flaginfo{'message'}= sprintf "Patron owes \$%.02f", $amount; 
704         if ($amount > 5) {
705             $flaginfo{'noissues'} = 1;
706         }
707         $flags{'CHARGES'} = \%flaginfo;
708     } elsif ($amount < 0){
709        my %flaginfo;
710        $amount = $amount*-1;
711        $flaginfo{'message'} = sprintf "Patron has credit of \$%.02f", $amount;
712         $flags{'CHARGES'} = \%flaginfo;
713     }
714     if ($patroninformation->{'gonenoaddress'} == 1) {
715         my %flaginfo;
716         $flaginfo{'message'} = 'Borrower has no valid address.'; 
717         $flaginfo{'noissues'} = 1;
718         $flags{'GNA'} = \%flaginfo;
719     }
720     if ($patroninformation->{'lost'} == 1) {
721         my %flaginfo;
722         $flaginfo{'message'} = 'Borrower\'s card reported lost.'; 
723         $flaginfo{'noissues'} = 1;
724         $flags{'LOST'} = \%flaginfo;
725     }
726     if ($patroninformation->{'debarred'} == 1) {
727         my %flaginfo;
728         $flaginfo{'message'} = 'Borrower is Debarred.'; 
729         $flaginfo{'noissues'} = 1;
730         $flags{'DBARRED'} = \%flaginfo;
731     }
732     if ($patroninformation->{'borrowernotes'}) {
733         my %flaginfo;
734         $flaginfo{'message'} = "$patroninformation->{'borrowernotes'}";
735         $flags{'NOTES'} = \%flaginfo;
736     }
737     my ($odues, $itemsoverdue) 
738                   = checkoverdues($env, $patroninformation->{'borrowernumber'}, $dbh);
739     if ($odues > 0) {
740         my %flaginfo;
741         $flaginfo{'message'} = "Yes";
742         $flaginfo{'itemlist'} = $itemsoverdue;
743         foreach (sort {$a->{'date_due'} cmp $b->{'date_due'}} @$itemsoverdue) {
744             $flaginfo{'itemlisttext'}.="$_->{'date_due'} $_->{'barcode'} $_->{'title'} \n";
745         }
746         $flags{'ODUES'} = \%flaginfo;
747     }
748     my ($nowaiting, $itemswaiting) 
749                   = CheckWaiting($patroninformation->{'borrowernumber'});
750     if ($nowaiting > 0) {
751         my %flaginfo;
752         $flaginfo{'message'} = "Reserved items available";
753         $flaginfo{'itemlist'} = $itemswaiting;
754         $flags{'WAITING'} = \%flaginfo;
755     }
756     return(\%flags);
757 }
758
759
760 sub checkoverdues {
761 # From Main.pm, modified to return a list of overdueitems, in addition to a count
762   #checks whether a borrower has overdue items
763   my ($env, $bornum, $dbh)=@_;
764   my @datearr = localtime;
765   my $today = ($datearr[5] + 1900)."-".($datearr[4]+1)."-".$datearr[3];
766   my @overdueitems;
767   my $count = 0;
768   my $query = "SELECT * FROM issues,biblio,biblioitems,items 
769                        WHERE items.biblioitemnumber = biblioitems.biblioitemnumber 
770                          AND items.biblionumber     = biblio.biblionumber 
771                          AND issues.itemnumber      = items.itemnumber 
772                          AND issues.borrowernumber  = $bornum 
773                          AND issues.returndate is NULL 
774                          AND issues.date_due < '$today'";
775   my $sth = $dbh->prepare($query);
776   $sth->execute;
777   while (my $data = $sth->fetchrow_hashref) {
778       push (@overdueitems, $data);
779       $count++;
780   }
781   $sth->finish;
782   return ($count, \@overdueitems);
783 }
784
785 sub currentborrower {
786 # Original subroutine for Circ2.pm
787     my ($itemnumber) = @_;
788     my $dbh = &C4Connect;
789     my $q_itemnumber = $dbh->quote($itemnumber);
790     my $sth=$dbh->prepare("select borrowers.borrowernumber from
791     issues,borrowers where issues.itemnumber=$q_itemnumber and
792     issues.borrowernumber=borrowers.borrowernumber and issues.returndate is
793     NULL");
794     $sth->execute;
795     my ($borrower) = $sth->fetchrow;
796     return($borrower);
797 }
798
799 sub checkreserve {
800 # Stolen from Main.pm
801   # Check for reserves for biblio 
802   my ($env,$dbh,$itemnum)=@_;
803   my $resbor = "";
804   my $query = "select * from reserves,items 
805     where (items.itemnumber = '$itemnum')
806     and (reserves.cancellationdate is NULL)
807     and (items.biblionumber = reserves.biblionumber)
808     and ((reserves.found = 'W')
809     or (reserves.found is null)) 
810     order by priority";
811   my $sth = $dbh->prepare($query);
812   $sth->execute();
813   my $resrec;
814   my $data=$sth->fetchrow_hashref;
815   while ($data && $resbor eq '') {
816     $resrec=$data;
817     my $const = $data->{'constrainttype'};
818     if ($const eq "a") {
819       $resbor = $data->{'borrowernumber'};
820     } else {
821       my $found = 0;
822       my $cquery = "select * from reserveconstraints,items 
823          where (borrowernumber='$data->{'borrowernumber'}') 
824          and reservedate='$data->{'reservedate'}'
825          and reserveconstraints.biblionumber='$data->{'biblionumber'}'
826          and (items.itemnumber=$itemnum and 
827          items.biblioitemnumber = reserveconstraints.biblioitemnumber)";
828       my $csth = $dbh->prepare($cquery);
829       $csth->execute;
830       if (my $cdata=$csth->fetchrow_hashref) {$found = 1;}
831       if ($const eq 'o') {
832         if ($found eq 1) {$resbor = $data->{'borrowernumber'};}
833       } else {
834         if ($found eq 0) {$resbor = $data->{'borrowernumber'};}
835       }
836       $csth->finish();
837     }
838     $data=$sth->fetchrow_hashref;
839   }
840   $sth->finish;
841   return ($resbor,$resrec);
842 }
843
844 sub currentissues {
845 # New subroutine for Circ2.pm
846     my ($env, $borrower) = @_;
847     my $dbh=&C4Connect;
848     my %currentissues;
849     my $counter=1;
850     my $borrowernumber = $borrower->{'borrowernumber'};
851     my $crit='';
852     if ($env->{'todaysissues'}) {
853         my @datearr = localtime(time());
854         my $today = (1900+$datearr[5]).sprintf "%02d", ($datearr[4]+1).sprintf "%02d", $datearr[3];
855         $crit=" and issues.timestamp like '$today%' ";
856     }
857     if ($env->{'nottodaysissues'}) {
858         my @datearr = localtime(time());
859         my $today = (1900+$datearr[5]).sprintf "%02d", ($datearr[4]+1).sprintf "%02d", $datearr[3];
860         $crit=" and !(issues.timestamp like '$today%') ";
861     }
862     my $select="select * from issues,items,biblioitems,biblio where
863        borrowernumber='$borrowernumber' and issues.itemnumber=items.itemnumber and
864        items.biblionumber=biblio.biblionumber and
865        items.biblioitemnumber=biblioitems.biblioitemnumber and returndate is null
866        $crit order by issues.timestamp desc";
867 #    print $select;
868     my $sth=$dbh->prepare($select);
869     $sth->execute;
870     while (my $data = $sth->fetchrow_hashref) {
871         $data->{'dewey'}=~s/0*$//;
872         ($data->{'dewey'} == 0) && ($data->{'dewey'}='');
873         my @datearr = localtime(time());
874         my $todaysdate = (1900+$datearr[5]).sprintf ("%0.2d", ($datearr[4]
875         +1)).sprintf ("%0.2d", $datearr[3]);
876         my $datedue=$data->{'date_due'};
877         $datedue=~s/-//g;
878         if ($datedue < $todaysdate) {
879             $data->{'overdue'}=1;
880         }
881         my $itemnumber=$data->{'itemnumber'};
882         $currentissues{$counter}=$data;
883         $counter++;
884     }
885     $sth->finish;
886     $dbh->disconnect;
887     return(\%currentissues);
888 }
889 sub getissues {
890 # New subroutine for Circ2.pm
891     my ($borrower) = @_;
892     my $dbh=&C4Connect;
893     my $borrowernumber = $borrower->{'borrowernumber'};
894     my $brn =$dbh->quote($borrowernumber);
895     my %currentissues;
896     my $select = "select issues.timestamp, issues.date_due, items.biblionumber,
897                          items.barcode, biblio.title, biblio.author, biblioitems.dewey, 
898                          biblioitems.subclass 
899                     from issues,items,biblioitems,biblio
900                    where issues.borrowernumber = $brn 
901                      and issues.itemnumber = items.itemnumber 
902                      and items.biblionumber = biblio.biblionumber 
903                      and items.biblioitemnumber = biblioitems.biblioitemnumber 
904                      and issues.returndate is null
905                          order by issues.date_due";
906 #    print $select;
907     my $sth=$dbh->prepare($select);
908     $sth->execute;
909     my $counter = 0;
910     while (my $data = $sth->fetchrow_hashref) {
911         $data->{'dewey'} =~ s/0*$//;
912         ($data->{'dewey'} == 0) && ($data->{'dewey'} = '');
913         my @datearr = localtime(time());
914         my $todaysdate = (1900+$datearr[5]).sprintf ("%0.2d", ($datearr[4]+1)).sprintf ("%0.2d", $datearr[3]);
915         my $datedue = $data->{'date_due'};
916         $datedue =~ s/-//g;
917         if ($datedue < $todaysdate) {
918             $data->{'overdue'} = 1;
919         }
920         $currentissues{$counter} = $data;
921         $counter++;
922     }
923     $sth->finish;
924     $dbh->disconnect;
925     return(\%currentissues);
926 }
927
928 sub checkwaiting {
929 #Stolen from Main.pm
930   # check for reserves waiting
931   my ($env,$dbh,$bornum)=@_;
932   my @itemswaiting;
933   my $query = "select * from reserves
934     where (borrowernumber = '$bornum')
935     and (reserves.found='W') and cancellationdate is NULL";
936   my $sth = $dbh->prepare($query);
937   $sth->execute();
938   my $cnt=0;
939   if (my $data=$sth->fetchrow_hashref) {
940     $itemswaiting[$cnt] =$data;
941     $cnt ++
942   }
943   $sth->finish;
944   return ($cnt,\@itemswaiting);
945 }
946
947
948 sub checkaccount  {
949 # Stolen from Accounts.pm
950   #take borrower number
951   #check accounts and list amounts owing
952   my ($env,$bornumber,$dbh,$date)=@_;
953   my $select="Select sum(amountoutstanding) from accountlines where
954   borrowernumber=$bornumber and amountoutstanding<>0";
955   if ($date ne ''){
956     $select.=" and date < '$date'";
957   }
958 #  print $select;
959   my $sth=$dbh->prepare($select);
960   $sth->execute;
961   my $total=0;
962   while (my $data=$sth->fetchrow_hashref){
963     $total=$total+$data->{'sum(amountoutstanding)'};
964   }
965   $sth->finish;
966   # output(1,2,"borrower owes $total");
967   #if ($total > 0){
968   #  # output(1,2,"borrower owes $total");
969   #  if ($total > 5){
970   #    reconcileaccount($env,$dbh,$bornumber,$total);
971   #  }
972   #}
973   #  pause();
974   return($total);
975 }    
976
977 sub renewstatus {
978 # Stolen from Renewals.pm
979   # check renewal status
980   my ($env,$dbh,$bornum,$itemno)=@_;
981   my $renews = 1;
982   my $renewokay = 0;
983   my $q1 = "select * from issues 
984     where (borrowernumber = '$bornum')
985     and (itemnumber = '$itemno') 
986     and returndate is null";
987   my $sth1 = $dbh->prepare($q1);
988   $sth1->execute;
989   if (my $data1 = $sth1->fetchrow_hashref) {
990     my $q2 = "select renewalsallowed from items,biblioitems,itemtypes
991        where (items.itemnumber = '$itemno')
992        and (items.biblioitemnumber = biblioitems.biblioitemnumber) 
993        and (biblioitems.itemtype = itemtypes.itemtype)";
994     my $sth2 = $dbh->prepare($q2);
995     $sth2->execute;     
996     if (my $data2=$sth2->fetchrow_hashref) {
997       $renews = $data2->{'renewalsallowed'};
998     }
999     if ($renews > $data1->{'renewals'}) {
1000       $renewokay = 1;
1001     }
1002     $sth2->finish;
1003   }   
1004   $sth1->finish;
1005   return($renewokay);    
1006 }
1007
1008 sub renewbook {
1009 # Stolen from Renewals.pm
1010   # mark book as renewed
1011   my ($env,$dbh,$bornum,$itemno,$datedue)=@_;
1012   $datedue=$env->{'datedue'};
1013   if ($datedue eq "" ) {    
1014     my $loanlength=21;
1015     my $query= "Select * from biblioitems,items,itemtypes
1016        where (items.itemnumber = '$itemno')
1017        and (biblioitems.biblioitemnumber = items.biblioitemnumber)
1018        and (biblioitems.itemtype = itemtypes.itemtype)";
1019     my $sth=$dbh->prepare($query);
1020     $sth->execute;
1021     if (my $data=$sth->fetchrow_hashref) {
1022       $loanlength = $data->{'loanlength'}
1023     }
1024     $sth->finish;
1025     my $ti = time;
1026     my $datedu = time + ($loanlength * 86400);
1027     my @datearr = localtime($datedu);
1028     $datedue = (1900+$datearr[5])."-".($datearr[4]+1)."-".$datearr[3];
1029   }
1030   my @date = split("-",$datedue);
1031   my $odatedue = ($date[2]+0)."-".($date[1]+0)."-".$date[0];
1032   my $issquery = "select * from issues where borrowernumber='$bornum' and
1033     itemnumber='$itemno' and returndate is null";
1034   my $sth=$dbh->prepare($issquery);
1035   $sth->execute;
1036   my $issuedata=$sth->fetchrow_hashref;
1037   $sth->finish;
1038   my $renews = $issuedata->{'renewals'} +1;
1039   my $updquery = "update issues 
1040     set date_due = '$datedue', renewals = '$renews'
1041     where borrowernumber='$bornum' and
1042     itemnumber='$itemno' and returndate is null";
1043   $sth=$dbh->prepare($updquery);
1044   
1045   $sth->execute;
1046   $sth->finish;
1047   return($odatedue);
1048 }
1049
1050 sub calc_charges {
1051 # Stolen from Issues.pm
1052 # calculate charges due
1053     my ($env, $dbh, $itemno, $bornum)=@_;
1054 #    if (!$dbh){
1055 #      $dbh=C4Connect();
1056 #    }
1057     my $charge=0;
1058 #    open (FILE,">>/tmp/charges");
1059     my $item_type;
1060     my $q1 = "select itemtypes.itemtype,rentalcharge from items,biblioitems,itemtypes 
1061     where (items.itemnumber ='$itemno')
1062     and (biblioitems.biblioitemnumber = items.biblioitemnumber) 
1063     and (biblioitems.itemtype = itemtypes.itemtype)";
1064     my $sth1= $dbh->prepare($q1);
1065 #    print FILE "$q1\n";
1066     $sth1->execute;
1067     if (my $data1=$sth1->fetchrow_hashref) {
1068         $item_type = $data1->{'itemtype'};
1069         $charge = $data1->{'rentalcharge'};
1070 #       print FILE "charge is $charge\n";
1071         my $q2 = "select rentaldiscount from borrowers,categoryitem 
1072         where (borrowers.borrowernumber = '$bornum') 
1073         and (borrowers.categorycode = categoryitem.categorycode)
1074         and (categoryitem.itemtype = '$item_type')";
1075         my $sth2=$dbh->prepare($q2);
1076 #       warn $q2;
1077         $sth2->execute;
1078         if (my $data2=$sth2->fetchrow_hashref) {
1079             my $discount = $data2->{'rentaldiscount'};
1080 #           print FILE "discount is $discount";
1081             if ($discount eq 'NULL') {
1082               $discount=0;
1083             }
1084             $charge = ($charge *(100 - $discount)) / 100;
1085         }
1086         $sth2->finish;
1087     }      
1088     $sth1->finish;
1089 #    close FILE;
1090     return ($charge);
1091 }
1092
1093 sub createcharge {
1094 #Stolen from Issues.pm
1095     my ($env,$dbh,$itemno,$bornum,$charge) = @_;
1096     my $nextaccntno = getnextacctno($env,$bornum,$dbh);
1097     my $query = "insert into accountlines (borrowernumber,itemnumber,accountno,date,amount, description,accounttype,amountoutstanding) values ($bornum,$itemno,$nextaccntno,now(),$charge,'Rental','Rent',$charge)";
1098     my $sth = $dbh->prepare($query);
1099     $sth->execute;
1100     $sth->finish;
1101 }
1102
1103
1104 sub getnextacctno {
1105 # Stolen from Accounts.pm
1106     my ($env,$bornumber,$dbh)=@_;
1107     my $nextaccntno = 1;
1108     my $query = "select * from accountlines where (borrowernumber = '$bornumber') order by accountno desc";
1109     my $sth = $dbh->prepare($query);
1110     $sth->execute;
1111     if (my $accdata=$sth->fetchrow_hashref){
1112         $nextaccntno = $accdata->{'accountno'} + 1;
1113     }
1114     $sth->finish;
1115     return($nextaccntno);
1116 }
1117
1118 sub find_reserves {
1119 # Stolen from Returns.pm
1120     my ($itemno) = @_;
1121     my %env;
1122     my $dbh=&C4Connect;
1123     my ($itemdata) = getiteminformation(\%env, $itemno,0);
1124     my $bibno = $dbh->quote($itemdata->{'biblionumber'});
1125     my $bibitm = $dbh->quote($itemdata->{'biblioitemnumber'});
1126     my $query = "select * from reserves where ((found = 'W') or (found is null)) 
1127                        and biblionumber = $bibno and cancellationdate is NULL
1128                        order by priority, reservedate ";
1129     my $sth = $dbh->prepare($query);
1130     $sth->execute;
1131     my $resfound = 0;
1132     my $resrec;
1133     my $lastrec;
1134 # print $query;
1135     while (($resrec = $sth->fetchrow_hashref) && (not $resfound)) {
1136         $lastrec = $resrec;
1137         my $brn = $dbh->quote($resrec->{'borrowernumber'});
1138         my $rdate = $dbh->quote($resrec->{'reservedate'});
1139         my $bibno = $dbh->quote($resrec->{'biblionumber'});
1140         if ($resrec->{'found'} eq "W") {
1141             if ($resrec->{'itemnumber'} eq $itemno) {
1142                 $resfound = 1;
1143             }
1144         } else {
1145             if ($resrec->{'constrainttype'} eq "a") {
1146                 $resfound = 1;
1147             } else {
1148                 my $conquery = "select * from reserveconstraints where borrowernumber = $brn 
1149                      and reservedate = $rdate and biblionumber = $bibno and biblioitemnumber = $bibitm";
1150                 my $consth = $dbh->prepare($conquery);
1151                 $consth->execute;
1152                 if (my $conrec = $consth->fetchrow_hashref) {
1153                     if ($resrec->{'constrainttype'} eq "o") {
1154                         $resfound = 1;
1155                     }
1156                 }
1157                 $consth->finish;
1158             }
1159         }
1160         if ($resfound) {
1161             my $updquery = "update reserves set found = 'W', itemnumber = '$itemno'
1162                   where borrowernumber = $brn and reservedate = $rdate and biblionumber = $bibno";
1163             my $updsth = $dbh->prepare($updquery);
1164             $updsth->execute;
1165             $updsth->finish;
1166         }
1167     }
1168     $sth->finish;
1169     return ($resfound,$lastrec);
1170 }
1171
1172 END { }       # module clean-up code here (global destructor)