Bug 18532: (follow-up) Remove digest switch and add preference
[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;
79 use Getopt::Long;
80
81 use Koha::Script -cron;
82 use C4::Circulation;
83 use C4::Context;
84 use C4::Log;
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 = '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     # CanBookBeRenewed returns 'auto_renew' when the renewal should be done by this script
148     my ( $ok, $error ) = CanBookBeRenewed( $auto_renew->borrowernumber, $auto_renew->itemnumber, undef, 1 );
149     if ( $error eq 'auto_renew' ) {
150         if ($verbose) {
151             say sprintf "Issue id: %s for borrower: %s and item: %s %s be renewed.",
152               $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber, $confirm ? 'will' : 'would';
153         }
154         if ($confirm){
155             my $date_due = AddRenewal( $auto_renew->borrowernumber, $auto_renew->itemnumber, $auto_renew->branchcode, undef, undef, undef, 0 );
156             $auto_renew->auto_renew_error(undef)->store;
157         }
158         push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew unless $borrower_preferences && (!$borrower_preferences->{transports} || !$borrower_preferences->{transports}->{email} || $borrower_preferences->{'wants_digest'});
159     } elsif ( $error eq 'too_many'
160         or $error eq 'on_reserve'
161         or $error eq 'restriction'
162         or $error eq 'overdue'
163         or $error eq 'too_unseen'
164         or $error eq 'auto_account_expired'
165         or $error eq 'auto_too_late'
166         or $error eq 'auto_too_much_oweing'
167         or $error eq 'auto_too_soon'
168         or $error eq 'item_denied_renewal' ) {
169         if ( $verbose ) {
170             say sprintf "Issue id: %s for borrower: %s and item: %s %s not be renewed. (%s)",
171               $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber, $confirm ? 'will' : 'would', $error;
172         }
173         if ( not $auto_renew->auto_renew_error or $error ne $auto_renew->auto_renew_error ) {
174             $auto_renew->auto_renew_error($error)->store if $confirm;
175             push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew
176               if $error ne 'auto_too_soon' && (!$borrower_preferences || ($borrower_preferences->{transports} && $borrower_preferences->{transports}->{email} && !$borrower_preferences->{'wants_digest'}));    # Do not notify if it's too soon
177         }
178     }
179
180     if ( $borrower_preferences && $borrower_preferences->{transports} && $borrower_preferences->{transports}->{email} && $borrower_preferences->{'wants_digest'} ) {
181         # cache this one to process after we've run through all of the items.
182         if ($digest_per_branch) {
183             $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{success}++ if $error eq 'auto_renew';
184             $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error == 'auto_too_soon' ;
185         } else {
186             $renew_digest->{ $auto_renew->borrowernumber }->{success} ++ if $error eq 'auto_renew';
187             $renew_digest->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error eq 'auto_too_soon' ;
188         }
189     }
190
191 }
192
193 if ( $send_notices && $confirm ) {
194     for my $borrowernumber ( keys %report ) {
195         my $patron = Koha::Patrons->find($borrowernumber);
196         for my $issue ( @{ $report{$borrowernumber} } ) {
197             my $item   = Koha::Items->find( $issue->itemnumber );
198             my $letter = C4::Letters::GetPreparedLetter(
199                 module      => 'circulation',
200                 letter_code => 'AUTO_RENEWALS',
201                 tables      => {
202                     borrowers => $patron->borrowernumber,
203                     issues    => $issue->itemnumber,
204                     items     => $issue->itemnumber,
205                     biblio    => $item->biblionumber,
206                 },
207                 lang => $patron->lang,
208             );
209
210             my $library = Koha::Libraries->find( $patron->branchcode );
211             my $admin_email_address = $library->branchemail || C4::Context->preference('KohaAdminEmailAddress');
212
213             C4::Letters::EnqueueLetter(
214                 {   letter                 => $letter,
215                     borrowernumber         => $borrowernumber,
216                     message_transport_type => 'email',
217                     from_address           => $admin_email_address,
218                 }
219             );
220         }
221     }
222
223     if ($digest_per_branch) {
224         while (my ($branchcode, $digests) = each %$renew_digest) {
225             send_digests({
226                 digests => $digests,
227                 branchcode => $branchcode,
228                 letter_code => 'AUTO_RENEWALS_DGST',
229             });
230         }
231     } else {
232         send_digests({
233             digests => $renew_digest,
234             letter_code => 'AUTO_RENEWALS_DGST',
235         });
236     }
237 }
238
239 =head1 METHODS
240
241 =head2 send_digests
242
243     send_digests({
244         digests => ...,
245         letter_code => ...,
246     })
247
248 Enqueue digested letters.
249
250 Parameters:
251
252 =over 4
253
254 =item C<$digests>
255
256 Reference to the array of digested messages.
257
258 =item C<$letter_code>
259
260 String that denote the letter code.
261
262 =back
263
264 =cut
265
266 sub send_digests {
267     my $params = shift;
268
269     my $admin_email_address = C4::Context->preference('KohaAdminEmailAddress');
270
271     PATRON: while ( my ( $borrowernumber, $digest ) = each %{$params->{digests}} ) {
272         my $borrower_preferences =
273             C4::Members::Messaging::GetMessagingPreferences(
274                 {
275                     borrowernumber => $borrowernumber,
276                     message_name   => 'auto_renewals'
277                 }
278             );
279
280         next PATRON unless $borrower_preferences; # how could this happen?
281
282         my $patron = Koha::Patrons->find( $borrowernumber );
283         my $library = Koha::Libraries->find( $params->{branchcode} );
284         my $from_address = $library->{branchemail} || $admin_email_address;
285
286         foreach my $transport ( keys %{ $borrower_preferences->{'transports'} } ) {
287             my $letter = C4::Letters::GetPreparedLetter (
288                 module => 'circulation',
289                 letter_code => $params->{letter_code},
290                 branchcode => $params->{branchcode},
291                 lang => $patron->lang,
292                 substitute => {
293                     error => $digest->{error}||0,
294                     success => $digest->{success}||0,
295                 },
296                 tables      => {
297                     borrowers => $patron->borrowernumber,
298                 },
299                 message_transport_type => $transport,
300             ) || warn "no letter of type '$params->{letter_code}' found for borrowernumber $borrowernumber. Please see sample_notices.sql";
301
302             next unless $letter;
303
304             C4::Letters::EnqueueLetter({
305                 letter                 => $letter,
306                 borrowernumber         => $borrowernumber,
307                 from_address           => $from_address,
308                 message_transport_type => $transport
309             });
310         }
311     }
312 }