New set of routines for HEAD.
[koha.git] / C4 / Circulation / Fines.pm
1 package C4::Circulation::Fines;
2
3 # $Id$
4
5 # Copyright 2000-2002 Katipo Communications
6 #
7 # This file is part of Koha.
8 #
9 # Koha is free software; you can redistribute it and/or modify it under the
10 # terms of the GNU General Public License as published by the Free Software
11 # Foundation; either version 2 of the License, or (at your option) any later
12 # version.
13 #
14 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
15 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License along with
19 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
20 # Suite 330, Boston, MA  02111-1307 USA
21
22 use strict;
23 require Exporter;
24
25 use C4::Context;
26 use C4::Biblio;
27 use vars qw($VERSION @ISA @EXPORT);
28
29 # set the version for version checking
30 $VERSION = 0.01;
31
32 =head1 NAME
33
34 C4::Circulation::Fines - Koha module dealing with fines
35
36 =head1 SYNOPSIS
37
38   use C4::Circulation::Fines;
39
40 =head1 DESCRIPTION
41
42 This module contains several functions for dealing with fines for
43 overdue items. It is primarily used by the 'misc/fines2.pl' script.
44
45 =head1 FUNCTIONS
46
47 =over 2
48
49 =cut
50
51 @ISA = qw(Exporter);
52 @EXPORT = qw(&Getoverdues &CalcFine &BorType &UpdateFine &ReplacementCost &GetFine &ReplacementCost2);
53
54 =item Getoverdues
55
56   ($count, $overdues) = &Getoverdues();
57
58 Returns the list of all overdue books.
59
60 C<$count> is the number of elements in C<@{$overdues}>.
61
62 C<$overdues> is a reference-to-array. Each element is a
63 reference-to-hash whose keys are the fields of the issues table in the
64 Koha database.
65
66 =cut
67 #'
68 sub Getoverdues{
69   my $dbh = C4::Context->dbh;
70   my $sth=$dbh->prepare("Select * from issues where date_due < now() and returndate is  NULL order by borrowernumber");
71   $sth->execute;
72   # FIXME - Use push @results
73   my $i=0;
74   my @results;
75   while (my $data=$sth->fetchrow_hashref){
76   push  @results,$data;
77     $i++;
78   }
79   $sth->finish;
80   return($i,\@results);
81 }
82
83 =item CalcFine
84
85   ($amount, $chargename, $message) =
86         &CalcFine($itemnumber, $borrowercode, $days_overdue);
87
88 Calculates the fine for a book.
89
90 The issuingrules table in the Koha database is a fine matrix, listing
91 the penalties for each type of patron for each type of item and each branch (e.g., the
92 standard fine for books might be $0.50, but $1.50 for DVDs, or staff
93 members might get a longer grace period between the first and second
94 reminders that a book is overdue).
95
96 The fine is calculated as follows: if it is time for the first
97 reminder, the fine is the value listed for the given (branch, item type,
98 borrower code) combination. If it is time for the second reminder, the
99 fine is doubled. Finally, if it is time to send the account to a
100 collection agency, the fine is set to 5 local monetary units (a really
101 good deal for the patron if the library is in Italy). Otherwise, the
102 fine is 0.
103
104 Note that the way this function is currently implemented, it only
105 returns a nonzero value on the notable days listed above. That is, if
106 the issuingruless entry says to send a first reminder 7 days after the
107 book is due, then if you call C<&CalcFine> 7 days after the book is
108 due, it will give a nonzero fine. If you call C<&CalcFine> the next
109 day, however, it will say that the fine is 0.
110
111 C<$itemnumber> is the book's item number.
112
113 C<$borrowercode> is the borrower code of the patron who currently has
114 the book.
115
116 C<$days_overdue> is the number of days elapsed since the book's due
117 date.
118
119 C<&CalcFine> returns a list of three values:
120
121 C<$amount> is the fine owed by the patron (see above).
122
123 C<$chargename> is the chargename field from the applicable record in
124 the issuingrules table, whatever that is.
125
126 C<$message> is a text message, either "First Notice", "Second Notice",
127 or "Final Notice".
128
129 =cut
130 #'
131 sub CalcFine {
132   my ($itemnumber,$bortype,$difference)=@_;
133   my $dbh = C4::Context->dbh;
134   # Look up the issuingrules record for this book's item type and the
135   # given borrwer type.
136   # The reason this query is so messy is that it's a messy question:
137   # given the barcode, we can find the book's items record. This gives
138   # us the biblio record, which gives us a set of issuingrules
139   # records. Then we select the one that corresponds to the desired
140   # borrower type.
141
142   # FIXME - Is it really necessary to get absolutely everything from
143   # all four tables? It looks as if this code only wants
144   # firstremind, chargeperiod, accountsent, and chargename from the
145   # issuingrules table.
146
147   my $sth=$dbh->prepare("Select * from items,biblio,itemtypes,issuingrules where items.itemnumber=?
148   and items.biblionumber=biblio.biblionumber and
149   biblio.itemtype=itemtypes.itemtype and
150   issuingrules.itemtype=itemtypes.itemtype and
151   issuingrules.categorycode=? ");
152 #  print $query;
153   $sth->execute($itemnumber,$bortype);
154   my $data=$sth->fetchrow_hashref;
155         # FIXME - Error-checking: the item might be lost, or there
156         # might not be an entry in 'issuingrules' for this item type
157         # or borrower type.
158   $sth->finish;
159   my $amount=0;
160   my $printout;
161
162   # Is it time to send out the first reminder?
163   # FIXME - I'm not sure the "=="s are correct here. Let's say that
164   # $data->{firstremind} is today, but 'fines2.pl' doesn't run for
165   # some reason (the cron daemon died, the server crashed, the
166   # sysadmin had the machine down for maintenance, or whatever).
167   #
168   # Then the next day, the book is $data->{firstremind}+1 days
169   # overdue. But this function returns $amount == 0, $printout ==
170   # undef, on the assumption that 'fines2.pl' ran the previous day. So
171   # the first thing the patron gets is a second notice, but that's a
172   # week after the server crash, so people may not connect the two
173   # events.
174   if ($difference >= $data->{'firstremind'}){
175     # Yes. Set the fine as listed.
176     $amount=$data->{'fine'}* $difference;
177     $printout="First Notice";
178   }
179
180   # Is it time to send out a second reminder?
181 #  my $second=$data->{'firstremind'}+$data->{'chargeperiod'};
182 #  if ($difference == $second){
183 #    # Yes. The fine is double.
184 #    $amount=$data->{'fine'}*2;
185 #    $printout="Second Notice";
186 #  }
187
188   # Is it time to send the account to a collection agency?
189   # FIXME - At least, I *think* that's what this code is doing.
190   if ($difference == $data->{'accountsent'} && $data->{'fine'} > 0){
191     # Yes. Set the fine at 5 local monetary units.
192     # FIXME - This '5' shouldn't be hard-wired.
193     $amount=$data->{'fine'}* $difference;
194     $printout="Final Notice";
195   }
196   return($amount,$data->{'chargename'},$printout);
197 }
198
199 =item UpdateFine
200
201   &UpdateFine($itemnumber, $borrowernumber, $amount, $type, $description);
202
203 (Note: the following is mostly conjecture and guesswork.)
204
205 Updates the fine owed on an overdue book.
206
207 C<$itemnumber> is the book's item number.
208
209 C<$borrowernumber> is the borrower number of the patron who currently
210 has the book on loan.
211
212 C<$amount> is the current amount owed by the patron.
213
214 C<$type> will be used in the description of the fine.
215
216 C<$description> is a string that must be present in the description of
217 the fine. I think this is expected to be a date in DD/MM/YYYY format.
218
219 C<&UpdateFine> looks up the amount currently owed on the given item
220 and sets it to C<$amount>, creating, if necessary, a new entry in the
221 accountlines table of the Koha database.
222
223 =cut
224 #'
225 # FIXME - This API doesn't look right: why should the caller have to
226 # specify both the item number and the borrower number? A book can't
227 # be on loan to two different people, so the item number should be
228 # sufficient.
229 sub UpdateFine {
230   my ($itemnum,$bornum,$amount,$type,$due)=@_;
231   my $dbh = C4::Context->dbh;
232   # FIXME - What exactly is this query supposed to do? It looks up an
233   # entry in accountlines that matches the given item and borrower
234   # numbers, where the description contains $due, and where the
235   # account type has one of several values, but what does this _mean_?
236   # Does it look up existing fines for this item?
237   # FIXME - What are these various account types? ("FU", "O", "F", "M")
238
239   my $sth=$dbh->prepare("Select * from accountlines where itemnumber=? and
240   borrowernumber=? and (accounttype='FU' or accounttype='O' or
241   accounttype='F' or accounttype='M') ");
242   $sth->execute($itemnum,$bornum);
243
244   if (my $data=$sth->fetchrow_hashref){
245     # I think this if-clause deals with the case where we're updating
246     # an existing fine.
247 #    print "in accounts ...";
248     if ($data->{'amount'} != $amount){
249
250 #     print "updating";
251       my $diff=$amount - $data->{'amount'};
252       my $out=$data->{'amountoutstanding'}+$diff;
253       my $sth2=$dbh->prepare("update accountlines set date=now(), amount=?,
254       amountoutstanding=?,accounttype='FU' where
255       accountno=?");
256       $sth2->execute($amount,$out,$data->{'accountno'});
257       $sth2->finish;
258    } else {
259       print "no update needed $data->{'amount'} \n";
260     }
261   } else {
262     # I think this else-clause deals with the case where we're adding
263     # a new fine.
264     my $sth4=$dbh->prepare("select biblio.marc from biblio ,items where items.itemnumber=?
265     and biblio.biblionumber=items.biblionumber");
266     $sth4->execute($itemnum);
267     my $marc=$sth4->fetchrow;
268     $sth4->finish;
269 my $record=MARC::File::USMARC::decode($marc,\&func_title);
270 my $title=$record->title();
271  #   print "not in account";
272     my $sth3=$dbh->prepare("Select max(accountno) from accountlines");
273     $sth3->execute;
274     # FIXME - Make $accountno a scalar.
275     my $accountno=$sth3->fetchrow;
276     $sth3->finish;
277     $accountno++;
278     my $sth2=$dbh->prepare("Insert into accountlines
279     (borrowernumber,itemnumber,date,amount,
280     description,accounttype,amountoutstanding,accountno) values
281     (?,?,now(),?,?,'FU',?,?)");
282     $sth2->execute($bornum,$itemnum,$amount,"$type $title $due",$amount,$accountno);
283     $sth2->finish;
284   }
285   $sth->finish;
286 }
287
288   sub func_title {
289         my ($tagno,$tagdata) = @_;
290   my ($titlef,$subf)=&MARCfind_marc_from_kohafield("title","biblios");
291         return ($tagno == $titlef );
292     }
293
294 =item BorType
295
296   $borrower = &BorType($borrowernumber);
297
298 Looks up a patron by borrower number.
299
300 C<$borrower> is a reference-to-hash whose keys are all of the fields
301 from the borrowers and categories tables of the Koha database. Thus,
302 C<$borrower> contains all information about both the borrower and
303 category he or she belongs to.
304
305 =cut
306 #'
307 sub BorType {
308   my ($borrowernumber)=@_;
309   my $dbh = C4::Context->dbh;
310   my $sth=$dbh->prepare("Select * from borrowers,categories where
311   borrowernumber=? and
312 borrowers.categorycode=categories.categorycode");
313   $sth->execute($borrowernumber);
314   my $data=$sth->fetchrow_hashref;
315   $sth->finish;
316   return($data);
317 }
318
319 =item ReplacementCost
320
321   $cost = &ReplacementCost($itemnumber);
322
323 Returns the replacement cost of the item with the given item number.
324
325 =cut
326 #'
327 sub ReplacementCost{
328   my ($itemnumber)=@_;
329   my $dbh = C4::Context->dbh;
330   my ($itemrecord)=MARCgetitem($dbh,$itemnumber);
331  my $data=MARCmarc2koha($dbh,$itemrecord,"holdings"); 
332   return($data->{'replacementprice'});
333 }
334 sub GetFine {
335     my ( $itemnum, $bornum ) = @_;
336     my $dbh   = C4::Context->dbh();
337     my $query = "SELECT sum(amountoutstanding) FROM accountlines 
338     where accounttype like 'F%'  
339   AND amountoutstanding > 0 AND itemnumber = ? AND borrowernumber=?";
340     my $sth = $dbh->prepare($query);
341     $sth->execute( $itemnum, $bornum );
342     my $data = $sth->fetchrow_hashref();
343     $sth->finish();
344     $dbh->disconnect();
345     return ( $data->{'sum(amountoutstanding)'} );
346 }
347
348 sub ReplacementCost2 {
349     my ( $itemnum, $bornum ) = @_;
350     my $dbh   = C4::Context->dbh();
351     my $query = "SELECT amountoutstanding FROM accountlines
352     where accounttype like 'L'  AND amountoutstanding > 0 AND
353   itemnumber = ? AND borrowernumber= ?";
354     my $sth = $dbh->prepare($query);
355     $sth->execute( $itemnum, $bornum );
356     my $data = $sth->fetchrow_hashref();
357     $sth->finish();
358     $dbh->disconnect();
359     return ( $data->{'amountoutstanding'} );
360 1;
361 __END__
362
363 =back
364
365 =head1 AUTHOR
366
367 Koha Developement team <info@koha.org>
368
369 =cut