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