Bug 31203: Alter other cronjobs that currenlty use cronlogaction
[koha.git] / misc / cronjobs / fines.pl
1 #!/usr/bin/perl
2
3 #  This script loops through each overdue item, determines the fine,
4 #  and updates the total amount of fines due by each user.  It relies on
5 #  the existence of /tmp/fines, which is created by ???
6 # Doesn't really rely on it, it relys on being able to write to /tmp/
7 # It creates the fines file
8 #
9 #  This script is meant to be run nightly out of cron.
10
11 # Copyright 2000-2002 Katipo Communications
12 # Copyright 2011 PTFS-Europe Ltd
13 #
14 # This file is part of Koha.
15 #
16 # Koha is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 3 of the License, or
19 # (at your option) any later version.
20 #
21 # Koha is distributed in the hope that it will be useful, but
22 # WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 # GNU General Public License for more details.
25 #
26 # You should have received a copy of the GNU General Public License
27 # along with Koha; if not, see <http://www.gnu.org/licenses>.
28
29 use strict;
30 use warnings;
31 use 5.010;
32
33 use Koha::Script -cron;
34 use C4::Context;
35 use C4::Overdues qw( Getoverdues CalcFine UpdateFine );
36 use Getopt::Long qw( GetOptions );
37 use Carp qw( carp croak );
38 use File::Spec;
39 use Try::Tiny qw( catch try );
40
41 use Koha::Calendar;
42 use Koha::DateUtils qw( dt_from_string output_pref );
43 use Koha::Patrons;
44 use C4::Log qw( cronlogaction );
45
46 my $help;
47 my $verbose;
48 my $output_dir;
49 my $log;
50 my $maxdays;
51
52 my $command_line_options = join(" ",@ARGV);
53
54 GetOptions(
55     'h|help'    => \$help,
56     'v|verbose' => \$verbose,
57     'l|log'     => \$log,
58     'o|out:s'   => \$output_dir,
59     'm|maxdays:i' => \$maxdays,
60 );
61 my $usage = << 'ENDUSAGE';
62
63 This script calculates and charges overdue fines
64 to patron accounts.  The Koha system preference 'finesMode' controls
65 whether the fines are calculated and charged to the patron accounts ("Calculate and charge");
66 or not calculated ("Don't calculate").
67
68 This script has the following parameters :
69     -h --help: this message
70     -l --log: log the output to a file (optional if the -o parameter is given)
71     -o --out:  ouput directory for logs (defaults to env or /tmp if !exist)
72     -v --verbose
73     -m --maxdays: how many days back of overdues to process
74
75 ENDUSAGE
76
77 if ($help) {
78     print $usage;
79     exit;
80 }
81
82 my $script_handler = Koha::Script->new({ script => $0 });
83
84 try {
85     $script_handler->lock_exec;
86 }
87 catch {
88     my $message = "Skipping execution of $0 ($_)";
89     print STDERR "$message\n"
90         if $verbose;
91     cronlogaction( $message );
92     exit;
93 };
94
95 cronlogaction({ info => $command_line_options });
96
97 my @borrower_fields =
98   qw(cardnumber categorycode surname firstname email phone address citystate);
99 my @item_fields  = qw(itemnumber barcode date_due);
100 my @other_fields = qw(days_overdue fine);
101 my $libname      = C4::Context->preference('LibraryName');
102 my $control      = C4::Context->preference('CircControl');
103 my $mode         = C4::Context->preference('finesMode');
104 my $delim = "\t";    # ?  C4::Context->preference('CSVDelimiter') || "\t";
105
106 my %is_holiday;
107 my $today = dt_from_string();
108 my $filename;
109 if ($log or $output_dir) {
110     $filename = get_filename($output_dir);
111 }
112
113 my $fh;
114 if ($filename) {
115     open $fh, '>>', $filename or croak "Cannot write file $filename: $!";
116     print {$fh} join $delim, ( @borrower_fields, @item_fields, @other_fields );
117     print {$fh} "\n";
118 }
119 my $counted = 0;
120 my $updated = 0;
121 my $params;
122 $params->{maximumdays} = $maxdays if $maxdays;
123 my $overdues = Getoverdues($params);
124 for my $overdue ( @{$overdues} ) {
125     next if $overdue->{itemlost};
126
127     if ( !defined $overdue->{borrowernumber} ) {
128         carp
129 "ERROR in Getoverdues : issues.borrowernumber IS NULL.  Repair 'issues' table now!  Skipping record.\n";
130         next;
131     }
132     my $patron = Koha::Patrons->find( $overdue->{borrowernumber} );
133     my $branchcode =
134         ( $control eq 'ItemHomeLibrary' ) ? $overdue->{homebranch}
135       : ( $control eq 'PatronLibrary' )   ? $patron->branchcode
136       :                                     $overdue->{branchcode};
137
138 # In final case, CircControl must be PickupLibrary. (branchcode comes from issues table here).
139     if ( !exists $is_holiday{$branchcode} ) {
140         $is_holiday{$branchcode} = set_holiday( $branchcode, $today );
141     }
142
143     my $datedue = dt_from_string( $overdue->{date_due} );
144     if ( DateTime->compare( $datedue, $today ) == 1 ) {
145         next;    # not overdue
146     }
147     ++$counted;
148
149     my ( $amount, $unitcounttotal, $unitcount ) =
150       CalcFine( $overdue, $patron->categorycode,
151         $branchcode, $datedue, $today );
152
153     # Don't update the fine if today is a holiday.
154     # This ensures that dropbox mode will remove the correct amount of fine.
155     if (
156         $mode eq 'production'
157         && ( !$is_holiday{$branchcode}
158             || C4::Context->preference('ChargeFinesOnClosedDays') )
159         && ( $amount && $amount > 0 )
160       )
161     {
162         UpdateFine(
163             {
164                 issue_id       => $overdue->{issue_id},
165                 itemnumber     => $overdue->{itemnumber},
166                 borrowernumber => $overdue->{borrowernumber},
167                 amount         => $amount,
168                 due            => $datedue,
169             }
170         );
171         $updated++;
172     }
173     my $borrower = $patron->unblessed;
174     if ($filename) {
175         my @cells;
176         push @cells,
177           map { defined $borrower->{$_} ? $borrower->{$_} : q{} }
178           @borrower_fields;
179         push @cells, map { $overdue->{$_} } @item_fields;
180         push @cells, $unitcounttotal, $amount;
181         say {$fh} join $delim, @cells;
182     }
183 }
184 if ($filename){
185     close $fh;
186 }
187
188 if ($verbose) {
189     my $overdue_items = @{$overdues};
190     print <<"EOM";
191 Fines assessment -- $today
192 EOM
193     if ($filename) {
194         say "Saved to $filename";
195     }
196     print <<"EOM";
197 Number of Overdue Items:
198      counted $overdue_items
199     reported $counted
200      updated $updated
201
202 EOM
203 }
204
205 cronlogaction({ action => 'End', info => "COMPLETED" });
206
207 sub set_holiday {
208     my ( $branch, $dt ) = @_;
209
210     my $calendar = Koha::Calendar->new( branchcode => $branch );
211     return $calendar->is_holiday($dt);
212 }
213
214 sub get_filename {
215     my $directory = shift;
216     if ( !$directory ) {
217         $directory = C4::Context::temporary_directory;
218     }
219     if ( !-d $directory ) {
220         carp "Could not write to $directory ... does not exist!";
221     }
222     my $name = C4::Context->config('database');
223     $name =~ s/\W//;
224     $name .= join q{}, q{_}, $today->ymd(), '.log';
225     $name = File::Spec->catfile( $directory, $name );
226     if ($verbose && $log) {
227         say "writing to $name";
228     }
229     return $name;
230 }