Bug 31203: Alter other cronjobs that currenlty use cronlogaction
[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 $command_line_options = join(" ",@ARGV);
91
92 my ( $help, $send_notices, $verbose, $confirm, $digest_per_branch );
93 GetOptions(
94     'h|help' => \$help,
95     's|send-notices' => \$send_notices,
96     'v|verbose'    => \$verbose,
97     'c|confirm'     => \$confirm,
98     'b|digest-per-branch' => \$digest_per_branch,
99 ) || pod2usage(1);
100
101 pod2usage(0) if $help;
102
103 my $send_notices_pref = C4::Context->preference('AutoRenewalNotices');
104 if ( $send_notices_pref eq 'cron' ) {
105     warn <<'END_WARN';
106
107 The "AutoRenewalNotices" syspref is set to 'Follow the cron switch'.
108 The send_notices switch for this script is deprecated, you should either set the preference
109 to 'Never send emails' or 'Follow patron messaging preferences'
110
111 END_WARN
112 } else {
113     # If not following cron then:
114     # - we should not send if set to never
115     # - we will send any notice generated according to preferences if following those
116     $send_notices = $send_notices_pref eq 'never' ? 0 : 1;
117 }
118
119 # Since advance notice options are not visible in the web-interface
120 # unless EnhancedMessagingPreferences is on, let the user know that
121 # this script probably isn't going to do much
122 if ( ! C4::Context->preference('EnhancedMessagingPreferences') ) {
123     warn <<'END_WARN';
124
125 The "EnhancedMessagingPreferences" syspref is off.
126 Therefore, it is unlikely that this script will actually produce any messages to be sent.
127 To change this, edit the "EnhancedMessagingPreferences" syspref.
128
129 END_WARN
130 }
131
132 cronlogaction({ info => $command_line_options });
133
134 $verbose = 1 unless $verbose or $confirm;
135 print "Test run only\n" unless $confirm;
136
137 print "getting auto renewals\n" if $verbose;
138 my $auto_renews = Koha::Checkouts->search(
139     {
140         auto_renew                   => 1,
141         'patron.autorenew_checkouts' => 1,
142     },
143     {
144         join => ['patron','item']
145     }
146 );
147 print "found " . $auto_renews->count . " auto renewals\n" if $verbose;
148
149 my $renew_digest = {};
150 my %report;
151 while ( my $auto_renew = $auto_renews->next ) {
152     print "examining item '" . $auto_renew->itemnumber . "' to auto renew\n" if $verbose;
153
154     my ( $borrower_preferences, $wants_messages, $wants_digest ) = ( undef, 0, 0 );
155     if ( $send_notices_pref eq 'preferences' ){
156         $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences(
157             {
158                 borrowernumber => $auto_renew->borrowernumber,
159                 message_name   => 'auto_renewals'
160             }
161         );
162         $wants_messages = 1
163             if $borrower_preferences
164             && $borrower_preferences->{transports};
165         $wants_digest = 1
166             if $wants_messages
167             && $borrower_preferences->{wants_digest};
168     } else { # Preference is never or cron
169         $wants_messages = $send_notices;
170         $wants_digest = 0;
171     }
172
173     # CanBookBeRenewed returns 'auto_renew' when the renewal should be done by this script
174     my ( $ok, $error ) = CanBookBeRenewed( $auto_renew->borrowernumber, $auto_renew->itemnumber, undef, 1 );
175     my $updated;
176     if ( $error eq 'auto_renew' ) {
177         $updated = 1;
178         if ($verbose) {
179             say sprintf "Issue id: %s for borrower: %s and item: %s %s be renewed.",
180               $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber, $confirm ? 'will' : 'would';
181         }
182         if ($confirm){
183             my $date_due = AddRenewal( $auto_renew->borrowernumber, $auto_renew->itemnumber, $auto_renew->branchcode, undef, undef, undef, 0 );
184             $auto_renew->auto_renew_error(undef)->store;
185         }
186         push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew
187             if ( $wants_messages ) && !$wants_digest;
188     } elsif ( $error eq 'too_many'
189         or $error eq 'on_reserve'
190         or $error eq 'restriction'
191         or $error eq 'overdue'
192         or $error eq 'too_unseen'
193         or $error eq 'auto_account_expired'
194         or $error eq 'auto_too_late'
195         or $error eq 'auto_too_much_oweing'
196         or $error eq 'auto_too_soon'
197         or $error eq 'item_denied_renewal' ) {
198         if ( $verbose ) {
199             say sprintf "Issue id: %s for borrower: %s and item: %s %s not be renewed. (%s)",
200               $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber, $confirm ? 'will' : 'would', $error;
201         }
202         $updated = 1 if (!$auto_renew->auto_renew_error || $error ne $auto_renew->auto_renew_error);
203         if ( $updated ) {
204             $auto_renew->auto_renew_error($error)->store if $confirm;
205             push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew
206               if $error ne 'auto_too_soon' && ( $wants_messages  && !$wants_digest );    # Do not notify if it's too soon
207         }
208     }
209
210     if ( $wants_digest ) {
211         # cache this one to process after we've run through all of the items.
212         if ($digest_per_branch) {
213             $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{success}++ if $error eq 'auto_renew';
214             $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error eq 'auto_too_soon';
215             $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{results}->{$error}++ ;
216             push @{$renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{issues}}, $auto_renew->itemnumber;
217             $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{updated} = 1 if $updated && $error ne 'auto_too_soon';
218         } else {
219             $renew_digest->{ $auto_renew->borrowernumber }->{success} ++ if $error eq 'auto_renew';
220             $renew_digest->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error eq 'auto_too_soon';
221             $renew_digest->{ $auto_renew->borrowernumber }->{results}->{$error}++ ;
222             $renew_digest->{ $auto_renew->borrowernumber }->{updated} = 1 if $updated && $error ne 'auto_too_soon';
223             push @{$renew_digest->{ $auto_renew->borrowernumber }->{issues}}, $auto_renew->itemnumber;
224         }
225     }
226
227 }
228
229 if ( $send_notices && $confirm ) {
230     for my $borrowernumber ( keys %report ) {
231         my $patron = Koha::Patrons->find($borrowernumber);
232         my $borrower_preferences =
233             C4::Members::Messaging::GetMessagingPreferences(
234                 {
235                     borrowernumber => $borrowernumber,
236                     message_name   => 'auto_renewals'
237                 }
238             );
239         for my $issue ( @{ $report{$borrowernumber} } ) {
240             my $item = $issue->item;
241             # Force sending of email and only email if pref is set to "cron"
242             my @transports = $send_notices_pref eq 'preferences' ? keys %{ $borrower_preferences->{'transports'} } : 'email';
243             foreach my $transport ( @transports ) {
244                 my $letter = C4::Letters::GetPreparedLetter (
245                     module      => 'circulation',
246                     letter_code => 'AUTO_RENEWALS',
247                     tables      => {
248                         borrowers => $patron->borrowernumber,
249                         issues    => $issue->itemnumber,
250                         items     => $issue->itemnumber,
251                         biblio    => $item->biblionumber,
252                     },
253                     lang => $patron->lang,
254                     message_transport_type => $transport,
255                 );
256
257                 if ($letter) {
258                     my $library = $patron->library;
259                     my $admin_email_address = $library->from_email_address;
260
261                     C4::Letters::EnqueueLetter(
262                         {
263                             letter                 => $letter,
264                             borrowernumber         => $borrowernumber,
265                             from_address           => $admin_email_address,
266                             message_transport_type => $transport
267                         }
268                     );
269                 }
270             }
271         }
272     }
273
274     if ($digest_per_branch) {
275         while (my ($branchcode, $digests) = each %$renew_digest) {
276             send_digests({
277                 digests => $digests,
278                 branchcode => $branchcode,
279                 letter_code => 'AUTO_RENEWALS_DGST',
280             });
281         }
282     } else {
283         send_digests({
284             digests => $renew_digest,
285             letter_code => 'AUTO_RENEWALS_DGST',
286         });
287     }
288 }
289
290 cronlogaction({ action => 'End', info => "COMPLETED" });
291
292 =head1 METHODS
293
294 =head2 send_digests
295
296     send_digests({
297         digests => ...,
298         letter_code => ...,
299     })
300
301 Enqueue digested letters.
302
303 Parameters:
304
305 =over 4
306
307 =item C<$digests>
308
309 Reference to the array of digested messages.
310
311 =item C<$letter_code>
312
313 String that denote the letter code.
314
315 =back
316
317 =cut
318
319 sub send_digests {
320     my $params = shift;
321
322     PATRON: while ( my ( $borrowernumber, $digest ) = each %{$params->{digests}} ) {
323         next unless defined $digest->{updated} && $digest->{updated} == 1;
324         my $borrower_preferences =
325             C4::Members::Messaging::GetMessagingPreferences(
326                 {
327                     borrowernumber => $borrowernumber,
328                     message_name   => 'auto_renewals'
329                 }
330             );
331
332         next PATRON unless $borrower_preferences; # how could this happen?
333
334         my $patron = Koha::Patrons->find( $borrowernumber );
335         my $branchcode;
336         if ( defined $params->{branchcode} ) {
337             $branchcode = $params->{branchcode};
338         } else {
339             $branchcode = $patron->branchcode;
340         }
341         my $library = Koha::Libraries->find( $branchcode );
342         my $from_address = $library->from_email_address;
343
344         foreach my $transport ( keys %{ $borrower_preferences->{'transports'} } ) {
345             my $letter = C4::Letters::GetPreparedLetter (
346                 module => 'circulation',
347                 letter_code => $params->{letter_code},
348                 branchcode => $branchcode,
349                 lang => $patron->lang,
350                 substitute => {
351                     error => $digest->{error}||0,
352                     success => $digest->{success}||0,
353                     results => $digest->{results},
354                 },
355                 loops => { issues => \@{$digest->{issues}} },
356                 tables      => {
357                     borrowers => $patron->borrowernumber,
358                 },
359                 message_transport_type => $transport,
360             );
361
362             if ($letter) {
363                 C4::Letters::EnqueueLetter(
364                     {
365                         letter                 => $letter,
366                         borrowernumber         => $borrowernumber,
367                         from_address           => $from_address,
368                         message_transport_type => $transport
369                     }
370                 );
371             }
372             else {
373                 warn
374 "no letter of type '$params->{letter_code}' found for borrowernumber $borrowernumber. Please see sample_notices.sql";
375             }
376
377         }
378     }
379 }