3 # This file is part of Koha.
5 # Copyright (C) 2014 Hochschule für Gesundheit (hsg), Germany
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.
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.
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>.
22 automatic_renewals.pl - cron script to renew loans
26 ./automatic_renewals.pl [-c|--confirm] [-s|--send-notices] [-d|--digest] [-b|--digest-per-branch] [-v|--verbose]
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
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.
44 =item B<-s|--send-notices>
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.
52 Print report to standard out.
56 Without this parameter no changes will be made
58 =item B<-b|--digest-per-branch>
60 Flag to indicate that generation of message digests should be
61 performed separately for each branch.
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.
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.
78 use Pod::Usage qw( pod2usage );
79 use Getopt::Long qw( GetOptions );
81 use Koha::Script -cron;
83 use C4::Log qw( cronlogaction );
89 my $command_line_options = join(" ",@ARGV);
91 my ( $help, $send_notices, $verbose, $confirm, $digest_per_branch );
94 's|send-notices' => \$send_notices,
95 'v|verbose' => \$verbose,
96 'c|confirm' => \$confirm,
97 'b|digest-per-branch' => \$digest_per_branch,
100 pod2usage(0) if $help;
102 my $send_notices_pref = C4::Context->preference('AutoRenewalNotices');
103 if ( $send_notices_pref eq 'cron' ) {
106 The "AutoRenewalNotices" syspref is set to 'Follow the cron switch'.
107 The send_notices switch for this script is deprecated, you should either set the preference
108 to 'Never send emails' or 'Follow patron messaging preferences'
112 # If not following cron then:
113 # - we should not send if set to never
114 # - we will send any notice generated according to preferences if following those
115 $send_notices = $send_notices_pref eq 'never' ? 0 : 1;
118 # Since advance notice options are not visible in the web-interface
119 # unless EnhancedMessagingPreferences is on, let the user know that
120 # this script probably isn't going to do much
121 if ( ! C4::Context->preference('EnhancedMessagingPreferences') ) {
124 The "EnhancedMessagingPreferences" syspref is off.
125 Therefore, it is unlikely that this script will actually produce any messages to be sent.
126 To change this, edit the "EnhancedMessagingPreferences" syspref.
131 cronlogaction({ info => $command_line_options });
133 $verbose = 1 unless $verbose or $confirm;
134 print "Test run only\n" unless $confirm;
136 print "getting auto renewals\n" if $verbose;
137 my $auto_renews = Koha::Checkouts->search(
140 'patron.autorenew_checkouts' => 1,
143 join => ['patron','item']
146 print "found " . $auto_renews->count . " auto renewals\n" if $verbose;
148 my $renew_digest = {};
150 my @item_renewal_ids;
151 while ( my $auto_renew = $auto_renews->next ) {
152 print "examining item '" . $auto_renew->itemnumber . "' to auto renew\n" if $verbose;
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(
158 borrowernumber => $auto_renew->borrowernumber,
159 message_name => 'auto_renewals'
163 if $borrower_preferences
164 && $borrower_preferences->{transports};
167 && $borrower_preferences->{wants_digest};
168 } else { # Preference is never or cron
169 $wants_messages = $send_notices;
173 my ( $success, $error, $updated ) = $auto_renew->attempt_auto_renew( { confirm => $confirm } );
176 say sprintf "Issue id: %s for borrower: %s and item: %s %s be renewed.",
177 $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber,
178 $confirm ? 'will' : 'would';
181 push @item_renewal_ids, $auto_renew->itemnumber;
183 push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew
184 if ($wants_messages) && !$wants_digest;
187 # FIXME Do we need to list every status? Why not simply else?
189 || $error eq 'on_reserve'
190 || $error eq 'restriction'
191 || $error eq 'overdue'
192 || $error eq 'too_unseen'
193 || $error eq 'auto_account_expired'
194 || $error eq 'auto_too_late'
195 || $error eq 'auto_too_much_oweing'
196 || $error eq 'auto_too_soon'
197 || $error eq 'item_denied_renewal'
198 || $error eq 'item_issued_to_other_patron'
202 say sprintf "Issue id: %s for borrower: %s and item: %s %s not be renewed. (%s)",
203 $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber,
204 $confirm ? 'will' : 'would', $error;
207 push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew
208 if $error ne 'auto_too_soon' && ( $wants_messages && !$wants_digest ); # Do not notify if it's too soon
212 if ( $wants_digest ) {
213 # cache this one to process after we've run through all of the items.
214 if ($digest_per_branch) {
215 $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{success}++ if $success;
216 $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{error}++ unless $success || $error eq 'auto_too_soon';
217 $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{results}->{defined $error ? $error : 'auto-renew'}++ ;
218 push @{$renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{issues}}, $auto_renew->itemnumber;
219 $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{updated} = 1 if $updated && (!$error || $error ne 'auto_too_soon');
221 $renew_digest->{ $auto_renew->borrowernumber }->{success} ++ if $success;
222 $renew_digest->{ $auto_renew->borrowernumber }->{error}++ unless $success || $error eq 'auto_too_soon';
223 $renew_digest->{ $auto_renew->borrowernumber }->{results}->{defined $error ? $error : 'auto-renew'}++ ;
224 $renew_digest->{ $auto_renew->borrowernumber }->{updated} = 1 if $updated && (!$error || $error ne 'auto_too_soon');
225 push @{$renew_digest->{ $auto_renew->borrowernumber }->{issues}}, $auto_renew->itemnumber;
231 if( @item_renewal_ids ){
232 my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
233 $indexer->index_records( \@item_renewal_ids, "specialUpdate", "biblioserver" );
236 if ( $send_notices && $confirm ) {
237 for my $borrowernumber ( keys %report ) {
238 my $patron = Koha::Patrons->find($borrowernumber);
239 my $borrower_preferences =
240 C4::Members::Messaging::GetMessagingPreferences(
242 borrowernumber => $borrowernumber,
243 message_name => 'auto_renewals'
246 for my $issue ( @{ $report{$borrowernumber} } ) {
247 my $item = $issue->item;
248 # Force sending of email and only email if pref is set to "cron"
249 my @transports = $send_notices_pref eq 'preferences' ? keys %{ $borrower_preferences->{'transports'} } : 'email';
250 foreach my $transport ( @transports ) {
251 my $letter = C4::Letters::GetPreparedLetter (
252 module => 'circulation',
253 letter_code => 'AUTO_RENEWALS',
255 borrowers => $patron->borrowernumber,
256 issues => $issue->itemnumber,
257 items => $issue->itemnumber,
258 biblio => $item->biblionumber,
260 lang => $patron->lang,
261 message_transport_type => $transport,
265 my $library = $patron->library;
266 my $admin_email_address = $library->from_email_address;
268 C4::Letters::EnqueueLetter(
271 borrowernumber => $borrowernumber,
272 from_address => $admin_email_address,
273 message_transport_type => $transport
281 if ($digest_per_branch) {
282 while (my ($branchcode, $digests) = each %$renew_digest) {
285 branchcode => $branchcode,
286 letter_code => 'AUTO_RENEWALS_DGST',
291 digests => $renew_digest,
292 letter_code => 'AUTO_RENEWALS_DGST',
297 cronlogaction({ action => 'End', info => "COMPLETED" });
308 Enqueue digested letters.
316 Reference to the array of digested messages.
318 =item C<$letter_code>
320 String that denote the letter code.
329 PATRON: while ( my ( $borrowernumber, $digest ) = each %{$params->{digests}} ) {
330 next unless defined $digest->{updated} && $digest->{updated} == 1;
331 my $borrower_preferences =
332 C4::Members::Messaging::GetMessagingPreferences(
334 borrowernumber => $borrowernumber,
335 message_name => 'auto_renewals'
339 next PATRON unless $borrower_preferences; # how could this happen?
341 my $patron = Koha::Patrons->find( $borrowernumber );
343 if ( defined $params->{branchcode} ) {
344 $branchcode = $params->{branchcode};
346 $branchcode = $patron->branchcode;
348 my $library = Koha::Libraries->find( $branchcode );
349 my $from_address = $library->from_email_address;
351 foreach my $transport ( keys %{ $borrower_preferences->{'transports'} } ) {
352 my $letter = C4::Letters::GetPreparedLetter (
353 module => 'circulation',
354 letter_code => $params->{letter_code},
355 branchcode => $branchcode,
356 lang => $patron->lang,
358 error => $digest->{error}||0,
359 success => $digest->{success}||0,
360 results => $digest->{results},
362 loops => { issues => \@{$digest->{issues}} },
364 borrowers => $patron->borrowernumber,
366 message_transport_type => $transport,
370 C4::Letters::EnqueueLetter(
373 borrowernumber => $borrowernumber,
374 from_address => $from_address,
375 message_transport_type => $transport
381 "no letter of type '$params->{letter_code}' found for borrowernumber $borrowernumber. Please see sample_notices.sql";