View | Details | Raw Unified | Return to bug 29507
Collapse All | Expand All

(-)a/debian/templates/koha-conf-site.xml.in (+4 lines)
Lines 457-462 __END_SRU_PUBLICSERVER__ Link Here
457
   <vhost>__MESSAGE_BROKER_VHOST__</vhost>
457
   <vhost>__MESSAGE_BROKER_VHOST__</vhost>
458
 </message_broker>
458
 </message_broker>
459
459
460
 <auto_renew_cronjob>
461
   <parallel_loops_count>1</parallel_loops_count>
462
 </auto_renew_cronjob>
463
460
 <background_jobs_worker>
464
 <background_jobs_worker>
461
     <!-- Max simultaneous processes per worker -->
465
     <!-- Max simultaneous processes per worker -->
462
     <max_processes>1</max_processes>
466
     <max_processes>1</max_processes>
(-)a/etc/koha-conf.xml (+4 lines)
Lines 274-279 Link Here
274
   <vhost></vhost>
274
   <vhost></vhost>
275
 </message_broker>
275
 </message_broker>
276
276
277
 <auto_renew_cronjob>
278
   <parallel_loops_count>1</parallel_loops_count>
279
 </auto_renew_cronjob>
280
277
 <background_jobs_worker>
281
 <background_jobs_worker>
278
     <!-- Max simultaneous processes per worker -->
282
     <!-- Max simultaneous processes per worker -->
279
     <max_processes>1</max_processes>
283
     <max_processes>1</max_processes>
(-)a/misc/cronjobs/automatic_renewals.pl (-129 / +172 lines)
Lines 76-81 chosen 'Digests only' on the advance messages. Link Here
76
76
77
use Modern::Perl;
77
use Modern::Perl;
78
use Pod::Usage qw( pod2usage );
78
use Pod::Usage qw( pod2usage );
79
use Parallel::ForkManager;
79
use Getopt::Long qw( GetOptions );
80
use Getopt::Long qw( GetOptions );
80
81
81
use Koha::Script -cron;
82
use Koha::Script -cron;
Lines 135-141 $verbose = 1 unless $verbose or $confirm; Link Here
135
print "Test run only\n" unless $confirm;
136
print "Test run only\n" unless $confirm;
136
137
137
print "getting auto renewals\n" if $verbose;
138
print "getting auto renewals\n" if $verbose;
138
my $auto_renews = Koha::Checkouts->search(
139
my @auto_renews = Koha::Checkouts->search(
139
    {
140
    {
140
        auto_renew                   => 1,
141
        auto_renew                   => 1,
141
        'patron.autorenew_checkouts' => 1,
142
        'patron.autorenew_checkouts' => 1,
Lines 143-302 my $auto_renews = Koha::Checkouts->search( Link Here
143
    {
144
    {
144
        join => ['patron','item']
145
        join => ['patron','item']
145
    }
146
    }
146
);
147
)->as_list;
147
print "found " . $auto_renews->count . " auto renewals\n" if $verbose;
148
print "found " . scalar @auto_renews . " auto renewals\n" if $verbose;
148
149
149
my $renew_digest = {};
150
my $renew_digest = {};
150
my %report;
151
my %report;
151
my @item_renewal_ids;
152
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;
154
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(
158
            {
159
                borrowernumber => $auto_renew->borrowernumber,
160
                message_name   => 'auto_renewals'
161
            }
162
        );
163
        $wants_messages = 1
164
            if $borrower_preferences
165
            && $borrower_preferences->{transports};
166
        $wants_digest = 1
167
            if $wants_messages
168
            && $borrower_preferences->{wants_digest};
169
    } else { # Preference is never or cron
170
        $wants_messages = $send_notices;
171
        $wants_digest = 0;
172
    }
173
153
174
    # CanBookBeRenewed returns 'auto_renew' when the renewal should be done by this script
154
my $cron_options = C4::Context->config('auto_renew_cronjob');
175
    my ( $ok, $error ) = CanBookBeRenewed( $auto_renew->borrowernumber, $auto_renew->itemnumber, undef, 1 );
155
my $loops = $cron_options ? $cron_options->{parallel_loops_count} // 1 : 1;
176
    my $updated;
156
177
    if ( $error eq 'auto_renew' ) {
157
# Split the list of issues into chunks to run in parallel
178
        $updated = 1;
158
my @chunks;
179
        if ($verbose) {
159
if ( $loops > 1 ) {
180
            say sprintf "Issue id: %s for borrower: %s and item: %s %s be renewed.",
160
    my $i              = 0;
181
              $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber, $confirm ? 'will' : 'would';
161
    my $borrowernumber = 0;
182
        }
162
    while (@auto_renews) {
183
        if ($confirm){
163
        my $auto_renew = pop(@auto_renews);
184
            my $date_due = AddRenewal( $auto_renew->borrowernumber, $auto_renew->itemnumber, $auto_renew->branchcode, undef, undef, undef, 0, 1 );
164
        if ( $borrowernumber != $auto_renew->borrowernumber ) {
185
            push @item_renewal_ids, $auto_renew->itemnumber;
165
            $i++ if $borrowernumber;
186
            $auto_renew->auto_renew_error(undef)->store;
166
            $borrowernumber = $auto_renew->borrowernumber;
187
        }
188
        push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew
189
            if ( $wants_messages ) && !$wants_digest;
190
    } elsif ( $error eq 'too_many'
191
        or $error eq 'on_reserve'
192
        or $error eq 'restriction'
193
        or $error eq 'overdue'
194
        or $error eq 'too_unseen'
195
        or $error eq 'auto_account_expired'
196
        or $error eq 'auto_too_late'
197
        or $error eq 'auto_too_much_oweing'
198
        or $error eq 'auto_too_soon'
199
        or $error eq 'item_denied_renewal' ) {
200
        if ( $verbose ) {
201
            say sprintf "Issue id: %s for borrower: %s and item: %s %s not be renewed. (%s)",
202
              $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber, $confirm ? 'will' : 'would', $error;
203
        }
204
        $updated = 1 if (!$auto_renew->auto_renew_error || $error ne $auto_renew->auto_renew_error);
205
        if ( $updated ) {
206
            $auto_renew->auto_renew_error($error)->store if $confirm;
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
209
        }
167
        }
168
        $i = 0 if $i >= $loops;
169
        push( @{ $chunks[$i] }, $auto_renew );
210
    }
170
    }
211
171
    my $pm = Parallel::ForkManager->new($loops);
212
    if ( $wants_digest ) {
172
  DATA_LOOP:
213
        # cache this one to process after we've run through all of the items.
173
    foreach my $chunk (@chunks) {
214
        if ($digest_per_branch) {
174
        my $pid = $pm->start and next DATA_LOOP;
215
            $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{success}++ if $error eq 'auto_renew';
175
        _ProcessRenewals($chunk);
216
            $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error eq 'auto_too_soon';
176
        $pm->finish;
217
            $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{results}->{$error}++ ;
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 ne 'auto_too_soon';
220
        } else {
221
            $renew_digest->{ $auto_renew->borrowernumber }->{success} ++ if $error eq 'auto_renew';
222
            $renew_digest->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error eq 'auto_too_soon';
223
            $renew_digest->{ $auto_renew->borrowernumber }->{results}->{$error}++ ;
224
            $renew_digest->{ $auto_renew->borrowernumber }->{updated} = 1 if $updated && $error ne 'auto_too_soon';
225
            push @{$renew_digest->{ $auto_renew->borrowernumber }->{issues}}, $auto_renew->itemnumber;
226
        }
227
    }
177
    }
228
178
    $pm->wait_all_children;
229
}
179
}
230
180
else {
231
if( @item_renewal_ids ){
181
    _ProcessRenewals( \@auto_renews );
232
    my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
233
    $indexer->index_records( \@item_renewal_ids, "specialUpdate", "biblioserver" );
234
}
182
}
235
183
236
if ( $send_notices && $confirm ) {
184
cronlogaction({ action => 'End', info => "COMPLETED" });
237
    for my $borrowernumber ( keys %report ) {
185
238
        my $patron = Koha::Patrons->find($borrowernumber);
186
=head1 METHODS
239
        my $borrower_preferences =
187
240
            C4::Members::Messaging::GetMessagingPreferences(
188
=head2 _ProcessRenewals
189
190
    Internal method to process the queue in chunks
191
192
=cut
193
194
sub _ProcessRenewals {
195
196
    my $auto_renew_issues = shift;
197
198
    foreach my $auto_renew (@$auto_renew_issues) {
199
        print "examining item '" . $auto_renew->itemnumber . "' to auto renew\n" if $verbose;
200
201
        my ( $borrower_preferences, $wants_messages, $wants_digest ) = ( undef, 0, 0 );
202
        if ( $send_notices_pref eq 'preferences' ){
203
            $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences(
241
                {
204
                {
242
                    borrowernumber => $borrowernumber,
205
                    borrowernumber => $auto_renew->borrowernumber,
243
                    message_name   => 'auto_renewals'
206
                    message_name   => 'auto_renewals'
244
                }
207
                }
245
            );
208
            );
246
        for my $issue ( @{ $report{$borrowernumber} } ) {
209
            $wants_messages = 1
247
            my $item = $issue->item;
210
                if $borrower_preferences
248
            # Force sending of email and only email if pref is set to "cron"
211
                && $borrower_preferences->{transports};
249
            my @transports = $send_notices_pref eq 'preferences' ? keys %{ $borrower_preferences->{'transports'} } : 'email';
212
            $wants_digest = 1
250
            foreach my $transport ( @transports ) {
213
                if $wants_messages
251
                my $letter = C4::Letters::GetPreparedLetter (
214
                && $borrower_preferences->{wants_digest};
252
                    module      => 'circulation',
215
        } else { # Preference is never or cron
253
                    letter_code => 'AUTO_RENEWALS',
216
            $wants_messages = $send_notices;
254
                    tables      => {
217
            $wants_digest = 0;
255
                        borrowers => $patron->borrowernumber,
218
        }
256
                        issues    => $issue->itemnumber,
257
                        items     => $issue->itemnumber,
258
                        biblio    => $item->biblionumber,
259
                    },
260
                    lang => $patron->lang,
261
                    message_transport_type => $transport,
262
                );
263
219
264
                if ($letter) {
220
        # CanBookBeRenewed returns 'auto_renew' when the renewal should be done by this script
265
                    my $library = $patron->library;
221
        my ( $ok, $error ) = CanBookBeRenewed( $auto_renew->borrowernumber, $auto_renew->itemnumber, undef, 1 );
266
                    my $admin_email_address = $library->from_email_address;
222
        my $updated;
267
223
        if ( $error eq 'auto_renew' ) {
268
                    C4::Letters::EnqueueLetter(
224
            $updated = 1;
269
                        {
225
            if ($verbose) {
270
                            letter                 => $letter,
226
                say sprintf "Issue id: %s for borrower: %s and item: %s %s be renewed.",
271
                            borrowernumber         => $borrowernumber,
227
                  $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber, $confirm ? 'will' : 'would';
272
                            from_address           => $admin_email_address,
228
            }
273
                            message_transport_type => $transport
229
            if ($confirm){
274
                        }
230
                my $date_due = AddRenewal( $auto_renew->borrowernumber, $auto_renew->itemnumber, $auto_renew->branchcode, undef, undef, undef, 0, 1 );
231
                push @item_renewal_ids, $auto_renew->itemnumber;
232
                $auto_renew->auto_renew_error(undef)->store;
233
            }
234
            push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew
235
                if ( $wants_messages ) && !$wants_digest;
236
        } elsif ( $error eq 'too_many'
237
            or $error eq 'on_reserve'
238
            or $error eq 'restriction'
239
            or $error eq 'overdue'
240
            or $error eq 'too_unseen'
241
            or $error eq 'auto_account_expired'
242
            or $error eq 'auto_too_late'
243
            or $error eq 'auto_too_much_oweing'
244
            or $error eq 'auto_too_soon'
245
            or $error eq 'item_denied_renewal' ) {
246
            if ( $verbose ) {
247
                say sprintf "Issue id: %s for borrower: %s and item: %s %s not be renewed. (%s)",
248
                  $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber, $confirm ? 'will' : 'would', $error;
249
            }
250
            $updated = 1 if (!$auto_renew->auto_renew_error || $error ne $auto_renew->auto_renew_error);
251
            if ( $updated ) {
252
                $auto_renew->auto_renew_error($error)->store if $confirm;
253
                push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew
254
                  if $error ne 'auto_too_soon' && ( $wants_messages  && !$wants_digest );    # Do not notify if it's too soon
255
            }
256
        }
257
258
        if ( $wants_digest ) {
259
            # cache this one to process after we've run through all of the items.
260
            if ($digest_per_branch) {
261
                $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{success}++ if $error eq 'auto_renew';
262
                $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error eq 'auto_too_soon';
263
                $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{results}->{$error}++ ;
264
                push @{$renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{issues}}, $auto_renew->itemnumber;
265
                $renew_digest->{ $auto_renew->branchcode }->{ $auto_renew->borrowernumber }->{updated} = 1 if $updated && $error ne 'auto_too_soon';
266
            } else {
267
                $renew_digest->{ $auto_renew->borrowernumber }->{success} ++ if $error eq 'auto_renew';
268
                $renew_digest->{ $auto_renew->borrowernumber }->{error}++ unless $error eq 'auto_renew' || $error eq 'auto_too_soon';
269
                $renew_digest->{ $auto_renew->borrowernumber }->{results}->{$error}++ ;
270
                $renew_digest->{ $auto_renew->borrowernumber }->{updated} = 1 if $updated && $error ne 'auto_too_soon';
271
                push @{$renew_digest->{ $auto_renew->borrowernumber }->{issues}}, $auto_renew->itemnumber;
272
            }
273
        }
274
275
    }
276
277
    if( @item_renewal_ids ){
278
        my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
279
        $indexer->index_records( \@item_renewal_ids, "specialUpdate", "biblioserver" );
280
    }
281
282
    if ( $send_notices && $confirm ) {
283
        for my $borrowernumber ( keys %report ) {
284
            my $patron = Koha::Patrons->find($borrowernumber);
285
            my $borrower_preferences =
286
                C4::Members::Messaging::GetMessagingPreferences(
287
                    {
288
                        borrowernumber => $borrowernumber,
289
                        message_name   => 'auto_renewals'
290
                    }
291
                );
292
            for my $issue ( @{ $report{$borrowernumber} } ) {
293
                my $item = $issue->item;
294
                # Force sending of email and only email if pref is set to "cron"
295
                my @transports = $send_notices_pref eq 'preferences' ? keys %{ $borrower_preferences->{'transports'} } : 'email';
296
                foreach my $transport ( @transports ) {
297
                    my $letter = C4::Letters::GetPreparedLetter (
298
                        module      => 'circulation',
299
                        letter_code => 'AUTO_RENEWALS',
300
                        tables      => {
301
                            borrowers => $patron->borrowernumber,
302
                            issues    => $issue->itemnumber,
303
                            items     => $issue->itemnumber,
304
                            biblio    => $item->biblionumber,
305
                        },
306
                        lang => $patron->lang,
307
                        message_transport_type => $transport,
275
                    );
308
                    );
309
310
                    if ($letter) {
311
                        my $library = $patron->library;
312
                        my $admin_email_address = $library->from_email_address;
313
314
                        C4::Letters::EnqueueLetter(
315
                            {
316
                                letter                 => $letter,
317
                                borrowernumber         => $borrowernumber,
318
                                from_address           => $admin_email_address,
319
                                message_transport_type => $transport
320
                            }
321
                        );
322
                    }
276
                }
323
                }
277
            }
324
            }
278
        }
325
        }
279
    }
280
326
281
    if ($digest_per_branch) {
327
        if ($digest_per_branch) {
282
        while (my ($branchcode, $digests) = each %$renew_digest) {
328
            while (my ($branchcode, $digests) = each %$renew_digest) {
329
                send_digests({
330
                    digests => $digests,
331
                    branchcode => $branchcode,
332
                    letter_code => 'AUTO_RENEWALS_DGST',
333
                });
334
            }
335
        } else {
283
            send_digests({
336
            send_digests({
284
                digests => $digests,
337
                digests => $renew_digest,
285
                branchcode => $branchcode,
286
                letter_code => 'AUTO_RENEWALS_DGST',
338
                letter_code => 'AUTO_RENEWALS_DGST',
287
            });
339
            });
288
        }
340
        }
289
    } else {
290
        send_digests({
291
            digests => $renew_digest,
292
            letter_code => 'AUTO_RENEWALS_DGST',
293
        });
294
    }
341
    }
295
}
296
342
297
cronlogaction({ action => 'End', info => "COMPLETED" });
343
}
298
299
=head1 METHODS
300
344
301
=head2 send_digests
345
=head2 send_digests
302
346
303
- 

Return to bug 29507