From 600d479f4e47041a6465797150ac6e16f03d0b32 Mon Sep 17 00:00:00 2001 From: Ryan Higgins Date: Mon, 7 Apr 2008 03:28:01 -0500 Subject: [PATCH] Add a working fines script, some changes to CalcFine and Circulation.pm Signed-off-by: Joshua Ferraro --- C4/Circulation.pm | 55 +++++++++++ C4/Overdues.pm | 49 ++++------ misc/cronjobs/fines-ll.pl | 186 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 260 insertions(+), 30 deletions(-) create mode 100755 misc/cronjobs/fines-ll.pl diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 5c14e2be1f..634135c26f 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -69,6 +69,7 @@ BEGIN { &GetItemIssues &GetBorrowerIssues &GetIssuingCharges + &GetIssuingRule &GetBiblioIssues &AnonymiseIssueHistory ); @@ -1094,6 +1095,60 @@ sub GetLoanLength { return 21; } +=head2 GetIssuingRule + +FIXME - This is a copy-paste of GetLoanLength +as a stop-gap. Do not wish to change API for GetLoanLength +this close to release, however, Overdues::GetIssuingRules is broken. + +Get the issuing rule for an itemtype, a borrower type and a branch +Returns a hashref from the issuingrules table. + +my $irule = &GetIssuingRule($borrowertype,$itemtype,branchcode) + +=cut + +sub GetIssuingRule { + my ( $borrowertype, $itemtype, $branchcode ) = @_; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare( "select * from issuingrules where categorycode=? and itemtype=? and branchcode=? and issuelength is not null" ); + + $sth->execute( $borrowertype, $itemtype, $branchcode ); + my $irule = $sth->fetchrow_hashref; + return $irule if defined($irule) ; + + $sth->execute( $borrowertype, $itemtype, "*" ); + my $irule = $sth->fetchrow_hashref; + return $irule if defined($irule) ; + + $sth->execute( $borrowertype, "*", $branchcode ); + my $irule = $sth->fetchrow_hashref; + return $irule if defined($irule) ; + + $sth->execute( "*", $itemtype, $branchcode ); + my $irule = $sth->fetchrow_hashref; + return $irule if defined($irule) ; + + $sth->execute( $borrowertype, "*", "*" ); + my $irule = $sth->fetchrow_hashref; + return $irule if defined($irule) ; + + $sth->execute( "*", "*", $branchcode ); + my $irule = $sth->fetchrow_hashref; + return $irule if defined($irule) ; + + $sth->execute( "*", $itemtype, "*" ); + my $irule = $sth->fetchrow_hashref; + return $irule if defined($irule) ; + + $sth->execute( "*", "*", "*" ); + my $irule = $sth->fetchrow_hashref; + return $irule if defined($irule) ; + + # if no rule matches, + return undef; +} + =head2 AddReturn ($doreturn, $messages, $iteminformation, $borrower) = diff --git a/C4/Overdues.pm b/C4/Overdues.pm index 0457090c1d..1e4fd69868 100644 --- a/C4/Overdues.pm +++ b/C4/Overdues.pm @@ -21,6 +21,7 @@ package C4::Overdues; use strict; use Date::Calc qw/Today/; use Date::Manip qw/UnixDate/; +use C4::Circulation; use C4::Context; use C4::Accounts; use C4::Log; # logaction @@ -66,9 +67,12 @@ BEGIN { # subs to move to Circulation.pm push @EXPORT, qw( - &GetIssuingRules &GetIssuesIteminfo ); + # + # &GetIssuingRules - delete. + # use C4::Circulation::GetIssuingRule instead. + # subs to move to Members.pm push @EXPORT, qw( &CheckBorrowerDebarred @@ -174,8 +178,8 @@ sub checkoverdues { =item CalcFine - ($amount, $chargename, $message) = - &CalcFine($itemnumber, $borrowercode, $days_overdue); + ($amount, $chargename, $message, $daycounttotal, $daycount) = + &CalcFine($itemnumber, $categorycode, $branch, $days_overdue, $description); Calculates the fine for a book. @@ -185,29 +189,18 @@ standard fine for books might be $0.50, but $1.50 for DVDs, or staff members might get a longer grace period between the first and second reminders that a book is overdue). -The fine is calculated as follows: if it is time for the first -reminder, the fine is the value listed for the given (branch, item type, -borrower code) combination. If it is time for the second reminder, the -fine is doubled. Finally, if it is time to send the account to a -collection agency, the fine is set to 5 local monetary units (a really -good deal for the patron if the library is in Italy). Otherwise, the -fine is 0. - -Note that the way this function is currently implemented, it only -returns a nonzero value on the notable days listed above. That is, if -the categoryitems entry says to send a first reminder 7 days after the -book is due, then if you call C<&CalcFine> 7 days after the book is -due, it will give a nonzero fine. If you call C<&CalcFine> the next -day, however, it will say that the fine is 0. C<$itemnumber> is the book's item number. -C<$borrowercode> is the borrower code of the patron who currently has +C<$categorycode> is the category code of the patron who currently has the book. +C<$branchcode> is the library whose issuingrules govern this transaction. + C<$days_overdue> is the number of days elapsed since the book's due date. + C<&CalcFine> returns a list of three values: C<$amount> is the fine owed by the patron (see above). @@ -222,7 +215,7 @@ or "Final Notice". #' sub CalcFine { - my ( $item, $bortype, $difference , $dues ) = @_; + my ( $item, $bortype, $branchcode, $difference , $dues ) = @_; my $dbh = C4::Context->dbh; my $amount = 0; my $printout; @@ -232,7 +225,7 @@ sub CalcFine { my $countalldayclosed = $countspecialday + $countrepeatableday; my $daycount = $difference - $countalldayclosed; # get issuingrules (fines part will be used) - my $data = GetIssuingRules($item->{'itemtype'},$bortype); + my $data = C4::Circulation::GetIssuingRule($item->{'itemtype'},$bortype,$branchcode); my $daycounttotal = $daycount - $data->{'firstremind'}; if ($data->{'chargeperiod'} >0) { # if there is a rule for this bortype if ($data->{'firstremind'} < $daycount) @@ -240,18 +233,11 @@ sub CalcFine { $amount = int($daycounttotal/$data->{'chargeperiod'})*$data->{'fine'}; } } else { - # get fines default rules - my $data = GetIssuingRules($item->{'itemtype'},'*'); - $daycounttotal = $daycount - $data->{'firstremind'}; - if ($data->{'firstremind'} < $daycount) - { - if ($data->{'chargeperiod'} >0) { # if there is a rule for this bortype - $amount = int($daycounttotal/$data->{'chargeperiod'})*$data->{'fine'}; - } - } + # a zero (or null) chargeperiod means no charge. + # } - warn "Calc Fine for $item->{'itemnumber'}, $bortype, $difference , $dues = $amount / $daycount"; +# warn "Calc Fine: " . join(", ", ($item->{'itemnumber'}, $bortype, $difference , $data->{'fine'} . " * " . $daycount . " days = \$ " . $amount , "desc: $dues")) ; return ( $amount, $data->{'chargename'}, $printout ,$daycounttotal ,$daycount ); } @@ -598,6 +584,9 @@ sub GetFine { =item GetIssuingRules +FIXME - This sub should be deprecated and removed. +It ignores branch and defaults. + $data = &GetIssuingRules($itemtype,$categorycode); Looks up for all issuingrules an item info diff --git a/misc/cronjobs/fines-ll.pl b/misc/cronjobs/fines-ll.pl new file mode 100755 index 0000000000..ff964ada21 --- /dev/null +++ b/misc/cronjobs/fines-ll.pl @@ -0,0 +1,186 @@ +#!/usr/bin/perl + +# This script loops through each overdue item, determines the fine, +# and updates the total amount of fines due by each user. It relies on +# the existence of /tmp/fines, which is created by ??? +# Doesnt really rely on it, it relys on being able to write to /tmp/ +# It creates the fines file +# +# This script is meant to be run nightly out of cron. + +# Copyright 2000-2002 Katipo Communications +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place, +# Suite 330, Boston, MA 02111-1307 USA + + +use strict; +BEGIN { + # find Koha's Perl modules + # test carefully before changing this + use FindBin; + eval { require "$FindBin::Bin/kohalib.pl" }; +} +use C4::Context; +use C4::Circulation; +use C4::Overdues; +use Date::Manip qw/Date_DaysSince1BC/; +use C4::Biblio; + +my $fldir = "/tmp"; + +my $libname=C4::Context->preference('LibraryName'); +my $dbname= C4::Context->config('database'); + +my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =localtime(time); +$mon++; +$year=$year+1900; +my $date=Date_DaysSince1BC($mon,$mday,$year); +my $datestr = $year . sprintf("-%02d-%02d",$mon,$mday); +my $filename= $dbname; +$filename =~ s/\W//; +$filename = $fldir . '/'. $filename . $datestr . ".log"; +open (FILE,">$filename") || die "Can't open LOG"; +print FILE "cardnumber\tcategory\tsurname\tfirstname\temail\tphone\taddress\tcitystate\tbarcode\tdate_due\ttype\tdays_overdue\tfine\n"; + +# FIXME +# it looks like $count is just a counter, would it be +# better to rely on the length of the array @$data and turn the +# for loop below into a foreach loop? +# +my $DEBUG =1; +my $data=Getoverdues(); +# warn "Overdues : = ".scalar(@$data)." => ".Data::Dumper::Dumper($data); +my $overdueItemsCounted=0 if $DEBUG; +my $reference = $year."".$mon; +my $borrowernumber; + +for (my $i=0;$i[$i]->{'date_due'}); + my $date2=Date_DaysSince1BC($dates[1],$dates[2],$dates[0]); + my $datedue=$data->[$i]->{'date_due'}; + my $due="$dates[1]/$dates[2]/$dates[0]"; + my $borrower=BorType($data->[$i]->{'borrowernumber'}); + my $branchcode; + if ( C4::Context->preference('CircControl') eq 'ItemHomeLibrary' ) { + $branchcode = $data->[$i]->{'homebranch'}; + } elsif ( C4::Context->preference('CircControl') eq 'PatronLibrary' ) { + $branchcode = $borrower->{'branchcode'}; + } else { + # CircControl must be PickupLibrary. (branchcode comes from issues table here). + $branchcode = $data->[$i]->{'branchcode'}; + } + + my $starter; + + if ($date2 <= $date){ + $overdueItemsCounted++ if $DEBUG; + my $difference=$date-$date2; + my ($amount,$type,$printout,$daycounttotal,$daycount)= + CalcFine($data->[$i], $borrower->{'categorycode'}, $branchcode, + $difference, $datedue); + + + my ($delays1,$delays2,$delays3)=GetOverdueDelays($borrower->{'categorycode'}); + my $issuingrules=GetIssuingRule($data->[$i]->{'itemnumber'},$borrower->{'categorycode'},$branchcode); + + UpdateFine($data->[$i]->{'itemnumber'},$data->[$i]->{'borrowernumber'},$amount,$type,$due); + + if($delays1 and $delays2 and $delays3) { + + my $debarredstatus=CheckBorrowerDebarred($borrower->{'borrowernumber'}); + + #DELAYS 1########################################## + if ($amount > 0 && $daycount >= $delays1 && $daycount < $delays2){ + # FIXME : already in GetIssuingRules ? + my $debarred1=GetOverduerules($borrower->{'categorycode'},1); + (UpdateBorrowerDebarred($borrower->{'borrowernumber'}))if(($debarred1 eq '1' ) and ($debarredstatus eq '0')); + # is there an open "dossier" for this date & borrower + my $getnotifyid=CheckExistantNotifyid($borrower->{'borrowernumber'},$datedue); + my $update=CheckAccountLineLevelInfo($borrower->{'borrowernumber'},$data->[$i]->{'itemnumber'},1,$datedue); + if ($update eq '0'){ + if ($getnotifyid eq '0'){ + $starter=GetNextIdNotify($reference,$borrower->{'borrowernumber'}); + } else { + $starter=$getnotifyid; + } + } + UpdateAccountLines($starter,1,$borrower->{'borrowernumber'},$data->[$i]->{'itemnumber'}); + } + + #DELAYS 2################################# + + if ($daycount >= $delays2 && $daycount < $delays3){ + my $debarred2=GetOverduerules($borrower->{'categorycode'},2); + (UpdateBorrowerDebarred($borrower->{'borrowernumber'}))if(($debarred2 eq '1' ) and ($debarredstatus eq '0')); + my $update=CheckAccountLineLevelInfo($borrower->{'borrowernumber'},$data->[$i]->{'itemnumber'},2,$datedue); + if ($update eq '0'){ + UpdateAccountLines(undef,2,$borrower->{'borrowernumber'},$data->[$i]->{'itemnumber'}); + } + } + ############################################### + + #DELAYS 3################################### + if ($daycount >= $delays3 ){ + my $debarred3=GetOverduerules($borrower->{'categorycode'},3); + (UpdateBorrowerDebarred($borrower->{'borrowernumber'}))if(($debarred3 eq '1' ) and ($debarredstatus eq '0')); + my $update=CheckAccountLineLevelInfo($borrower->{'borrowernumber'},$data->[$i]->{'itemnumber'},3,$datedue); + if ($update eq '0'){ + UpdateAccountLines(undef,3,$borrower->{'borrowernumber'},$data->[$i]->{'itemnumber'}); + } + my $items=GetItems($data->[$i]->{'itemnumber'}); + my $todaydate=$year."-".$mon."-".$mday; + # add item price, the item is considered as lost. + my $description="Item Price"; + my $typeaccount="IP"; + my $level="3"; + my $notifyid=GetNotifyId($borrower->{'borrowernumber'},$data->[$i]->{'itemnumber'}); + my $timestamp=$todaydate." ".$hour."\:".$min."\:".$sec; + my $create=CheckAccountLineItemInfo($borrower->{'borrowernumber'},$data->[$i]->{'itemnumber'},$typeaccount,$notifyid); + if ($create eq '0'){ + CreateItemAccountLine($borrower->{'borrowernumber'},$data->[$i]->{'itemnumber'},$todaydate,$items->{'price'},$description,$typeaccount, + $items->{'price'},$timestamp,$notifyid,$level); + } + } + ############################################### + } + + + if ($borrower->{'category_type'} eq 'C'){ + my $query=qq| SELECT * + FROM borrowers + WHERE borrowernumber=?|; + my $dbh = C4::Context->dbh; + my $sth=$dbh->prepare($query); + $sth->execute($borrower->{'guarantorid'}); + my $tdata=$sth->fetchrow_hashref; + $sth->finish; + $borrower->{'phone'}=$tdata->{'phone'}; + } + print FILE "$printout\t$borrower->{'cardnumber'}\t$borrower->{'categorycode'}\t$borrower->{'surname'}\t$borrower->{'firstname'}\t$borrower->{'email'}\t$borrower->{'phone'}\t$borrower->{'address'}\t$borrower->{'city'}\t$data->[$i]->{'barcode'}\t$data->[$i]->{'date_due'}\t$type\t$difference\t$amount\n"; + } +} + +my $numOverdueItems=scalar(@$data); +if ($DEBUG) { + print <