Bug 18532: set auto_renewals as patron messaging 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 Send AUTO_RENEWALS notices to patrons if the auto renewal has been done.
47
48 =item B<-v|--verbose>
49
50 Print report to standard out.
51
52 =item B<-c|--confirm>
53
54 Without this parameter no changes will be made
55
56 =item B<-d|--digest>
57
58 Flag to indicate that this script should process digest messages.
59
60 =item B<-b|--digest-per-branch>
61
62 Flag to indicate that generation of message digests should be
63 performed separately for each branch. Needs --digest option.
64
65 A patron could potentially have loans at several different branches
66 There is no natural branch to set as the sender on the aggregated
67 message in this situation so the default behavior is to use the
68 borrowers home branch.  This could surprise to the borrower when
69 message sender is a library where they have not borrowed anything.
70
71 Enabling this flag ensures that the issuing library is the sender of
72 the digested message.  It has no effect unless the borrower has
73 chosen 'Digests only' on the advance messages.
74
75 =back
76
77 =cut
78
79 use Modern::Perl;
80 use Pod::Usage;
81 use Getopt::Long;
82
83 use Koha::Script -cron;
84 use C4::Circulation;
85 use C4::Context;
86 use C4::Log;
87 use C4::Letters;
88 use Koha::Checkouts;
89 use Koha::Libraries;
90 use Koha::Patrons;
91
92 my ( $help, $send_notices, $verbose, $confirm, $digest, $digest_per_branch );
93 GetOptions(
94     'h|help' => \$help,
95     's|send-notices' => \$send_notices,
96     'v|verbose'    => \$verbose,
97     'c|confirm'     => \$confirm,
98     'd|digest|' => \$digest,
99     'b|digest-per-branch' => \$digest_per_branch,
100 ) || pod2usage(1);
101
102 pod2usage(0) if $help;
103
104 # Since advance notice options are not visible in the web-interface
105 # unless EnhancedMessagingPreferences is on, let the user know that
106 # this script probably isn't going to do much
107 if ( ! C4::Context->preference('EnhancedMessagingPreferences') ) {
108     warn <<'END_WARN';
109
110 The "EnhancedMessagingPreferences" syspref is off.
111 Therefore, it is unlikely that this script will actually produce any messages to be sent.
112 To change this, edit the "EnhancedMessagingPreferences" syspref.
113
114 END_WARN
115 }
116
117 cronlogaction();
118
119 $verbose = 1 unless $verbose or $confirm;
120 print "Test run only\n" unless $confirm;
121
122 print "getting auto renewals\n" if $verbose;
123 my $auto_renews = Koha::Checkouts->search({ auto_renew => 1, 'borrower.autorenew_checkouts' => 1 },{ join => 'borrower'});
124 print "found " . $auto_renews->count . " auto renewals\n" if $verbose;
125
126 my $renew_digest = {};
127 my %report;
128 while ( my $auto_renew = $auto_renews->next ) {
129     print "examining item '" . $auto_renew->itemnumber . "' to auto renew\n" if $verbose;
130
131     my $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $auto_renew->borrowernumber,
132                                                                                    message_name   => 'auto_renewals' } );
133
134     next if !$digest && $borrower_preferences && $borrower_preferences->{'wants_digest'};
135
136     # CanBookBeRenewed returns 'auto_renew' when the renewal should be done by this script
137     my ( $ok, $error ) = CanBookBeRenewed( $auto_renew->borrowernumber, $auto_renew->itemnumber, undef, 1 );
138     if ( $error eq 'auto_renew' ) {
139         if ($verbose) {
140             say sprintf "Issue id: %s for borrower: %s and item: %s %s be renewed.",
141               $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber, $confirm ? 'will' : 'would';
142         }
143         if ($confirm){
144             my $date_due = AddRenewal( $auto_renew->borrowernumber, $auto_renew->itemnumber, $auto_renew->branchcode, undef, undef, undef, 0 );
145             $auto_renew->auto_renew_error(undef)->store;
146         }
147         push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew unless $borrower_preferences && (!$borrower_preferences->{transports} || !$borrower_preferences->{transports}->{email} || $borrower_preferences->{'wants_digest'});
148     } elsif ( $error eq 'too_many'
149         or $error eq 'on_reserve'
150         or $error eq 'restriction'
151         or $error eq 'overdue'
152         or $error eq 'too_unseen'
153         or $error eq 'auto_account_expired'
154         or $error eq 'auto_too_late'
155         or $error eq 'auto_too_much_oweing'
156         or $error eq 'auto_too_soon'
157         or $error eq 'item_denied_renewal' ) {
158         if ( $verbose ) {
159             say sprintf "Issue id: %s for borrower: %s and item: %s %s not be renewed. (%s)",
160               $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber, $confirm ? 'will' : 'would', $error;
161         }
162         if ( not $auto_renew->auto_renew_error or $error ne $auto_renew->auto_renew_error ) {
163             $auto_renew->auto_renew_error($error)->store if $confirm;
164             push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew
165               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
166         }
167     }
168
169     if ( $borrower_preferences && $borrower_preferences->{transports} && $borrower_preferences->{transports}->{email} && $borrower_preferences->{'wants_digest'} ) {
170         # cache this one to process after we've run through all of the items.
171         if ($digest_per_branch) {
172             $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{success}++ if $error eq 'auto_renew';
173             $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error == 'auto_too_soon' ;
174         } else {
175             $renew_digest->{ $auto_renew->borrowernumber }->{success} ++ if $error eq 'auto_renew';
176             $renew_digest->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error eq 'auto_too_soon' ;
177         }
178     }
179
180 }
181
182 if ( $send_notices && $confirm ) {
183     for my $borrowernumber ( keys %report ) {
184         my $patron = Koha::Patrons->find($borrowernumber);
185         for my $issue ( @{ $report{$borrowernumber} } ) {
186             my $item   = Koha::Items->find( $issue->itemnumber );
187             my $letter = C4::Letters::GetPreparedLetter(
188                 module      => 'circulation',
189                 letter_code => 'AUTO_RENEWALS',
190                 tables      => {
191                     borrowers => $patron->borrowernumber,
192                     issues    => $issue->itemnumber,
193                     items     => $issue->itemnumber,
194                     biblio    => $item->biblionumber,
195                 },
196                 lang => $patron->lang,
197             );
198
199             my $library = Koha::Libraries->find( $patron->branchcode );
200             my $admin_email_address = $library->branchemail || C4::Context->preference('KohaAdminEmailAddress');
201
202             C4::Letters::EnqueueLetter(
203                 {   letter                 => $letter,
204                     borrowernumber         => $borrowernumber,
205                     message_transport_type => 'email',
206                     from_address           => $admin_email_address,
207                 }
208             );
209         }
210     }
211
212     if ($digest_per_branch) {
213         while (my ($branchcode, $digests) = each %$renew_digest) {
214             send_digests({
215                 digests => $digests,
216                 branchcode => $branchcode,
217                 letter_code => 'AUTO_RENEWALS_DGST',
218             });
219         }
220     } else {
221         send_digests({
222             digests => $renew_digest,
223             letter_code => 'AUTO_RENEWALS_DGST',
224         });
225     }
226 }
227
228 =head1 METHODS
229
230 =head2 send_digests
231
232     send_digests({
233         digests => ...,
234         letter_code => ...,
235     })
236
237 Enqueue digested letters.
238
239 Parameters:
240
241 =over 4
242
243 =item C<$digests>
244
245 Reference to the array of digested messages.
246
247 =item C<$letter_code>
248
249 String that denote the letter code.
250
251 =back
252
253 =cut
254
255 sub send_digests {
256     my $params = shift;
257
258     my $admin_email_address = C4::Context->preference('KohaAdminEmailAddress');
259
260     PATRON: while ( my ( $borrowernumber, $digest ) = each %{$params->{digests}} ) {
261         my $borrower_preferences =
262             C4::Members::Messaging::GetMessagingPreferences(
263                 {
264                     borrowernumber => $borrowernumber,
265                     message_name   => 'auto_renewals'
266                 }
267             );
268
269         next PATRON unless $borrower_preferences; # how could this happen?
270
271         my $patron = Koha::Patrons->find( $borrowernumber );
272         my $library = Koha::Libraries->find( $params->{branchcode} );
273         my $from_address = $library->{branchemail} || $admin_email_address;
274
275         foreach my $transport ( keys %{ $borrower_preferences->{'transports'} } ) {
276             my $letter = C4::Letters::GetPreparedLetter (
277                 module => 'circulation',
278                 letter_code => $params->{letter_code},
279                 branchcode => $params->{branchcode},
280                 lang => $patron->lang,
281                 substitute => {
282                     error => $digest->{error}||0,
283                     success => $digest->{success}||0,
284                 },
285                 tables      => {
286                     borrowers => $patron->borrowernumber,
287                 },
288                 message_transport_type => $transport,
289             ) || warn "no letter of type '$params->{letter_code}' found for borrowernumber $borrowernumber. Please see sample_notices.sql";
290
291             next unless $letter;
292
293             C4::Letters::EnqueueLetter({
294                 letter                 => $letter,
295                 borrowernumber         => $borrowernumber,
296                 from_address           => $from_address,
297                 message_transport_type => $transport
298             });
299         }
300     }
301 }