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;
82 use C4::Circulation qw( CanBookBeRenewed AddRenewal );
84 use C4::Log qw( cronlogaction );
90 my $command_line_options = join(" ",@ARGV);
92 my ( $help, $send_notices, $verbose, $confirm, $digest_per_branch );
95 's|send-notices' => \$send_notices,
96 'v|verbose' => \$verbose,
97 'c|confirm' => \$confirm,
98 'b|digest-per-branch' => \$digest_per_branch,
101 pod2usage(0) if $help;
103 my $send_notices_pref = C4::Context->preference('AutoRenewalNotices');
104 if ( $send_notices_pref eq 'cron' ) {
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'
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;
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') ) {
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.
132 cronlogaction({ info => $command_line_options });
134 $verbose = 1 unless $verbose or $confirm;
135 print "Test run only\n" unless $confirm;
137 print "getting auto renewals\n" if $verbose;
138 my $auto_renews = Koha::Checkouts->search(
141 'patron.autorenew_checkouts' => 1,
144 join => ['patron','item']
147 print "found " . $auto_renews->count . " auto renewals\n" if $verbose;
149 my $renew_digest = {};
151 my @item_renewal_ids;
152 while ( my $auto_renew = $auto_renews->next ) {
153 print "examining item '" . $auto_renew->itemnumber . "' to auto renew\n" if $verbose;
155 my ( $borrower_preferences, $wants_messages, $wants_digest ) = ( undef, 0, 0 );
156 if ( $send_notices_pref eq 'preferences' ){
157 $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences(
159 borrowernumber => $auto_renew->borrowernumber,
160 message_name => 'auto_renewals'
164 if $borrower_preferences
165 && $borrower_preferences->{transports};
168 && $borrower_preferences->{wants_digest};
169 } else { # Preference is never or cron
170 $wants_messages = $send_notices;
174 # CanBookBeRenewed returns 'auto_renew' when the renewal should be done by this script
175 my ( $ok, $error ) = CanBookBeRenewed( $auto_renew->patron, $auto_renew, undef, 1 );
177 if ( $error eq 'auto_renew' ) {
180 say sprintf "Issue id: %s for borrower: %s and item: %s %s be renewed.",
181 $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber,
182 $confirm ? 'will' : 'would';
185 my $date_due = AddRenewal(
187 borrowernumber => $auto_renew->borrowernumber,
188 itemnumber => $auto_renew->itemnumber,
189 branch => $auto_renew->branchcode,
194 push @item_renewal_ids, $auto_renew->itemnumber;
195 $auto_renew->auto_renew_error(undef)->store;
197 push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew
198 if ($wants_messages) && !$wants_digest;
199 } elsif ( $error eq 'too_many'
200 || $error eq 'on_reserve'
201 || $error eq 'restriction'
202 || $error eq 'overdue'
203 || $error eq 'too_unseen'
204 || $error eq 'auto_account_expired'
205 || $error eq 'auto_too_late'
206 || $error eq 'auto_too_much_oweing'
207 || $error eq 'auto_too_soon'
208 || $error eq 'too_soon'
209 || $error eq 'item_denied_renewal'
210 || $error eq 'item_issued_to_other_patron' )
213 say sprintf "Issue id: %s for borrower: %s and item: %s %s not be renewed. (%s)",
214 $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber,
215 $confirm ? 'will' : 'would', $error;
217 $updated = 1 if ( !$auto_renew->auto_renew_error || $error ne $auto_renew->auto_renew_error );
219 $auto_renew->auto_renew_error($error)->store if $confirm;
220 push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew
221 if $error ne 'auto_too_soon' && ( $wants_messages && !$wants_digest ); # Do not notify if it's too soon
225 if ( $wants_digest ) {
226 # cache this one to process after we've run through all of the items.
227 if ($digest_per_branch) {
228 $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{success}++ if $error eq 'auto_renew';
229 $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error eq 'auto_too_soon';
230 $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{results}->{$error}++ ;
231 push @{$renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{issues}}, $auto_renew->itemnumber;
232 $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{updated} = 1 if $updated && $error ne 'auto_too_soon';
234 $renew_digest->{ $auto_renew->borrowernumber }->{success} ++ if $error eq 'auto_renew';
235 $renew_digest->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error eq 'auto_too_soon';
236 $renew_digest->{ $auto_renew->borrowernumber }->{results}->{$error}++ ;
237 $renew_digest->{ $auto_renew->borrowernumber }->{updated} = 1 if $updated && $error ne 'auto_too_soon';
238 push @{$renew_digest->{ $auto_renew->borrowernumber }->{issues}}, $auto_renew->itemnumber;
244 if( @item_renewal_ids ){
245 my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
246 $indexer->index_records( \@item_renewal_ids, "specialUpdate", "biblioserver" );
249 if ( $send_notices && $confirm ) {
250 for my $borrowernumber ( keys %report ) {
251 my $patron = Koha::Patrons->find($borrowernumber);
252 my $borrower_preferences =
253 C4::Members::Messaging::GetMessagingPreferences(
255 borrowernumber => $borrowernumber,
256 message_name => 'auto_renewals'
259 for my $issue ( @{ $report{$borrowernumber} } ) {
260 my $item = $issue->item;
261 # Force sending of email and only email if pref is set to "cron"
262 my @transports = $send_notices_pref eq 'preferences' ? keys %{ $borrower_preferences->{'transports'} } : 'email';
263 foreach my $transport ( @transports ) {
264 my $letter = C4::Letters::GetPreparedLetter (
265 module => 'circulation',
266 letter_code => 'AUTO_RENEWALS',
268 borrowers => $patron->borrowernumber,
269 issues => $issue->itemnumber,
270 items => $issue->itemnumber,
271 biblio => $item->biblionumber,
273 lang => $patron->lang,
274 message_transport_type => $transport,
278 my $library = $patron->library;
279 my $admin_email_address = $library->from_email_address;
281 C4::Letters::EnqueueLetter(
284 borrowernumber => $borrowernumber,
285 from_address => $admin_email_address,
286 message_transport_type => $transport
294 if ($digest_per_branch) {
295 while (my ($branchcode, $digests) = each %$renew_digest) {
298 branchcode => $branchcode,
299 letter_code => 'AUTO_RENEWALS_DGST',
304 digests => $renew_digest,
305 letter_code => 'AUTO_RENEWALS_DGST',
310 cronlogaction({ action => 'End', info => "COMPLETED" });
321 Enqueue digested letters.
329 Reference to the array of digested messages.
331 =item C<$letter_code>
333 String that denote the letter code.
342 PATRON: while ( my ( $borrowernumber, $digest ) = each %{$params->{digests}} ) {
343 next unless defined $digest->{updated} && $digest->{updated} == 1;
344 my $borrower_preferences =
345 C4::Members::Messaging::GetMessagingPreferences(
347 borrowernumber => $borrowernumber,
348 message_name => 'auto_renewals'
352 next PATRON unless $borrower_preferences; # how could this happen?
354 my $patron = Koha::Patrons->find( $borrowernumber );
356 if ( defined $params->{branchcode} ) {
357 $branchcode = $params->{branchcode};
359 $branchcode = $patron->branchcode;
361 my $library = Koha::Libraries->find( $branchcode );
362 my $from_address = $library->from_email_address;
364 foreach my $transport ( keys %{ $borrower_preferences->{'transports'} } ) {
365 my $letter = C4::Letters::GetPreparedLetter (
366 module => 'circulation',
367 letter_code => $params->{letter_code},
368 branchcode => $branchcode,
369 lang => $patron->lang,
371 error => $digest->{error}||0,
372 success => $digest->{success}||0,
373 results => $digest->{results},
375 loops => { issues => \@{$digest->{issues}} },
377 borrowers => $patron->borrowernumber,
379 message_transport_type => $transport,
383 C4::Letters::EnqueueLetter(
386 borrowernumber => $borrowernumber,
387 from_address => $from_address,
388 message_transport_type => $transport
394 "no letter of type '$params->{letter_code}' found for borrowernumber $borrowernumber. Please see sample_notices.sql";