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