[Koha-patches] [PATCH] Fines On Return
Kyle Hall
kyle.m.hall at gmail.com
Wed Jan 21 15:54:03 CET 2009
---
C4/Circulation.pm | 10 +
C4/FinesOnReturn.pm | 284 ++++++++++++++++++++
installer/data/mysql/en/mandatory/sysprefs.sql | 1 +
.../1-Obligatoire/unimarc_standard_systemprefs.sql | 2 +-
installer/data/mysql/updatedatabase.pl | 7 +
5 files changed, 303 insertions(+), 1 deletions(-)
create mode 100644 C4/FinesOnReturn.pm
diff --git a/C4/Circulation.pm b/C4/Circulation.pm
index 372b5e8..dfc57ae 100644
--- a/C4/Circulation.pm
+++ b/C4/Circulation.pm
@@ -1364,6 +1364,10 @@ sub AddReturn {
my $validTransfert = 0;
my $reserveDone = 0;
+ if ( C4::Context->preference("calcFineOnReturn") ) {
+ CreateFineOnReturn( $barcode );
+ }
+
# get information on item
my $iteminformation = GetItemIssue( GetItemnumberFromBarcode($barcode));
my $biblio = GetBiblioItemData($iteminformation->{'biblioitemnumber'});
@@ -2073,6 +2077,12 @@ sub AddRenewal {
$sth->execute( $borrowernumber, $itemnumber );
my $issuedata = $sth->fetchrow_hashref;
$sth->finish;
+
+ # If using FinesOnReturn, we need to create the fine before
+ # updating the issues row
+ if ( C4::Context->preference("calcFineOnReturn") ) {
+ CreateFineOnReturn( my $barcode, $itemnumber );
+ }
# Update the issues record to have the new due date, and a new count
# of how many times it has been renewed.
diff --git a/C4/FinesOnReturn.pm b/C4/FinesOnReturn.pm
new file mode 100644
index 0000000..c5bd7f9
--- /dev/null
+++ b/C4/FinesOnReturn.pm
@@ -0,0 +1,284 @@
+package C4::FinesOnReturn;
+
+# Copyright 2008 Kyle Hall <kyle.m.hall at gmail.com> kylehall.info
+#
+# 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;
+
+require Exporter;
+
+use vars qw($VERSION @ISA @EXPORT);
+
+use C4::Context;
+
+use DBI;
+use POSIX;
+use Date::Calc qw(Add_Delta_Days);
+use List::Util qw(min);
+use Data::Dumper;
+
+
+# Set the version for version checking
+$VERSION = 0.01;
+
+=head1 NAME
+
+C4::FinesOnReturn - Koha module for calculating fines on return/renewal.
+
+=head1 SYNOPSIS
+
+ use C4::Accounts::FinesOnReturn;
+
+=head1 DESCRIPTION
+
+This module is an alternative system to the current nightly fines generator.
+With this system, a fine for an overdue item is calculated at the time of
+return/renewal. The fine for the entire overdue period is treated as a single
+fine.
+
+=head1 FUNCTIONS
+
+=over 2
+
+=cut
+
+ at ISA = qw(Exporter);
+ at EXPORT = qw(&CreateFineOnReturn &CalculateFine);
+
+## Function Calculate Fine
+## Calculates the fine for the given barcode or itemnumber.
+## $future_date is an optional date ( YYYY-MM-DD ) for estimating a
fine in the future.
+## Returns amount of fine.
+sub CalculateFine {
+ my $maxFine = C4::Context->preference('MaxFine');
+ my ( $itembarcode, $itemnumber, $future_date ) = @_;
+
+ my $return_date = sprintf("%04d-%02d-%02d", (localtime(time))[5] +
1900, (localtime(time))[4] + 1, (localtime(time))[3]);
+ if ( $future_date ) {
+ $return_date = $future_date;
+ }
+
+ my $dbh = C4::Context->dbh;
+
+ my $fineData = _GetFineData( $itembarcode, $itemnumber, $future_date );
+
+
+ if ( ( $fineData->{'replacementprice'} > 0 ) && (
$fineData->{'replacementprice'} < $maxFine ) ) {
+ $maxFine = $fineData->{'replacementprice'};
+ }
+
+ if ( $fineData->{'days_overdue'} < 1 ) { return 0; } ## Short
circuit for speed
+
+ my $issuing_rule = _GetIssuingRule( $fineData->{'categorycode'},
$fineData->{'itemtype'}, $fineData->{'holdingbranch'} );
+ my $days_to_charge = $fineData->{'days_overdue'} -
$issuing_rule->{'firstremind'} - _GetHolidaysBetween(
$fineData->{'date_due'}, $return_date, $fineData->{'holdingbranch'} );
+ if ( $days_to_charge < 1 ) { return 0; } ## Short circuit for speed
+
+ if ( ! $issuing_rule->{'chargeperiod'} ) {
$issuing_rule->{'chargeperiod'} = 1; }
+
+ my $fine = $days_to_charge * $issuing_rule->{'fine'} /
$issuing_rule->{'chargeperiod'};
+ if ( $fine > $maxFine ) { $fine = $maxFine; }
+
+ return $fine;
+}
+
+## Function CreateFineOnReturn
+## This function accepts to arguments, $itembarcode and $itemnumber
+## Only one is required, if both are passed $itemnumber will be used.
+## NOTE: This function should be run right before an item is returned
or renewed.
+sub CreateFineOnReturn {
+ my ( $itembarcode, $itemnumber ) = @_;
+
+ my $dbh = C4::Context->dbh;
+
+ my $amount = CalculateFine( $itembarcode, $itemnumber );
+
+ if ( $amount > 0 ) {
+ my $fineData = _GetFineData( $itembarcode, $itemnumber );
+ my $description = " $fineData->{'itemcallnumber'} : (
$fineData->{'barcode'} ) Issued: $fineData->{'issuedate'}, Due:
$fineData->{'date_due'}, Returned: $fineData->{'date_returned'}";
+ _CreateFine( $fineData->{'itemnumber'},
$fineData->{'borrowernumber'}, $amount, my $type = 'F', $description
);
+ }
+}
+
+## Function _CreateFine
+## Creates the accountline in the db
+sub _CreateFine {
+ my ( $itemnum, $bornum, $amount, $type, $description ) = @_;
+ my $dbh = C4::Context->dbh;
+
+ my $itemData = _GetFineData( '', $itemnum );
+ my $title = $itemData->{'title'};
+ my $holdingBranch = $itemData->{'holdingbranch'};
+
+ ## Get the next accountno from accountlines
+ ## FIXME: Should accountlines.accountno just be set to
autoincrement in MySQL?
+ my $sth2 = $dbh->prepare("SELECT MAX( accountno ) FROM accountlines");
+ $sth2->execute;
+ my $accountno = $sth2->fetchrow_array + 1;
+ $sth2->finish;
+
+ if ( $amount > 0 ) {
+ ## Insert the fine into the database
+ my $sth3 = $dbh->prepare("INSERT INTO accountlines (
+ borrowernumber, itemnumber, date, amount, description,
accounttype, amountoutstanding, accountno )
+ VALUES ( ?, ?, NOW(), ?, ?, 'F', ?, ? )");
+ warn "_CreateFine:: Insert Data sth3->execute( $bornum, $itemnum,
$amount, '$type: $title {$holdingBranch} $description', $amount,
$accountno )";
+ $sth3->execute( $bornum, $itemnum, $amount, "$type: $title
{$holdingBranch} $description", $amount, $accountno );
+ $sth3->finish;
+ }
+
+}
+
+
+## Function getFineData
+## This function returns an array associated array of data about the
borrower and the item
+## $future_date is an optional date ( YYYY-MM-DD ) for estimating a
fine in the future.
+sub _GetFineData {
+ my ( $itembarcode, $itemnumber, $future_date ) = @_;
+
+ my $diff_date = "NOW()";
+ if ( $future_date ) {
+ $diff_date = "DATE( $future_date )";
+ }
+
+ my $dbh = C4::Context->dbh;
+
+ my $sql = "SELECT items.itemnumber,
+ items.itemcallnumber,
+ items.replacementprice,
+ issues.date_due,
+ issues.issuedate,
+ DATEDIFF( $diff_date, issues.date_due ) as days_overdue,
+ borrowers.borrowernumber,
+ borrowers.categorycode,
+ biblioitems.itemtype,
+ items.price,
+ items.barcode,
+ items.holdingbranch,
+ biblio.title,
+ CURDATE() AS date_returned
+ FROM items, issues, borrowers, biblio, biblioitems,
branches, itemtypes
+ WHERE
+ issues.itemnumber = items.itemnumber
+ AND borrowers.borrowernumber = issues.borrowernumber
+ AND items.biblionumber = biblioitems.biblionumber
+ AND items.biblionumber = biblio.biblionumber
+ AND branches.branchcode = items.holdingbranch
+ AND biblioitems.itemtype = itemtypes.itemtype
+ AND issues.returndate IS NULL
+ ";
+
+ if ( $itembarcode ) {
+ $sql .= "AND items.barcode = ?";
+ } else {
+ $sql .= "AND items.itemnumber = ?";
+ }
+
+ my $sth = $dbh->prepare( $sql );
+
+ if ( $itembarcode ) {
+ $sth->execute( $itembarcode );
+ } else {
+ $sth->execute( $itemnumber );
+ }
+
+ my $fineData = $sth->fetchrow_hashref();
+
+ return $fineData;
+}
+
+## _GetIssuingRule takes the borrower categorycode, itemtype, and the
item's holdingbranch
+## And returns the proper issuing rule, if there is no exact issuing
rule for the combination,
+## it tries to match just the category code, if that fails it tries
just the itemtype
+## if that fails, it returns the default rule for the branch
+sub _GetIssuingRule {
+ my ( $categorycode, $itemtype, $holdingbranch ) = @_;
+
+ my $issuingrule = _CheckForIssuingRule( $categorycode, $itemtype,
$holdingbranch );
+ if ( $issuingrule ) { return $issuingrule; }
+
+ my $issuingrule = _CheckForIssuingRule( $categorycode, '', $holdingbranch );
+ if ( $issuingrule ) { return $issuingrule; }
+
+ my $issuingrule = _CheckForIssuingRule( '', $itemtype, $holdingbranch );
+ if ( $issuingrule ) { return $issuingrule; }
+
+ my $issuingrule = _CheckForIssuingRule( '', '', $holdingbranch );
+ if ( $issuingrule ) { return $issuingrule; }
+
+ my $issuingrule = _CheckForIssuingRule( '', '', '' );
+ return $issuingrule;
+
+}
+
+## Checks to see if there is an issuing rule for the given criteria
+## $categorycode and/or $itemtype can be empty to indicate wildcard
+## $holdingbranch is the current holdingbranch for the item
+## If the issuingrule exists, it returns a hashref for it, if not, it returns 0
+sub _CheckForIssuingRule {
+ my ( $categorycode, $itemtype, $branchcode ) = @_;
+
+ if ( ! $categorycode ) { $categorycode = "*"; }
+ if ( ! $itemtype ) { $itemtype = "*"; }
+ if ( ! $branchcode ) { $branchcode = "*"; }
+
+ my $dbh = C4::Context->dbh;
+
+ my $sql = "SELECT * FROM issuingrules WHERE categorycode LIKE ? AND
itemtype LIKE ? AND branchcode LIKE ?";
+
+ my $sth = $dbh->prepare( $sql );
+
+ $sth->execute( $categorycode, $itemtype, $branchcode );
+
+ my $issuingrule = $sth->fetchrow_hashref();
+
+ return $issuingrule;
+}
+
+## Takes a starting and ending date
+## in the format YYYY-MM-DD
+## and returns the number of
+## holidays between the two dates
+sub _GetHolidaysBetween {
+ my ( $startDate, $endDate, $branchcode ) = @_;
+
+ my ( $sYear, $sMonth, $sDay ) = split( /-/, $startDate );
+ my ( $eYear, $eMonth, $eDay ) = split( /-/, $endDate );
+
+ my $holidaysCount = 0;
+
+ my $calendar = C4::Calendar->new( branchcode => $branchcode );
+
+ $holidaysCount += $calendar->isHoliday( $sDay, $sMonth, $sYear );
+
+ while ( mktime( 0, 0, 0, $sDay, $sMonth - 1, $sYear - 1900, 0, 0 )
< mktime( 0, 0, 0, $eDay, $eMonth - 1, $eYear - 1900, 0, 0 ) ) { ##
Changed <= to < to fix fine issue
+ ( $sYear, $sMonth, $sDay ) = Add_Delta_Days( $sYear, $sMonth, $sDay, 1 );
+ $holidaysCount += $calendar->isHoliday( $sDay, $sMonth, $sYear );
+ }
+
+ return $holidaysCount;
+}
+
+1;
+__END__
+
+=back
+
+=head1 AUTHOR
+
+Kyle Hall
+
+=cut
diff --git a/installer/data/mysql/en/mandatory/sysprefs.sql
b/installer/data/mysql/en/mandatory/sysprefs.sql
index 47a0c4b..2bde4f1 100644
--- a/installer/data/mysql/en/mandatory/sysprefs.sql
+++ b/installer/data/mysql/en/mandatory/sysprefs.sql
@@ -214,3 +214,4 @@ INSERT INTO systempreferences
(variable,value,explanation,options,type) VALUES('
INSERT INTO `systempreferences`
(variable,value,options,explanation,type) VALUES
('OPACDisplayRequestPriority','0','','Show patrons the priority level
on holds in the OPAC','YesNo');
INSERT INTO `systempreferences` ( `variable` , `value` , `options` ,
`explanation` , `type` ) VALUES ( 'UseBranchTransferLimits', '0', '',
'If ON, Koha will will use the rules defined in branch_transfer_limits
to decide if an item transfer should be allowed.', 'YesNo');
INSERT INTO systempreferences
(variable,value,explanation,options,type)
VALUES('AllowHoldPolicyOverride', '0', 'Allow staff to override hold
policies when placing holds',NULL,'YesNo');
+INSERT INTO `systempreferences` ( `variable` , `value` , `options` ,
`explanation` , `type` ) VALUES ( 'calcFineOnReturn', '0', '', 'Turns
on the feature to calculate fines at the time of return and/or renewal
as an alternative to the nightly cronjob fines system.', 'YesNo' );
diff --git a/installer/data/mysql/fr-FR/1-Obligatoire/unimarc_standard_systemprefs.sql
b/installer/data/mysql/fr-FR/1-Obligatoire/unimarc_standard_systemprefs.sql
index 6047741..545b68c 100644
--- a/installer/data/mysql/fr-FR/1-Obligatoire/unimarc_standard_systemprefs.sql
+++ b/installer/data/mysql/fr-FR/1-Obligatoire/unimarc_standard_systemprefs.sql
@@ -216,4 +216,4 @@ INSERT INTO systempreferences
(variable,value,explanation,options,type) VALUES('
INSERT INTO `systempreferences`
(variable,value,options,explanation,type) VALUES
('OPACDisplayRequestPriority','0','','Afficher l\'ordre des
rÃ(c)servation pour les adhÃ(c)rents á l\'opac','YesNo');
INSERT INTO `systempreferences` ( `variable` , `value` , `options` ,
`explanation` , `type` ) VALUES ( 'UseBranchTransferLimits', '0', '',
'If ON, Koha will will use the rules defined in branch_transfer_limits
to decide if an item transfer should be allowed.', 'YesNo');
INSERT INTO systempreferences
(variable,value,explanation,options,type)
VALUES('AllowHoldPolicyOverride', '0', "Autorise le personnel á
outrepasser la politique de rÃ(c)servation au moment d'une
rÃ(c)servation",NULL,'YesNo');
-
+INSERT INTO `systempreferences` ( `variable` , `value` , `options` ,
`explanation` , `type` ) VALUES ( 'calcFineOnReturn', '0', '', 'Turns
on the feature to calculate fines at the time of return and/or renewal
as an alternative to the nightly cronjob fines system.', 'YesNo' );
diff --git a/installer/data/mysql/updatedatabase.pl
b/installer/data/mysql/updatedatabase.pl
index a819f8f..9306563 100755
--- a/installer/data/mysql/updatedatabase.pl
+++ b/installer/data/mysql/updatedatabase.pl
@@ -2184,6 +2184,13 @@ if (C4::Context->preference("Version") <
TransformToNum($DBversion)) {
SetVersion ($DBversion);
}
+$DBversion = "3.01.00.014";
+if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
+ $dbh->do("INSERT INTO `systempreferences` ( `variable` , `value`
, `options` , `explanation` , `type` ) VALUES ( 'calcFineOnReturn',
'0', '', 'Turns on the feature to calculate fines at the time of
return and/or renewal as an alternative to the nightly cronjob fines
system.', 'YesNo' )");
+ print "Upgrade to $DBversion done (add new syspref)\n";
+ SetVersion ($DBversion);
+}
+
=item DropAllForeignKeys($table)
Drop all foreign keys of the table $table
--
1.5.6.5
More information about the Koha-patches
mailing list