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

(-)a/misc/cronjobs/stockrotation.pl (-158 / +195 lines)
Lines 109-165 database updates have been performed."). Link Here
109
109
110
use Modern::Perl;
110
use Modern::Perl;
111
use Getopt::Long qw/HelpMessage :config gnu_getopt/;
111
use Getopt::Long qw/HelpMessage :config gnu_getopt/;
112
use Mail::Sendmail;
113
use C4::Context;
112
use C4::Context;
114
use Koha::Email;
115
use Koha::StockRotationRotas;
113
use Koha::StockRotationRotas;
116
114
117
my $admin_email = '';
115
my $admin_email = '';
118
my $branch = 0;
116
my $branch      = 0;
119
my $execute = 0;
117
my $execute     = 0;
120
my $report = 'full';
118
my $report      = 'full';
121
my $send_all = 0;
119
my $send_all    = 0;
122
my $send_email = 0;
120
my $send_email  = 0;
123
121
124
my $ok = GetOptions(
122
my $ok = GetOptions(
125
    'admin-email|a=s' => \$admin_email,
123
    'admin-email|a=s' => \$admin_email,
126
    'branchcode|b=s' => sub {
124
    'branchcode|b=s'  => sub {
127
        my ($opt_name, $opt_value) = @_;
125
        my ( $opt_name, $opt_value ) = @_;
128
        my $branches = Koha::Libraries->search(
126
        my $branches = Koha::Libraries->search( {},
129
            {}, { order_by => { -asc => 'branchname'} }
127
            { order_by => { -asc => 'branchname' } } );
130
        );
131
        my $brnch = $branches->find($opt_value);
128
        my $brnch = $branches->find($opt_value);
132
        if ( $brnch ) {
129
        if ($brnch) {
133
            $branch = $brnch;
130
            $branch = $brnch;
134
            return $brnch;
131
            return $brnch;
135
        } else {
132
        }
133
        else {
136
            printf("Option $opt_name should be one of (name -> code):\n");
134
            printf("Option $opt_name should be one of (name -> code):\n");
137
            while ( my $candidate = $branches->next ) {
135
            while ( my $candidate = $branches->next ) {
138
                printf(
136
                printf( "  %-40s  ->  %s\n",
139
                    "  %-40s  ->  %s\n", $candidate->branchname,
137
                    $candidate->branchname, $candidate->branchcode );
140
                    $candidate->branchcode
141
                );
142
            }
138
            }
143
            exit 1
139
            exit 1;
144
        }
140
        }
145
    },
141
    },
146
    'execute|x' => \$execute,
142
    'execute|x'  => \$execute,
147
    'report|r=s' => sub {
143
    'report|r=s' => sub {
148
        my ($opt_name, $opt_value) = @_;
144
        my ( $opt_name, $opt_value ) = @_;
149
        if ( $opt_value eq 'full' || $opt_value eq 'email' ) {
145
        if ( $opt_value eq 'full' || $opt_value eq 'email' ) {
150
            $report = $opt_value;
146
            $report = $opt_value;
151
        } else {
147
        }
148
        else {
152
            printf("Option $opt_name should be either 'email' or 'full'.\n");
149
            printf("Option $opt_name should be either 'email' or 'full'.\n");
153
            exit 1;
150
            exit 1;
154
        }
151
        }
155
    },
152
    },
156
    'send-all|S' => \$send_all,
153
    'send-all|S'   => \$send_all,
157
    'send-email|s' => \$send_email,
154
    'send-email|s' => \$send_email,
158
    'help|h|?' => sub { HelpMessage }
155
    'help|h|?'     => sub { HelpMessage }
159
);
156
);
160
exit 1 unless ( $ok );
157
exit 1 unless ($ok);
161
158
162
$send_email++ if ( $send_all ); # if we send all, then we must want emails.
159
$send_email++ if ($send_all);    # if we send all, then we must want emails.
163
160
164
=head2 Helpers
161
=head2 Helpers
165
162
Lines 177-195 This procedure WILL mess with your database. Link Here
177
=cut
174
=cut
178
175
179
sub execute {
176
sub execute {
180
    my ( $data ) = @_;
177
    my ($data) = @_;
181
178
182
    # Begin transaction
179
    # Begin transaction
183
    my $schema = Koha::Database->new->schema;
180
    my $schema = Koha::Database->new->schema;
184
    $schema->storage->txn_begin;
181
    $schema->storage->txn_begin;
185
182
186
    # Carry out db updates
183
    # Carry out db updates
187
    foreach my $item (@{$data->{items}}) {
184
    foreach my $item ( @{ $data->{items} } ) {
188
        my $reason = $item->{reason};
185
        my $reason = $item->{reason};
189
        if ( $reason eq 'repatriation' ) {
186
        if ( $reason eq 'repatriation' ) {
190
            $item->{object}->repatriate;
187
            $item->{object}->repatriate;
191
        } elsif ( grep { $reason eq $_ }
188
        }
192
                      qw/in-demand advancement initiation/ ) {
189
        elsif ( grep { $reason eq $_ } qw/in-demand advancement initiation/ ) {
193
            $item->{object}->advance;
190
            $item->{object}->advance;
194
        }
191
        }
195
    }
192
    }
Lines 212-225 No data in the database is manipulated by this procedure. Link Here
212
=cut
209
=cut
213
210
214
sub report_full {
211
sub report_full {
215
    my ( $data ) = @_;
212
    my ($data) = @_;
216
213
217
    my $header = "";
214
    my $header = "";
218
    my $body = "";
215
    my $body   = "";
216
219
    # Summary
217
    # Summary
220
    $header .= sprintf "
218
    $header .= sprintf "
221
STOCKROTATION REPORT
219
STOCKROTATION REPORT
222
--------------------
220
--------------------\n";
221
    $body .= sprintf "
223
  Total number of rotas:         %5u
222
  Total number of rotas:         %5u
224
    Inactive rotas:              %5u
223
    Inactive rotas:              %5u
225
    Active rotas:                %5u
224
    Active rotas:                %5u
Lines 231-266 STOCKROTATION REPORT Link Here
231
  Total items to be repatriated: %5u
230
  Total items to be repatriated: %5u
232
  Total items to be advanced:    %5u
231
  Total items to be advanced:    %5u
233
  Total items in demand:         %5u\n\n",
232
  Total items in demand:         %5u\n\n",
234
      $data->{sum_rotas}, $data->{rotas_inactive}, $data->{rotas_active},
233
      $data->{sum_rotas},  $data->{rotas_inactive}, $data->{rotas_active},
235
      $data->{sum_items}, $data->{items_inactive}, $data->{stationary},
234
      $data->{sum_items},  $data->{items_inactive}, $data->{stationary},
236
      $data->{actionable}, $data->{initiable}, $data->{repatriable},
235
      $data->{actionable}, $data->{initiable},      $data->{repatriable},
237
      $data->{advanceable}, $data->{indemand};
236
      $data->{advanceable}, $data->{indemand};
238
237
239
    if (@{$data->{rotas}}) {    # Per Rota details
238
    if ( @{ $data->{rotas} } ) {    # Per Rota details
240
        $body .= sprintf "ROTAS DETAIL\n------------\n\n";
239
        $body .= sprintf "ROTAS DETAIL\n------------\n\n";
241
        foreach my $rota (@{$data->{rotas}}) {
240
        foreach my $rota ( @{ $data->{rotas} } ) {
242
            $body .= sprintf "Details for %s [%s]:\n",
241
            $body .= sprintf "Details for %s [%s]:\n",
243
                $rota->{name}, $rota->{id};
242
              $rota->{name}, $rota->{id};
244
            $body .= sprintf "\n  Items:"; # Rota item details
243
            $body .= sprintf "\n  Items:";    # Rota item details
245
            if (@{$rota->{items}}) {
244
            if ( @{ $rota->{items} } ) {
246
                $body .= join("", map { _print_item($_) } @{$rota->{items}});
245
                $body .=
247
            } else {
246
                  join( "", map { _print_item($_) } @{ $rota->{items} } );
248
                $body .= sprintf
249
                    "\n    No items to be processed for this rota.\n";
250
            }
247
            }
251
            $body .= sprintf "\n  Log:";  # Rota log details
248
            else {
252
            if (@{$rota->{log}}) {
249
                $body .=
253
                $body .= join("", map { _print_item($_) } @{$rota->{log}});
250
                  sprintf "\n    No items to be processed for this rota.\n";
254
            } else {
251
            }
252
            $body .= sprintf "\n  Log:";      # Rota log details
253
            if ( @{ $rota->{log} } ) {
254
                $body .= join( "", map { _print_item($_) } @{ $rota->{log} } );
255
            }
256
            else {
255
                $body .= sprintf "\n    No items in log for this rota.\n\n";
257
                $body .= sprintf "\n    No items in log for this rota.\n\n";
256
            }
258
            }
257
        }
259
        }
258
    }
260
    }
259
    return [ $header, {
261
    return [
260
        body            => $body, # The body of the report
262
        $header,
261
        status          => 1,     # We have a meaningful report
263
        {
262
        no_branch_email => 1,     # We don't expect branch email in report
264
            letter => {
263
    } ];
265
                title   => 'Stockrotation Report',
266
                content => $body                     # The body of the report
267
            },
268
            status          => 1,    # We have a meaningful report
269
            no_branch_email => 1,    # We don't expect branch email in report
270
        }
271
    ];
264
}
272
}
265
273
266
=head3 report_email
274
=head3 report_email
Lines 282-314 No data in the database is manipulated by this procedure. Link Here
282
sub report_email {
290
sub report_email {
283
    my ( $data, $branch ) = @_;
291
    my ( $data, $branch ) = @_;
284
292
285
    my $out = [];
293
    my $out    = [];
286
    my $header = "";
294
    my $header = "";
295
287
    # Summary
296
    # Summary
288
    my $branched = $data->{branched};
297
    my $branched = $data->{branched};
289
    my $flag = 0;
298
    my $flag     = 0;
290
299
291
    $header .= sprintf "
300
    $header .= sprintf "
292
BRANCH-BASED STOCKROTATION REPORT
301
BRANCH-BASED STOCKROTATION REPORT
293
---------------------------------\n";
302
---------------------------------\n";
294
    push @{$out}, $header;
303
    push @{$out}, $header;
295
304
296
    if ( $branch ) {                      # Branch limited report
305
    if ($branch) {    # Branch limited report
297
        push @{$out}, _report_per_branch(
306
        push @{$out}, _report_per_branch( $branched->{ $branch->branchcode } );
298
            $branched->{$branch->branchcode}, $branch->branchcode,
307
    }
299
            $branch->branchname
308
    elsif ( $data->{actionable} ) {    # Full email report
300
        );
309
        while ( my ( $branchcode_id, $details ) = each %{$branched} ) {
301
    } elsif ( $data->{actionable} ) {     # Full email report
310
            push @{$out}, _report_per_branch($details)
302
        while ( my ($branchcode_id, $details) = each %{$branched} ) {
311
              if ( @{ $details->{items} } );
303
            push @{$out}, _report_per_branch(
304
                $details, $details->{code}, $details->{name}
305
            ) if ( @{$details->{items}} );
306
        }
312
        }
307
    } else {
313
    }
314
    else {
308
        push @{$out}, {
315
        push @{$out}, {
309
            body            => sprintf "
316
            body => sprintf "
310
No actionable items at any libraries.\n\n", # The body of the report
317
No actionable items at any libraries.\n\n",    # The body of the report
311
            no_branch_email => 1, # We don't expect branch email in report
318
            no_branch_email => 1,    # We don't expect branch email in report
312
        };
319
        };
313
    }
320
    }
314
    return $out;
321
    return $out;
Lines 328-358 No data in the database is manipulated by this procedure. Link Here
328
=cut
335
=cut
329
336
330
sub _report_per_branch {
337
sub _report_per_branch {
331
    my ( $per_branch, $branchcode, $branchname ) = @_;
338
    my ($branch) = @_;
332
339
333
    my $out = "";
334
    my $status = 0;
340
    my $status = 0;
341
    if ( $branch && @{ $branch->{items} } ) {
342
        $status = 1;
343
    }
335
344
336
    $out .= sprintf "\nStockrotation report for '%s' [%s]:\n",
345
    if (
337
        $branchname || "N/A", $branchcode || "N/A";
346
        my $letter = C4::Letters::GetPreparedLetter(
338
    if ( $per_branch && @{$per_branch->{items}} ) {
347
            module                 => 'circulation',
339
        $out .= sprintf "
348
            letter_code            => "SR_SLIP",
340
  Email: %s
349
            message_transport_type => 'email',
341
  Phone: %s
350
            substitute             => $branch
342
  Items:",
351
        )
343
        $per_branch->{email} || "N/A", $per_branch->{phone} || "N/A";
352
      )
344
        $status++;
353
    {
345
    } else {
354
        return {
346
        $out .= sprintf "No items to be processed for this branch.\n\n";
355
            letter        => $letter,
356
            email_address => $branch->{email},
357
            $status
358
        };
347
    }
359
    }
348
    $out .= join("", map {
360
    return;
349
        _print_item($_) unless $_->{reason} eq 'in-demand'
350
    } @{$per_branch->{items}});
351
    return {
352
        body          => $out,                 # The body of the report
353
        email_address => $per_branch->{email}, # The branch email address
354
        status        => $status,              # We may have empty report...
355
    };
356
}
361
}
357
362
358
=head3 _print_item
363
=head3 _print_item
Lines 361-375 sub _report_per_branch { Link Here
361
366
362
Return a string containing an overview about $ITEM_SECTION.
367
Return a string containing an overview about $ITEM_SECTION.
363
368
364
This helper procedure is only used from within `report_email` and
369
This helper procedure is only used from within `report_full`.
365
`report_full`.
366
370
367
No data in the database is manipulated by this procedure.
371
No data in the database is manipulated by this procedure.
368
372
369
=cut
373
=cut
370
374
371
sub _print_item {
375
sub _print_item {
372
    my ( $item ) = @_;
376
    my ($item) = @_;
373
    return sprintf "
377
    return sprintf "
374
    Title:           %s
378
    Title:           %s
375
    Author:          %s
379
    Author:          %s
Lines 379-389 sub _print_item { Link Here
379
    On loan?:        %s
383
    On loan?:        %s
380
    Status:          %s
384
    Status:          %s
381
    Current Library: %s [%s]\n\n",
385
    Current Library: %s [%s]\n\n",
382
          $item->{title} || "N/A", $item->{author} || "N/A",
386
      $item->{title}      || "N/A", $item->{author}   || "N/A",
383
          $item->{callnumber} || "N/A", $item->{location} || "N/A",
387
      $item->{callnumber} || "N/A", $item->{location} || "N/A",
384
          $item->{barcode} || "N/A", $item->{onloan} ?  'Yes' : 'No',
388
      $item->{barcode} || "N/A", $item->{onloan} ? 'Yes' : 'No',
385
          $item->{reason} || "N/A", $item->{branch}->branchname,
389
      $item->{reason} || "N/A", $item->{branch}->branchname,
386
          $item->{branch}->branchcode;
390
      $item->{branch}->branchcode;
387
}
391
}
388
392
389
=head3 emit
393
=head3 emit
Lines 405-486 die. Link Here
405
=cut
409
=cut
406
410
407
sub emit {
411
sub emit {
408
    my ( $params ) = @_;
412
    my ($params) = @_;
409
413
410
    # REPORT is an arrayref of at least 2 elements:
414
# REPORT is an arrayref of at least 2 elements:
411
    #   - The header for the report, which will be repeated for each part
415
#   - The header for the report, which will be repeated for each part
412
    #   - a "part" for each report we want to emit
416
#   - a "part" for each report we want to emit
413
    # PARTS are hashrefs:
417
# PARTS are hashrefs:
414
    #   - part->{body}: the body of the report
418
#   - part->{status}: a boolean indicating whether the reported part is empty or not
415
    #   - part->{email_address}: the email address to send the report to
419
#   - part->{email_address}: the email address to send the report to
420
#   - part->{no_branch_email}: a boolean indicating that we are missing a branch email
421
#   - part->{letter}: a GetPreparedLetter hash as returned by the C4::Letters module
416
    my $report = $params->{report};
422
    my $report = $params->{report};
417
    my $header = shift @{$report};
423
    my $header = shift @{$report};
418
    my $parts = $report;
424
    my $parts  = $report;
419
425
420
    foreach my $part (@{$parts}) {
426
    my @emails;
421
        # The final message is the header + body of this part.
427
    foreach my $part ( @{$parts} ) {
422
        my $msg = $header . $part->{body};
428
423
        $msg .= "No database updates have been performed.\n\n"
429
        if ( $part->{status} || $params->{send_all} ) {
424
            unless ( $params->{execute} );
430
425
431
            # We have a report to send, or we want to send even empty
426
        if ( $params->{send_email} ) { # Only email if emails requested
432
            # reports.
427
            if ( $part->{status} || $params->{send_all} ) {
433
428
                # We have a report to send, or we want to send even empty
434
            # Send to branch
429
                # reports.
435
            my $addressee;
430
                my $message = Koha::Email->new;
436
            if ( $part->{email_address} ) {
431
437
                $addressee = $part->{email_address};
432
                # Collate 'to' addresses
433
                my @to = ();
434
                if ( $part->{email_address} ) {
435
                    push @to, $part->{email_address};
436
                } elsif ( !$part->{no_branch_email} ) {
437
                    $msg = "***We tried to send a branch report, but we have no email address for this branch.***\n\n" . $msg;
438
                    push @to, C4::Context->preference('KohaAdminEmailAddress')
439
                        if ( C4::Context->preference('KohaAdminEmailAddress') );
440
                }
441
                push @to, $params->{admin_email} if $params->{admin_email};
442
443
                # Create email data.
444
                my %mail = $message->create_message_headers(
445
                    {
446
                        to          => join("; ", @to),
447
                        subject     => "Stockrotation Email Report",
448
                        message     => Encode::encode("utf8", $msg),
449
                        contenttype => 'text/plain; charset="utf8"',
450
                    }
451
                );
452
453
                # Send or die.
454
                die($Mail::Sendmail::error)
455
                    unless Mail::Sendmail::sendmail(%mail);
456
            }
438
            }
439
            elsif ( !$part->{no_branch_email} ) {
457
440
458
        } else {                       #  Else emit to stdout.
441
#push @emails, "***We tried to send a branch report, but we have no email address for this branch.***\n\n";
459
            printf $msg;
442
                $addressee = C4::Context->preference('KohaAdminEmailAddress')
443
                  if ( C4::Context->preference('KohaAdminEmailAddress') );
444
            }
445
446
            if ( $params->{send_email} ) {    # Only email if emails requested
447
                if ( defined($addressee) ) {
448
                    C4::Letters::EnqueueLetter(
449
                        {
450
                            letter                 => $part->{letter},
451
                            to_address             => $addressee,
452
                            message_transport_type => 'email',
453
                        }
454
                      )
455
                      or warn
456
                      "can't enqueue letter $part->{letter} for $addressee";
457
                }
458
459
                # Copy to admin?
460
                if ( $params->{admin_email} ) {
461
                    C4::Letters::EnqueueLetter(
462
                        {
463
                            letter                 => $part->{letter},
464
                            to_address             => $params->{admin_email},
465
                            message_transport_type => 'email',
466
                        }
467
                      )
468
                      or warn
469
"can't enqueue letter $part->{letter} for $params->{admin_email}";
470
                }
471
            }
472
            else {
473
                my $email =
474
                  "-------- Email message --------" . "\n\n" . "To: "
475
                  . defined($addressee)               ? $addressee
476
                  : defined( $params->{admin_email} ) ? $params->{admin_email}
477
                  : '' . "\n"
478
                  . "Subject: "
479
                  . $part->{letter}->{title} . "\n\n"
480
                  . $part->{letter}->{content};
481
                push @emails, $email;
482
            }
460
        }
483
        }
461
    }
484
    }
485
486
    # Emit to stdout instead of email?
487
    if ( !$params->{send_email} ) {
488
489
        # The final message is the header + body of this part.
490
        my $msg = $header;
491
        $msg .= "No database updates have been performed.\n\n"
492
          unless ( $params->{execute} );
493
494
        # Append email reports to message
495
        $msg .= join( "\n\n", @emails );
496
        printf $msg;
497
    }
462
}
498
}
463
499
464
#### Main Code
500
#### Main Code
465
501
466
# Compile Stockrotation Report data
502
# Compile Stockrotation Report data
467
my $rotas = Koha::StockRotationRotas->search;
503
my $rotas = Koha::StockRotationRotas->search;
468
my $data = $rotas->investigate;
504
my $data  = $rotas->investigate;
469
505
470
# Perform db updates if requested
506
# Perform db updates if requested
471
execute($data) if ( $execute );
507
execute($data) if ($execute);
472
508
473
# Emit Reports
509
# Emit Reports
474
my $out_report = {};
510
my $out_report = {};
475
$out_report = report_email($data, $branch) if $report eq 'email';
511
$out_report = report_email( $data, $branch ) if $report eq 'email';
476
$out_report = report_full($data, $branch) if $report eq 'full';
512
$out_report = report_full( $data, $branch ) if $report eq 'full';
477
emit({
513
emit(
478
    admin_email => $admin_email,
514
    {
479
    execute     => $execute,
515
        admin_email => $admin_email,
480
    report      => $out_report,
516
        execute     => $execute,
481
    send_all    => $send_all,
517
        report      => $out_report,
482
    send_email  => $send_email,
518
        send_all    => $send_all,
483
});
519
        send_email  => $send_email,
520
    }
521
);
484
522
485
=head1 AUTHOR
523
=head1 AUTHOR
486
524
487
- 

Return to bug 11897