# 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/
+# Doesn't 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
+# Copyright 2011 PTFS-Europe Ltd
#
# 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 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 3 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.
+# 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
-
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
use strict;
-BEGIN {
- # find Koha's Perl modules
- # test carefully before changing this
- use FindBin;
- eval { require "$FindBin::Bin/kohalib.pl" };
-}
+use warnings;
+use 5.010;
+
use C4::Context;
-use C4::Circulation;
use C4::Overdues;
-use C4::Calendar;
-use Date::Calc qw/Date_to_Days/;
-use C4::Biblio;
-
-
-my $fldir = "/tmp" ;
-
-my $libname=C4::Context->preference('LibraryName');
-my $dbname= C4::Context->config('database');
-
-my $today = C4::Dates->new();
-my $datestr = $today->output('iso');
-my $today_days= Date_to_Days(split(/-/,$today->output('iso')));
-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\titemnumber\tdays_overdue\tfine\n";
-
-
-my $DEBUG =1;
-
-my $data=Getoverdues();
-my $overdueItemsCounted=0 ;
-my $borrowernumber;
-
-for (my $i=0;$i<scalar(@$data);$i++){
- my $datedue=C4::Dates->new($data->[$i]->{'date_due'},'iso');
- my $datedue_days = Date_to_Days(split(/-/,$datedue->output('iso')));
- my $due_str=$datedue->output();
- 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 $calendar = C4::Calendar->new( branchcode => $branchcode );
-
- my $isHoliday = $calendar->isHoliday( split( '/', C4::Dates->new()->output('metric') ) );
-
- if ($datedue_days <= $today_days){
- $overdueItemsCounted++ if $DEBUG;
- my $difference=$today_days - $datedue_days;
- my ($amount,$type,$printout,$daycounttotal,$daycount)=
- CalcFine($data->[$i], $borrower->{'categorycode'}, $branchcode,undef,undef, $datedue ,$today);
- my ($delays1,$delays2,$delays3)=GetOverdueDelays($borrower->{'categorycode'});
-
- # Don't update the fine if today is a holiday.
- # This ensures that dropbox mode will remove the correct amount of fine.
- if( (C4::Context->preference('finesMode') eq 'production') && ! $isHoliday ) {
- # FIXME - $type is always null, afaict.
- UpdateFine($data->[$i]->{'itemnumber'},$data->[$i]->{'borrowernumber'},$amount,$type,$due_str) if( $amount > 0 ) ;
- }
- 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$data->[$i]->{'itemnumber'}\t$daycounttotal\t$amount\n";
- }
+use Getopt::Long;
+use Carp;
+use File::Spec;
+
+use Koha::Calendar;
+use Koha::DateUtils;
+use C4::Log;
+
+my $help;
+my $verbose;
+my $output_dir;
+my $log;
+
+GetOptions(
+ 'h|help' => \$help,
+ 'v|verbose' => \$verbose,
+ 'l|log' => \$log,
+ 'o|out:s' => \$output_dir,
+);
+my $usage = << 'ENDUSAGE';
+
+This script calculates and charges overdue fines
+to patron accounts. The Koha system preference 'finesMode' controls
+whether the fines are calculated and charged to the patron accounts ("Calculate and charge");
+calculated and emailed to the admin but not applied ("Calculate (but only for mailing to the admin)"); or not calculated ("Don't calculate").
+
+This script has the following parameters :
+ -h --help: this message
+ -l --log: log the output to a file (optional if the -o parameter is given)
+ -o --out: ouput directory for logs (defaults to env or /tmp if !exist)
+ -v --verbose
+
+ENDUSAGE
+
+if ($help) {
+ print $usage;
+ exit;
}
-my $numOverdueItems=scalar(@$data);
-if ($DEBUG) {
- print <<EOM
+cronlogaction();
+
+my @borrower_fields =
+ qw(cardnumber categorycode surname firstname email phone address citystate);
+my @item_fields = qw(itemnumber barcode date_due);
+my @other_fields = qw(type days_overdue fine);
+my $libname = C4::Context->preference('LibraryName');
+my $control = C4::Context->preference('CircControl');
+my $mode = C4::Context->preference('finesMode');
+my $delim = "\t"; # ? C4::Context->preference('delimiter') || "\t";
+
+my %is_holiday;
+my $today = DateTime->now( time_zone => C4::Context->tz() );
+my $filename;
+if ($log or $output_dir) {
+ $filename = get_filename($output_dir);
+}
-Number of Overdue Items counted $overdueItemsCounted
-Number of Overdue Items reported $numOverdueItems
+my $fh;
+if ($filename) {
+ open $fh, '>>', $filename or croak "Cannot write file $filename: $!";
+ print {$fh} join $delim, ( @borrower_fields, @item_fields, @other_fields );
+ print {$fh} "\n";
+}
+my $counted = 0;
+my $overdues = Getoverdues();
+for my $overdue ( @{$overdues} ) {
+ next if $overdue->{itemlost};
+
+ if ( !defined $overdue->{borrowernumber} ) {
+ carp
+"ERROR in Getoverdues : issues.borrowernumber IS NULL. Repair 'issues' table now! Skipping record.\n";
+ next;
+ }
+ my $borrower = BorType( $overdue->{borrowernumber} );
+ my $branchcode =
+ ( $control eq 'ItemHomeLibrary' ) ? $overdue->{homebranch}
+ : ( $control eq 'PatronLibrary' ) ? $borrower->{branchcode}
+ : $overdue->{branchcode};
+
+# In final case, CircControl must be PickupLibrary. (branchcode comes from issues table here).
+ if ( !exists $is_holiday{$branchcode} ) {
+ $is_holiday{$branchcode} = set_holiday( $branchcode, $today );
+ }
+
+ my $datedue = dt_from_string( $overdue->{date_due} );
+ if ( DateTime->compare( $datedue, $today ) == 1 ) {
+ next; # not overdue
+ }
+ ++$counted;
+
+ my ( $amount, $type, $unitcounttotal ) =
+ CalcFine( $overdue, $borrower->{categorycode},
+ $branchcode, $datedue, $today );
+ $type ||= q{};
+
+ # Don't update the fine if today is a holiday.
+ # This ensures that dropbox mode will remove the correct amount of fine.
+ if ( $mode eq 'production' && !$is_holiday{$branchcode} ) {
+ if ( $amount > 0 ) {
+ UpdateFine(
+ {
+ issue_id => $overdue->{issue_id},
+ itemnumber => $overdue->{itemnumber},
+ borrowernumber => $overdue->{borrowernumber},
+ amount => $amount,
+ type => $type,
+ due => output_pref($datedue),
+ }
+ );
+ }
+ }
+ if ($filename) {
+ my @cells;
+ push @cells,
+ map { defined $borrower->{$_} ? $borrower->{$_} : q{} }
+ @borrower_fields;
+ push @cells, map { $overdue->{$_} } @item_fields;
+ push @cells, $type, $unitcounttotal, $amount;
+ say {$fh} join $delim, @cells;
+ }
+}
+if ($filename){
+ close $fh;
+}
+if ($verbose) {
+ my $overdue_items = @{$overdues};
+ print <<"EOM";
+Fines assessment -- $today
EOM
+ if ($filename) {
+ say "Saved to $filename";
+ }
+ print <<"EOM";
+Number of Overdue Items:
+ counted $overdue_items
+ reported $counted
+
+EOM
+}
+
+sub set_holiday {
+ my ( $branch, $dt ) = @_;
+
+ my $calendar = Koha::Calendar->new( branchcode => $branch );
+ return $calendar->is_holiday($dt);
}
-close FILE;
+sub get_filename {
+ my $directory = shift;
+ if ( !$directory ) {
+ $directory = File::Spec->tmpdir();
+ }
+ if ( !-d $directory ) {
+ carp "Could not write to $directory ... does not exist!";
+ }
+ my $name = C4::Context->config('database');
+ $name =~ s/\W//;
+ $name .= join q{}, q{_}, $today->ymd(), '.log';
+ $name = File::Spec->catfile( $directory, $name );
+ if ($verbose && $log) {
+ say "writing to $name";
+ }
+ return $name;
+}