1 package C4::Circulation::Fines;
5 # Copyright 2000-2002 Katipo Communications
7 # This file is part of Koha.
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
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.
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
26 use vars qw($VERSION @ISA @EXPORT);
28 # set the version for version checking
33 C4::Circulation::Fines - Koha module dealing with fines
37 use C4::Circulation::Fines;
41 This module contains several functions for dealing with fines for
42 overdue items. It is primarily used by the 'misc/fines2.pl' script.
51 @EXPORT = qw(&Getoverdues &CalcFine &BorType &UpdateFine &ReplacementCost
52 GetFine, ReplacementCost2);
56 ($count, $overdues) = &Getoverdues();
58 Returns the list of all overdue books.
60 C<$count> is the number of elements in C<@{$overdues}>.
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
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"
77 # FIXME - Use push @results
80 while ( my $data = $sth->fetchrow_hashref ) {
88 return ( $i, \@results );
93 ($amount, $chargename, $message) =
94 &CalcFine($itemnumber, $borrowercode, $days_overdue);
96 Calculates the fine for a book.
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).
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
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.
119 C<$itemnumber> is the book's item number.
121 C<$borrowercode> is the borrower code of the patron who currently has
124 C<$days_overdue> is the number of days elapsed since the book's due
127 C<&CalcFine> returns a list of three values:
129 C<$amount> is the fine owed by the patron (see above).
131 C<$chargename> is the chargename field from the applicable record in
132 the categoryitem table, whatever that is.
134 C<$message> is a text message, either "First Notice", "Second Notice",
141 my ( $itemnumber, $bortype, $difference ) = @_;
142 my $dbh = C4::Context->dbh;
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
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.
157 my $sth = $dbh->prepare(
158 "Select * from items,biblioitems,itemtypes,issuingrules where items.itemnumber=?
159 and items.biblioitemnumber=biblioitems.biblioitemnumber and
160 biblioitems.itemtype=itemtypes.itemtype and
161 categoryitem.itemtype=itemtypes.itemtype and
162 categoryitem.categorycode=? and (items.itemlost <> 1 or items.itemlost is NULL)"
166 $sth->execute( $itemnumber, $bortype );
167 my $data = $sth->fetchrow_hashref;
169 # FIXME - Error-checking: the item might be lost, or there
170 # might not be an entry in 'categoryitem' for this item type
176 # Is it time to send out the first reminder?
177 # FIXME - I'm not sure the "=="s are correct here. Let's say that
178 # $data->{firstremind} is today, but 'fines2.pl' doesn't run for
179 # some reason (the cron daemon died, the server crashed, the
180 # sysadmin had the machine down for maintenance, or whatever).
182 # Then the next day, the book is $data->{firstremind}+1 days
183 # overdue. But this function returns $amount == 0, $printout ==
184 # undef, on the assumption that 'fines2.pl' ran the previous day. So
185 # the first thing the patron gets is a second notice, but that's a
186 # week after the server crash, so people may not connect the two
188 if ( $difference == $data->{'firstremind'} ) {
190 # Yes. Set the fine as listed.
191 $amount = $data->{'fine'};
192 $printout = "First Notice";
195 # Is it time to send out a second reminder?
196 my $second = $data->{'firstremind'} + $data->{'chargeperiod'};
197 if ( $difference == $second ) {
199 # Yes. The fine is double.
200 $amount = $data->{'fine'} * 2;
201 $printout = "Second Notice";
204 # Is it time to send the account to a collection agency?
205 # FIXME - At least, I *think* that's what this code is doing.
206 if ( $difference == $data->{'accountsent'} && $data->{'fine'} > 0 ) {
208 # Yes. Set the fine at 5 local monetary units.
209 # FIXME - This '5' shouldn't be hard-wired.
211 $printout = "Final Notice";
213 return ( $amount, $data->{'chargename'}, $printout );
218 &UpdateFine($itemnumber, $borrowernumber, $amount, $type, $description);
220 (Note: the following is mostly conjecture and guesswork.)
222 Updates the fine owed on an overdue book.
224 C<$itemnumber> is the book's item number.
226 C<$borrowernumber> is the borrower number of the patron who currently
227 has the book on loan.
229 C<$amount> is the current amount owed by the patron.
231 C<$type> will be used in the description of the fine.
233 C<$description> is a string that must be present in the description of
234 the fine. I think this is expected to be a date in DD/MM/YYYY format.
236 C<&UpdateFine> looks up the amount currently owed on the given item
237 and sets it to C<$amount>, creating, if necessary, a new entry in the
238 accountlines table of the Koha database.
243 # FIXME - This API doesn't look right: why should the caller have to
244 # specify both the item number and the borrower number? A book can't
245 # be on loan to two different people, so the item number should be
248 my ( $itemnum, $bornum, $amount, $type, $due ) = @_;
249 my $dbh = C4::Context->dbh;
251 # FIXME - What exactly is this query supposed to do? It looks up an
252 # entry in accountlines that matches the given item and borrower
253 # numbers, where the description contains $due, and where the
254 # account type has one of several values, but what does this _mean_?
255 # Does it look up existing fines for this item?
256 # FIXME - What are these various account types? ("FU", "O", "F", "M")
257 my $sth = $dbh->prepare(
258 "Select * from accountlines where itemnumber=? and
259 borrowernumber=? and (accounttype='FU' or accounttype='O' or
260 accounttype='F' or accounttype='M') and description like ?"
262 $sth->execute( $itemnum, $bornum, "%$due%" );
264 if ( my $data = $sth->fetchrow_hashref ) {
266 # I think this if-clause deals with the case where we're updating
268 # print "in accounts ...";
269 if ( $data->{'amount'} != $amount ) {
272 my $diff = $amount - $data->{'amount'};
273 my $out = $data->{'amountoutstanding'} + $diff;
274 my $sth2 = $dbh->prepare(
275 "update accountlines set date=now(), amount=?,
276 amountoutstanding=?,accounttype='FU' where
277 borrowernumber=? and itemnumber=?
278 and (accounttype='FU' or accounttype='O') and description like ?"
280 $sth2->execute( $amount, $out, $data->{'borrowernumber'},
281 $data->{'itemnumber'}, "%$due%" );
286 # print "no update needed $data->{'amount'}"
291 # I think this else-clause deals with the case where we're adding
293 my $sth4 = $dbh->prepare(
294 "select title from biblio,items where items.itemnumber=?
295 and biblio.biblionumber=items.biblionumber"
297 $sth4->execute($itemnum);
298 my $title = $sth4->fetchrow_hashref;
301 # print "not in account";
302 my $sth3 = $dbh->prepare("Select max(accountno) from accountlines");
305 # FIXME - Make $accountno a scalar.
306 my @accountno = $sth3->fetchrow_array;
309 my $sth2 = $dbh->prepare(
310 "Insert into accountlines
311 (borrowernumber,itemnumber,date,amount,
312 description,accounttype,amountoutstanding,accountno) values
313 (?,?,now(),?,?,'FU',?,?)"
315 $sth2->execute( $bornum, $itemnum, $amount,
316 "$type $title->{'title'} $due",
317 $amount, $accountno[0] );
325 $borrower = &BorType($borrowernumber);
327 Looks up a patron by borrower number.
329 C<$borrower> is a reference-to-hash whose keys are all of the fields
330 from the borrowers and categories tables of the Koha database. Thus,
331 C<$borrower> contains all information about both the borrower and
332 category he or she belongs to.
338 my ($borrowernumber) = @_;
339 my $dbh = C4::Context->dbh;
340 my $sth = $dbh->prepare(
341 "Select * from borrowers,categories where
343 borrowers.categorycode=categories.categorycode"
345 $sth->execute($borrowernumber);
346 my $data = $sth->fetchrow_hashref;
351 =item ReplacementCost
353 $cost = &ReplacementCost($itemnumber);
355 Returns the replacement cost of the item with the given item number.
360 sub ReplacementCost {
362 my $dbh = C4::Context->dbh;
364 $dbh->prepare("Select replacementprice from items where itemnumber=?");
365 $sth->execute($itemnum);
367 # FIXME - Use fetchrow_array or something.
368 my $data = $sth->fetchrow_hashref;
370 return ( $data->{'replacementprice'} );
374 my ( $itemnum, $bornum ) = @_;
375 my $dbh = C4::Context->dbh();
376 my $query = "SELECT sum(amountoutstanding) FROM accountlines
377 where accounttype like 'F%'
378 AND amountoutstanding > 0 AND itemnumber = ? AND borrowernumber=?";
379 my $sth = $dbh->prepare($query);
380 $sth->execute( $itemnum, $bornum );
381 my $data = $sth->fetchrow_hashref();
384 return ( $data->{'sum(amountoutstanding)'} );
387 sub ReplacementCost2 {
388 my ( $itemnum, $bornum ) = @_;
389 my $dbh = C4::Context->dbh();
390 my $query = "SELECT amountoutstanding FROM accountlines
391 where accounttype like 'L' AND amountoutstanding > 0 AND
392 itemnumber = ? AND borrowernumber= ?";
393 my $sth = $dbh->prepare($query);
394 $sth->execute( $itemnum, $bornum );
395 my $data = $sth->fetchrow_hashref();
398 return ( $data->{'amountoutstanding'} );
407 Koha Developement team <info@koha.org>