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 ( $help, $send_notices, $verbose, $confirm, $digest_per_branch );
93 's|send-notices' => \$send_notices,
94 'v|verbose' => \$verbose,
95 'c|confirm' => \$confirm,
96 'b|digest-per-branch' => \$digest_per_branch,
99 pod2usage(0) if $help;
101 my $send_notices_pref = C4::Context->preference('AutoRenewalNotices');
102 if ( $send_notices_pref eq 'cron' ) {
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'
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;
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') ) {
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.
131 $verbose = 1 unless $verbose or $confirm;
132 print "Test run only\n" unless $confirm;
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;
138 my $renew_digest = {};
140 while ( my $auto_renew = $auto_renews->next ) {
141 print "examining item '" . $auto_renew->itemnumber . "' to auto renew\n" if $verbose;
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';
147 $send_notices = 1 if !$send_notices && $send_notices_pref eq 'preferences' && $borrower_preferences && $borrower_preferences->{transports} && $borrower_preferences->{transports}->{email};
149 # CanBookBeRenewed returns 'auto_renew' when the renewal should be done by this script
150 my ( $ok, $error ) = CanBookBeRenewed( $auto_renew->borrowernumber, $auto_renew->itemnumber, undef, 1 );
151 if ( $error eq 'auto_renew' ) {
153 say sprintf "Issue id: %s for borrower: %s and item: %s %s be renewed.",
154 $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber, $confirm ? 'will' : 'would';
157 my $date_due = AddRenewal( $auto_renew->borrowernumber, $auto_renew->itemnumber, $auto_renew->branchcode, undef, undef, undef, 0 );
158 $auto_renew->auto_renew_error(undef)->store;
160 push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew unless $send_notices_pref ne 'cron' && (!$borrower_preferences || !$borrower_preferences->{transports} || !$borrower_preferences->{transports}->{email} || $borrower_preferences->{'wants_digest'});
161 } elsif ( $error eq 'too_many'
162 or $error eq 'on_reserve'
163 or $error eq 'restriction'
164 or $error eq 'overdue'
165 or $error eq 'too_unseen'
166 or $error eq 'auto_account_expired'
167 or $error eq 'auto_too_late'
168 or $error eq 'auto_too_much_oweing'
169 or $error eq 'auto_too_soon'
170 or $error eq 'item_denied_renewal' ) {
172 say sprintf "Issue id: %s for borrower: %s and item: %s %s not be renewed. (%s)",
173 $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber, $confirm ? 'will' : 'would', $error;
175 if ( not $auto_renew->auto_renew_error or $error ne $auto_renew->auto_renew_error ) {
176 $auto_renew->auto_renew_error($error)->store if $confirm;
177 push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew
178 if $error ne 'auto_too_soon' && ($send_notices_pref eq 'cron' || ($borrower_preferences && $borrower_preferences->{transports} && $borrower_preferences->{transports}->{email} && !$borrower_preferences->{'wants_digest'})); # Do not notify if it's too soon
182 if ( $borrower_preferences && $borrower_preferences->{transports} && $borrower_preferences->{transports}->{email} && $borrower_preferences->{'wants_digest'} ) {
183 # cache this one to process after we've run through all of the items.
184 if ($digest_per_branch) {
185 $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{success}++ if $error eq 'auto_renew';
186 $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error eq 'auto_too_soon' ;
187 push @{$renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{issues}}, $auto_renew->itemnumber;
189 $renew_digest->{ $auto_renew->borrowernumber }->{success} ++ if $error eq 'auto_renew';
190 $renew_digest->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error eq 'auto_too_soon' ;
191 push @{$renew_digest->{ $auto_renew->borrowernumber }->{issues}}, $auto_renew->itemnumber;
197 if ( $send_notices && $confirm ) {
198 for my $borrowernumber ( keys %report ) {
199 my $patron = Koha::Patrons->find($borrowernumber);
200 for my $issue ( @{ $report{$borrowernumber} } ) {
201 my $item = Koha::Items->find( $issue->itemnumber );
202 my $letter = C4::Letters::GetPreparedLetter(
203 module => 'circulation',
204 letter_code => 'AUTO_RENEWALS',
206 borrowers => $patron->borrowernumber,
207 issues => $issue->itemnumber,
208 items => $issue->itemnumber,
209 biblio => $item->biblionumber,
211 lang => $patron->lang,
214 my $library = Koha::Libraries->find( $patron->branchcode );
215 my $admin_email_address = $library->from_email_address;
217 C4::Letters::EnqueueLetter(
219 borrowernumber => $borrowernumber,
220 message_transport_type => 'email',
221 from_address => $admin_email_address,
227 if ($digest_per_branch) {
228 while (my ($branchcode, $digests) = each %$renew_digest) {
231 branchcode => $branchcode,
232 letter_code => 'AUTO_RENEWALS_DGST',
237 digests => $renew_digest,
238 letter_code => 'AUTO_RENEWALS_DGST',
252 Enqueue digested letters.
260 Reference to the array of digested messages.
262 =item C<$letter_code>
264 String that denote the letter code.
273 PATRON: while ( my ( $borrowernumber, $digest ) = each %{$params->{digests}} ) {
274 my $borrower_preferences =
275 C4::Members::Messaging::GetMessagingPreferences(
277 borrowernumber => $borrowernumber,
278 message_name => 'auto_renewals'
282 next PATRON unless $borrower_preferences; # how could this happen?
284 my $patron = Koha::Patrons->find( $borrowernumber );
285 my $library = Koha::Libraries->find( $params->{branchcode} );
286 my $from_address = $library->from_email_address;
288 foreach my $transport ( keys %{ $borrower_preferences->{'transports'} } ) {
289 my $letter = C4::Letters::GetPreparedLetter (
290 module => 'circulation',
291 letter_code => $params->{letter_code},
292 branchcode => $params->{branchcode},
293 lang => $patron->lang,
295 error => $digest->{error}||0,
296 success => $digest->{success}||0,
298 loops => { issues => \@{$digest->{issues}} },
300 borrowers => $patron->borrowernumber,
302 message_transport_type => $transport,
303 ) || warn "no letter of type '$params->{letter_code}' found for borrowernumber $borrowernumber. Please see sample_notices.sql";
307 C4::Letters::EnqueueLetter({
309 borrowernumber => $borrowernumber,
310 from_address => $from_address,
311 message_transport_type => $transport