Merge remote-tracking branch 'origin/new/bug_7083'
[koha.git] / misc / cronjobs / longoverdue.pl
1 #!/usr/bin/perl
2 #-----------------------------------
3 # Copyright 2008 LibLime
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #-----------------------------------
20
21 =head1 NAME
22
23 longoverdue.pl  cron script to set lost statuses on overdue materials.
24                 Execute without options for help.
25
26 =cut
27
28 use strict;
29 use warnings;
30 BEGIN {
31     # find Koha's Perl modules
32     # test carefully before changing this
33     use FindBin;
34     eval { require "$FindBin::Bin/../kohalib.pl" };
35 }
36 use C4::Context;
37 use C4::Items;
38 use C4::Circulation qw/LostItem/;
39 use Getopt::Long;
40
41 my  $lost;  #  key=lost value,  value=num days.
42 my ($charge, $verbose, $confirm, $quiet);
43 my $endrange = 366;
44
45 GetOptions( 
46     'lost=s%'    => \$lost,
47     'c|charge=s' => \$charge,
48     'confirm'    => \$confirm,
49     'verbose'    => \$verbose,
50     'quiet'      => \$quiet,
51     'maxdays=s'  => \$endrange
52 );
53
54 my $usage = << 'ENDUSAGE';
55 longoverdue.pl : This cron script set lost values on overdue items and optionally sets charges the patron's account
56 for the item's replacement price.  It is designed to be run as a nightly job.  The command line options that globally
57 define this behavior for this script  will likely be moved into Koha's core circulation / issuing rules code in a 
58 near-term release, so this script is not intended to have a long lifetime.  
59
60 This script takes the following parameters :
61
62     --lost | -l         This option takes the form of n=lv,
63                         where n is num days overdue, and lv is the lost value.  See warning below.
64
65     --charge | -c       This specifies what lost value triggers Koha to charge the account for the
66                         lost item.  Replacement costs are not charged if this is not specified.
67
68     --verbose | v       verbose.
69
70     --confirm           confirm.  without this option, the script will report the number of affected items and
71                         return without modifying any records.
72
73     --quiet             suppress summary output.
74
75     --maxdays           Specifies the end of the range of overdue days to deal with (defaults to 366).  This
76                         value is universal to all lost num days overdue passed.
77
78   examples :
79   $PERL5LIB/misc/cronjobs/longoverdue.pl --lost 30=1
80     Would set LOST=1 after 30 days (up to one year), but not charge the account.
81     This would be suitable for the Koha default LOST authorized value of 1 -> 'Lost'.
82
83   $PERL5LIB/misc/cronjobs/longoverdue.pl --lost 60=2 --charge 1
84     Would set LOST=2 after 60 days (up to one year), and charge the account when setting LOST=2.
85     This would be suitable for the Koha default LOST authorized value of 2 -> 'Long Overdue' 
86
87 WARNING:  Flippant use of this script could set all or most of the items in your catalog to Lost and charge your
88 patrons for them!
89
90 WARNING:  This script is known to be faulty.  It is NOT recommended to use multiple --lost options.
91           See http://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=2883
92
93 ENDUSAGE
94
95 # FIXME: We need three pieces of data to operate:
96 #         ~ lower bound (number of days),
97 #         ~ upper bound (number of days),
98 #         ~ new lost value.
99 #        Right now we get only two, causing the endrange hack.  This is a design-level failure.
100 # FIXME: do checks on --lost ranges to make sure they are exclusive.
101 # FIXME: do checks on --lost ranges to make sure the authorized values exist.
102 # FIXME: do checks on --lost ranges to make sure don't go past endrange.
103 # FIXME: convert to using pod2usage
104 # FIXME: allow --help or -h
105
106 if ( ! defined($lost) ) {
107     print $usage;
108     die "ERROR: No --lost (-l) option defined";
109 }
110 unless ($confirm) {
111     $verbose = 1;     # If you're not running it for real, then the whole point is the print output.
112     print "### TEST MODE -- NO ACTIONS TAKEN ###\n";
113 }
114
115 # In my opinion, this line is safe SQL to have outside the API. --atz
116 our $bounds_sth = C4::Context->dbh->prepare("SELECT DATE_SUB(CURDATE(), INTERVAL ? DAY)");
117
118 sub bounds ($) {
119     $bounds_sth->execute(shift);
120     return $bounds_sth->fetchrow;
121 }
122
123 # FIXME - This sql should be inside the API.
124 sub longoverdue_sth {
125     my $query = "
126     SELECT items.itemnumber, borrowernumber, date_due
127       FROM issues, items
128      WHERE items.itemnumber = issues.itemnumber
129       AND  DATE_SUB(CURDATE(), INTERVAL ? DAY)  > date_due
130       AND  DATE_SUB(CURDATE(), INTERVAL ? DAY) <= date_due
131       AND  itemlost <> ?
132      ORDER BY date_due
133     ";
134     return C4::Context->dbh->prepare($query);
135 }
136
137 #FIXME - Should add a 'system' user and get suitable userenv for it for logging, etc.
138
139 my $count;
140 # my @ranges = map { 
141 my @report;
142 my $total = 0;
143 my $i = 0;
144
145 # FIXME - The item is only marked returned if you supply --charge .
146 #         We need a better way to handle this.
147 #
148 my $sth_items = longoverdue_sth();
149
150 foreach my $startrange (sort keys %$lost) {
151     if( my $lostvalue = $lost->{$startrange} ) {
152         my ($date1) = bounds($startrange);
153         my ($date2) = bounds(  $endrange);
154         # print "\nRange ", ++$i, "\nDue $startrange - $endrange days ago ($date2 to $date1), lost => $lostvalue\n" if($verbose);
155         $verbose and 
156             printf "\nRange %s\nDue %3s - %3s days ago (%s to %s), lost => %s\n", ++$i,
157             $startrange, $endrange, $date2, $date1, $lostvalue;
158         $sth_items->execute($startrange, $endrange, $lostvalue);
159         $count=0;
160         while (my $row=$sth_items->fetchrow_hashref) {
161             printf ("Due %s: item %5s from borrower %5s to lost: %s\n", $row->{date_due}, $row->{itemnumber}, $row->{borrowernumber}, $lostvalue) if($verbose);
162             if($confirm) {
163                 ModItem({ itemlost => $lostvalue }, $row->{'biblionumber'}, $row->{'itemnumber'});
164                 LostItem($row->{'itemnumber'}, undef, 'CHARGE FEE') if( $charge && $charge eq $lostvalue);
165             }
166             $count++;
167         }
168         push @report, {
169            startrange => $startrange,
170              endrange => $endrange,
171                 range => "$startrange - $endrange",
172                 date1 => $date1,
173                 date2 => $date2,
174             lostvalue => $lostvalue,
175                 count => $count,
176         };
177         $total += $count;
178     }
179     $endrange = $startrange;
180 }
181
182 sub summarize ($$) {
183     my $arg = shift;    # ref to array
184     my $got_items = shift || 0;     # print "count" line for items
185     my @report = @$arg or return undef;
186     my $i = 0;
187     for my $range (@report) {
188         printf "\nRange %s\nDue %3s - %3s days ago (%s to %s), lost => %s\n", ++$i,
189             map {$range->{$_}} qw(startrange endrange date2 date1 lostvalue);
190         $got_items and printf "  %4s items\n", $range->{count};
191     }
192 }
193
194 if (!$quiet){
195     print "\n### LONGOVERDUE SUMMARY ###";
196     summarize (\@report, 1);
197     print "\nTOTAL: $total items\n";
198 }