Bug 17600: Standardize our EXPORT_OK
[koha.git] / misc / cronjobs / automatic_renewals.pl
1 #!/usr/bin/perl
2
3 # This file is part of Koha.
4 #
5 # Copyright (C) 2014 Hochschule für Gesundheit (hsg), Germany
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 =head1 NAME
21
22 automatic_renewals.pl - cron script to renew loans
23
24 =head1 SYNOPSIS
25
26 ./automatic_renewals.pl [-c|--confirm] [-s|--send-notices] [-d|--digest] [-b|--digest-per-branch] [-v|--verbose]
27
28 or, in crontab:
29 # Once every day for digest messages
30 0 3 * * * automatic_renewals.pl -c -d
31 # Three times a day for non digest messages
32 0 0,8,16 * * * automatic_renewals.pl -c
33
34 =head1 DESCRIPTION
35
36 This script searches for issues scheduled for automatic renewal
37 (issues.auto_renew). If there are still renews left (Renewals allowed)
38 and the renewal isn't premature (No Renewal before) the issue is renewed.
39
40 =head1 OPTIONS
41
42 =over
43
44 =item B<-s|--send-notices>
45
46 DEPRECATED: The system preference AutoRenewalNotices should be used to determine
47 whether notices are sent or not
48 Send AUTO_RENEWALS notices to patrons if the auto renewal has been done.
49
50 =item B<-v|--verbose>
51
52 Print report to standard out.
53
54 =item B<-c|--confirm>
55
56 Without this parameter no changes will be made
57
58 =item B<-b|--digest-per-branch>
59
60 Flag to indicate that generation of message digests should be
61 performed separately for each branch.
62
63 A patron could potentially have loans at several different branches
64 There is no natural branch to set as the sender on the aggregated
65 message in this situation so the default behavior is to use the
66 borrowers home branch.  This could surprise to the borrower when
67 message sender is a library where they have not borrowed anything.
68
69 Enabling this flag ensures that the issuing library is the sender of
70 the digested message.  It has no effect unless the borrower has
71 chosen 'Digests only' on the advance messages.
72
73 =back
74
75 =cut
76
77 use Modern::Perl;
78 use Pod::Usage qw( pod2usage );
79 use Getopt::Long qw( GetOptions );
80
81 use Koha::Script -cron;
82 use C4::Circulation qw( CanBookBeRenewed AddRenewal );
83 use C4::Context;
84 use C4::Log qw( cronlogaction );
85 use C4::Letters;
86 use Koha::Checkouts;
87 use Koha::Libraries;
88 use Koha::Patrons;
89
90 my ( $help, $send_notices, $verbose, $confirm, $digest_per_branch );
91 GetOptions(
92     'h|help' => \$help,
93     's|send-notices' => \$send_notices,
94     'v|verbose'    => \$verbose,
95     'c|confirm'     => \$confirm,
96     'b|digest-per-branch' => \$digest_per_branch,
97 ) || pod2usage(1);
98
99 pod2usage(0) if $help;
100
101 my $send_notices_pref = C4::Context->preference('AutoRenewalNotices');
102 if ( $send_notices_pref eq 'cron' ) {
103     warn <<'END_WARN';
104
105 The "AutoRenewalNotices" syspref is set to 'Follow the cron switch'.
106 The send_notices switch for this script is deprecated, you should either set the preference
107 to 'Never send emails' or 'Follow patron messaging preferences'
108
109 END_WARN
110 } else {
111     # If not following cron then we should not send if set to never
112     # and always send any generated according to preferences if following those
113     $send_notices = $send_notices_pref eq 'never' ? 0 : 1;
114 }
115
116 # Since advance notice options are not visible in the web-interface
117 # unless EnhancedMessagingPreferences is on, let the user know that
118 # this script probably isn't going to do much
119 if ( ! C4::Context->preference('EnhancedMessagingPreferences') ) {
120     warn <<'END_WARN';
121
122 The "EnhancedMessagingPreferences" syspref is off.
123 Therefore, it is unlikely that this script will actually produce any messages to be sent.
124 To change this, edit the "EnhancedMessagingPreferences" syspref.
125
126 END_WARN
127 }
128
129 cronlogaction();
130
131 $verbose = 1 unless $verbose or $confirm;
132 print "Test run only\n" unless $confirm;
133
134 print "getting auto renewals\n" if $verbose;
135 my $auto_renews = Koha::Checkouts->search({ auto_renew => 1, 'borrower.autorenew_checkouts' => 1 },{ join => 'borrower'});
136 print "found " . $auto_renews->count . " auto renewals\n" if $verbose;
137
138 my $renew_digest = {};
139 my %report;
140 while ( my $auto_renew = $auto_renews->next ) {
141     print "examining item '" . $auto_renew->itemnumber . "' to auto renew\n" if $verbose;
142
143     my $borrower_preferences;
144     $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $auto_renew->borrowernumber,
145                                                                                    message_name   => 'auto_renewals' } ) if $send_notices_pref eq 'preferences';
146
147     $send_notices = 1 if !$send_notices && $send_notices_pref eq 'preferences' && $borrower_preferences && $borrower_preferences->{transports} && $borrower_preferences->{transports}->{email};
148
149     # CanBookBeRenewed returns 'auto_renew' when the renewal should be done by this script
150     my ( $ok, $error ) = CanBookBeRenewed( $auto_renew->borrowernumber, $auto_renew->itemnumber, undef, 1 );
151     if ( $error eq 'auto_renew' ) {
152         if ($verbose) {
153             say sprintf "Issue id: %s for borrower: %s and item: %s %s be renewed.",
154               $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber, $confirm ? 'will' : 'would';
155         }
156         if ($confirm){
157             my $date_due = AddRenewal( $auto_renew->borrowernumber, $auto_renew->itemnumber, $auto_renew->branchcode, undef, undef, undef, 0 );
158             $auto_renew->auto_renew_error(undef)->store;
159         }
160         push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew unless $send_notices_pref ne 'cron' && (!$borrower_preferences || !$borrower_preferences->{transports} || !$borrower_preferences->{transports}->{email} || $borrower_preferences->{'wants_digest'});
161     } elsif ( $error eq 'too_many'
162         or $error eq 'on_reserve'
163         or $error eq 'restriction'
164         or $error eq 'overdue'
165         or $error eq 'too_unseen'
166         or $error eq 'auto_account_expired'
167         or $error eq 'auto_too_late'
168         or $error eq 'auto_too_much_oweing'
169         or $error eq 'auto_too_soon'
170         or $error eq 'item_denied_renewal' ) {
171         if ( $verbose ) {
172             say sprintf "Issue id: %s for borrower: %s and item: %s %s not be renewed. (%s)",
173               $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber, $confirm ? 'will' : 'would', $error;
174         }
175         if ( not $auto_renew->auto_renew_error or $error ne $auto_renew->auto_renew_error ) {
176             $auto_renew->auto_renew_error($error)->store if $confirm;
177             push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew
178               if $error ne 'auto_too_soon' && ($send_notices_pref eq 'cron' || ($borrower_preferences && $borrower_preferences->{transports} && $borrower_preferences->{transports}->{email} && !$borrower_preferences->{'wants_digest'}));    # Do not notify if it's too soon
179         }
180     }
181
182     if ( $borrower_preferences && $borrower_preferences->{transports} && $borrower_preferences->{transports}->{email} && $borrower_preferences->{'wants_digest'} ) {
183         # cache this one to process after we've run through all of the items.
184         if ($digest_per_branch) {
185             $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{success}++ if $error eq 'auto_renew';
186             $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error eq 'auto_too_soon' ;
187             push @{$renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{issues}}, $auto_renew->itemnumber;
188         } else {
189             $renew_digest->{ $auto_renew->borrowernumber }->{success} ++ if $error eq 'auto_renew';
190             $renew_digest->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error eq 'auto_too_soon' ;
191             push @{$renew_digest->{ $auto_renew->borrowernumber }->{issues}}, $auto_renew->itemnumber;
192         }
193     }
194
195 }
196
197 if ( $send_notices && $confirm ) {
198     for my $borrowernumber ( keys %report ) {
199         my $patron = Koha::Patrons->find($borrowernumber);
200         for my $issue ( @{ $report{$borrowernumber} } ) {
201             my $item   = Koha::Items->find( $issue->itemnumber );
202             my $letter = C4::Letters::GetPreparedLetter(
203                 module      => 'circulation',
204                 letter_code => 'AUTO_RENEWALS',
205                 tables      => {
206                     borrowers => $patron->borrowernumber,
207                     issues    => $issue->itemnumber,
208                     items     => $issue->itemnumber,
209                     biblio    => $item->biblionumber,
210                 },
211                 lang => $patron->lang,
212             );
213
214             my $library = Koha::Libraries->find( $patron->branchcode );
215             my $admin_email_address = $library->from_email_address;
216
217             C4::Letters::EnqueueLetter(
218                 {   letter                 => $letter,
219                     borrowernumber         => $borrowernumber,
220                     message_transport_type => 'email',
221                     from_address           => $admin_email_address,
222                 }
223             );
224         }
225     }
226
227     if ($digest_per_branch) {
228         while (my ($branchcode, $digests) = each %$renew_digest) {
229             send_digests({
230                 digests => $digests,
231                 branchcode => $branchcode,
232                 letter_code => 'AUTO_RENEWALS_DGST',
233             });
234         }
235     } else {
236         send_digests({
237             digests => $renew_digest,
238             letter_code => 'AUTO_RENEWALS_DGST',
239         });
240     }
241 }
242
243 =head1 METHODS
244
245 =head2 send_digests
246
247     send_digests({
248         digests => ...,
249         letter_code => ...,
250     })
251
252 Enqueue digested letters.
253
254 Parameters:
255
256 =over 4
257
258 =item C<$digests>
259
260 Reference to the array of digested messages.
261
262 =item C<$letter_code>
263
264 String that denote the letter code.
265
266 =back
267
268 =cut
269
270 sub send_digests {
271     my $params = shift;
272
273     PATRON: while ( my ( $borrowernumber, $digest ) = each %{$params->{digests}} ) {
274         my $borrower_preferences =
275             C4::Members::Messaging::GetMessagingPreferences(
276                 {
277                     borrowernumber => $borrowernumber,
278                     message_name   => 'auto_renewals'
279                 }
280             );
281
282         next PATRON unless $borrower_preferences; # how could this happen?
283
284         my $patron = Koha::Patrons->find( $borrowernumber );
285         my $library = Koha::Libraries->find( $params->{branchcode} );
286         my $from_address = $library->from_email_address;
287
288         foreach my $transport ( keys %{ $borrower_preferences->{'transports'} } ) {
289             my $letter = C4::Letters::GetPreparedLetter (
290                 module => 'circulation',
291                 letter_code => $params->{letter_code},
292                 branchcode => $params->{branchcode},
293                 lang => $patron->lang,
294                 substitute => {
295                     error => $digest->{error}||0,
296                     success => $digest->{success}||0,
297                 },
298                 loops => { issues => \@{$digest->{issues}} },
299                 tables      => {
300                     borrowers => $patron->borrowernumber,
301                 },
302                 message_transport_type => $transport,
303             ) || warn "no letter of type '$params->{letter_code}' found for borrowernumber $borrowernumber. Please see sample_notices.sql";
304
305             next unless $letter;
306
307             C4::Letters::EnqueueLetter({
308                 letter                 => $letter,
309                 borrowernumber         => $borrowernumber,
310                 from_address           => $from_address,
311                 message_transport_type => $transport
312             });
313         }
314     }
315 }