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

(-)a/misc/cronjobs/holds_reminder.pl (-1 / +747 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# Copyright 2008 Liblime
4
# Copyright 2010 BibLibre
5
#
6
# This file is part of Koha.
7
#
8
# Koha is free software; you can redistribute it and/or modify it
9
# under the terms of the GNU General Public License as published by
10
# the Free Software Foundation; either version 3 of the License, or
11
# (at your option) any later version.
12
#
13
# Koha is distributed in the hope that it will be useful, but
14
# WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU General Public License for more details.
17
#
18
# You should have received a copy of the GNU General Public License
19
# along with Koha; if not, see <http://www.gnu.org/licenses>.
20
21
use Modern::Perl;
22
23
BEGIN {
24
25
    # find Koha's Perl modules
26
    # test carefully before changing this
27
    use FindBin;
28
    eval { require "$FindBin::Bin/../kohalib.pl" };
29
}
30
31
use Getopt::Long;
32
use Pod::Usage;
33
use Text::CSV_XS;
34
use DateTime;
35
use DateTime::Duration;
36
37
use C4::Context;
38
use C4::Letters;
39
use C4::Log;
40
use Koha::DateUtils;
41
use Koha::Calendar;
42
use Koha::Libraries;
43
44
=head1 NAME
45
46
holds_reminder.pl - prepare reminder messages to be sent to patrons with waiting holds
47
48
=head1 SYNOPSIS
49
50
holds_reminder.pl
51
  [ -n ][ -library <branchcode> ][ -library <branchcode> ... ]
52
  [ -days <number of days> ][ -csv [<filename>] ][ -itemscontent <field list> ]
53
  [ -email <email_type> ... ]
54
55
 Options:
56
   -help                          brief help message
57
   -man                           full documentation
58
   -v                             verbose
59
   -n                             No email will be sent
60
   -days          <days>          days waiting to deal with
61
   -lettercode   <lettercode>     predefined notice to use
62
   -library      <branchname>     only deal with holds from this library (repeatable : several libraries can be given)
63
   -csv          <filename>       populate CSV file
64
   -html         <directory>      Output html to a file in the given directory
65
   -text         <directory>      Output plain text to a file in the given directory
66
   -itemscontent <list of fields> item information in templates
67
   -holidays                      use the calendar to not count holidays as waiting days
68
   -mtt          <message_transport_type> type of messages to send, default is email only
69
   -email        <email_type>     type of email that will be used. Can be 'email', 'emailpro' or 'B_email'. Repeatable.
70
71
=head1 OPTIONS
72
73
=over 8
74
75
=item B<-help>
76
77
Print a brief help message and exits.
78
79
=item B<-man>
80
81
Prints the manual page and exits.
82
83
=item B<-v>
84
85
Verbose. Without this flag set, only fatal errors are reported.
86
87
=item B<-n>
88
89
Do not send any email. Reminder notices that would have been sent to
90
the patrons or to the admin are printed to standard out. CSV data (if
91
the -csv flag is set) is written to standard out or to any csv
92
filename given.
93
94
=item B<-days>
95
96
Optional parameter, number of days an items has been 'waiting' on hold
97
to send a message for. If not included a notice will be sent to all
98
patrons with waiting holds.
99
100
=item B<-list-all>
101
102
Default lists only those holds that have been waiting since B<-days>
103
parameter.
104
Choose list-all to include all s waiting holds.
105
106
=item B<-library>
107
108
select overdues for one specific library. Use the value in the
109
branches.branchcode table. This option can be repeated in order
110
to select overdues for a group of libraries.
111
112
=item B<-csv>
113
114
Produces CSV data. if -n (no mail) flag is set, then this CSV data is
115
sent to standard out or to a filename if provided. Otherwise, only
116
overdues that could not be emailed are sent in CSV format to the admin.
117
118
=item B<-html>
119
120
Produces html data. If patron does not have an email address or
121
-n (no mail) flag is set, an HTML file is generated in the specified
122
directory. This can be downloaded or further processed by library staff.
123
The file will be called notices-YYYY-MM-DD.html and placed in the directory
124
specified.
125
126
=item B<-text>
127
128
Produces plain text data. If patron does not have an email address or
129
-n (no mail) flag is set, a text file is generated in the specified
130
directory. This can be downloaded or further processed by library staff.
131
The file will be called notices-YYYY-MM-DD.txt and placed in the directory
132
specified.
133
134
=item B<-itemscontent>
135
136
comma separated list of fields that get substituted into templates in
137
places of the E<lt>E<lt>items.contentE<gt>E<gt> placeholder. This
138
defaults to due date,title,barcode,author
139
140
Other possible values come from fields in the biblios, items and
141
issues tables.
142
143
=item B<-holidays>
144
145
This option determines whether library holidays are used when calculating how
146
long an item has been waiting. If enabled the count will skip closed days.
147
148
=item B<-date>
149
150
use it in order to send overdues on a specific date and not Now. Format: YYYY-MM-DD.
151
152
=item B<-email>
153
154
Allows to specify which type of email will be used. Can be email, emailpro or B_email. Repeatable.
155
156
=back
157
158
=head1 DESCRIPTION
159
160
This script is designed to alert patrons and administrators of waiting
161
holds.
162
163
=head2 Configuration
164
165
This script sends reminders to patrons with waiting holds using a notice
166
defined in the Tools->Notices & slips module within Koha. The lettercode
167
is passed into this script and, along with other options, determine the content
168
of the notices sent to patrons.
169
170
=head2 Outgoing emails
171
172
Typically, messages are prepared for each patron with waiting holds.
173
Messages for whom there is no email address on file are collected and
174
sent as attachments in a single email to each library
175
administrator, or if that is not set, then to the email address in the
176
C<KohaAdminEmailAddress> system preference.
177
178
These emails are staged in the outgoing message queue, as are messages
179
produced by other features of Koha. This message queue must be
180
processed regularly by the
181
F<misc/cronjobs/process_message_queue.pl> program.
182
183
=head2 Templates
184
185
Templates can contain variables enclosed in double angle brackets like
186
E<lt>E<lt>thisE<gt>E<gt>. Those variables will be replaced with values
187
specific to the waiting holds or relevant patron. Available variables
188
are:
189
190
=over
191
192
=item E<lt>E<lt>bibE<gt>E<gt>
193
194
the name of the library
195
196
=item E<lt>E<lt>items.contentE<gt>E<gt>
197
198
one line for each item, each line containing a tab separated list of
199
title, author, barcode, issuedate
200
201
=item E<lt>E<lt>borrowers.*E<gt>E<gt>
202
203
any field from the borrowers table
204
205
=item E<lt>E<lt>branches.*E<gt>E<gt>
206
207
any field from the branches table
208
209
=back
210
211
=head2 CSV output
212
213
The C<-csv> command line option lets you specify a file to which
214
hold reminder data should be output in CSV format.
215
216
With the C<-n> flag set, data about all holds is written to the
217
file. Without that flag, only information about holds that were
218
unable to be sent directly to the patrons will be written. In other
219
words, this CSV file replaces the data that is typically sent to the
220
administrator email address.
221
222
=head1 USAGE EXAMPLES
223
224
C<holds_reminder.pl> - With no arguments the simple help is printed
225
226
C<holds_reminder.pl -lettercode CODE > In this most basic usage all
227
libraries are processed individually, and notices are prepared for
228
all patrons with overdue items for whom we have email addresses.
229
Messages for those patrons for whom we have no email
230
address are sent in a single attachment to the library administrator's
231
email address, or to the address in the KohaAdminEmailAddress system
232
preference.
233
234
C<holds_reminder.pl -lettercode CODE -n -csv /tmp/holds_reminder.csv> - sends no email and
235
populates F</tmp/holds_reminder.csv> with information about all waiting holds
236
items.
237
238
C<holds_reminder.pl -lettercode CODE -library MAIN -days 14> - prepare notices of
239
holds waiting for 2 weeks for the MAIN library.
240
241
C<holds_reminder.pl -library MAIN -days 14 -list-all> - prepare notices
242
of holds waiting for 2 weeks for the MAIN library and include all the
243
patron's waiting hold
244
245
=cut
246
247
# These variables are set by command line options.
248
# They are initially set to default values.
249
my $dbh = C4::Context->dbh();
250
my $help    = 0;
251
my $man     = 0;
252
my $verbose = 0;
253
my $nomail  = 0;
254
my $days    ;
255
my $lettercode;
256
my @branchcodes; # Branch(es) passed as parameter
257
my @emails_to_use;    # Emails to use for messaging
258
my @emails;           # Emails given in command-line parameters
259
my @message_transport_types;
260
my $csvfilename;
261
my $htmlfilename;
262
my $text_filename;
263
my $triggered = 0;
264
my $listall = 0;
265
my $itemscontent = join( ',', qw( waitingdate title barcode author itemnumber ) );
266
my $use_calendar = 0;
267
my ( $date_input, $today );
268
269
GetOptions(
270
    'help|?'         => \$help,
271
    'man'            => \$man,
272
    'v'              => \$verbose,
273
    'n'              => \$nomail,
274
    'days=s'         => \$days,
275
    'lettercode=s'   => \$lettercode,
276
    'library=s'      => \@branchcodes,
277
    'csv:s'          => \$csvfilename,    # this optional argument gets '' if not supplied.
278
    'html:s'         => \$htmlfilename,    # this optional argument gets '' if not supplied.
279
    'text:s'         => \$text_filename,    # this optional argument gets '' if not supplied.
280
    'itemscontent=s' => \$itemscontent,
281
    'list-all'       => \$listall,
282
    't|triggered'    => \$triggered,
283
    'date=s'         => \$date_input,
284
    'email=s'        => \@emails,
285
    'mtt=s'          => \@message_transport_types,
286
    'holidays'     => \$use_calendar
287
) or pod2usage(1);
288
pod2usage(1) if $help;
289
pod2usage( -verbose => 2 ) if $man;
290
291
if ( !$lettercode ) {
292
    pod2usage({
293
        -exitval => 1,
294
        -msg => qq{\nError: You must specify a lettercode to send reminders.\n},
295
    });
296
}
297
298
299
cronlogaction();
300
301
unless (defined $days) {
302
    $days=0;
303
    $listall=1;
304
}
305
306
if ( defined $csvfilename && $csvfilename =~ /^-/ ) {
307
    warn qq(using "$csvfilename" as filename, that seems odd);
308
}
309
310
my $PrintNoticesMaxLines = C4::Context->preference('PrintNoticesMaxLines');
311
312
if (scalar @branchcodes > 0) {
313
    my $branchcodes_word = scalar @branchcodes > 1 ? 'branches' : 'branch';
314
    $verbose and warn "$branchcodes_word @branchcodes passed on parameter\n";
315
}
316
else {
317
    my $branches = Koha::Libraries->search( {} , {columns => 'branchcode' } );
318
    while ( my $branch = $branches->next ) {
319
        push @branchcodes, $branch->branchcode;
320
    }
321
}
322
323
my $date_to_run;
324
my $date;
325
if ( $date_input ){
326
    eval {
327
        $date_to_run = dt_from_string( $date_input, 'iso' );
328
    };
329
    die "$date_input is not a valid date, aborting! Use a date in format YYYY-MM-DD."
330
        if $@ or not $date_to_run;
331
    $date = $dbh->quote( $date_input );
332
}
333
else {
334
    $date="NOW()";
335
    $date_to_run = dt_from_string();
336
}
337
338
339
# these are the fields that will be substituted into <<item.content>>
340
my @item_content_fields = split( /,/, $itemscontent );
341
342
binmode( STDOUT, ':encoding(UTF-8)' );
343
344
345
our $csv;       # the Text::CSV_XS object
346
our $csv_fh;    # the filehandle to the CSV file.
347
if ( defined $csvfilename ) {
348
    my $sep_char = C4::Context->preference('delimiter') || ';';
349
    $sep_char = "\t" if ($sep_char eq 'tabulation');
350
    $csv = Text::CSV_XS->new( { binary => 1 , sep_char => $sep_char } );
351
    if ( $csvfilename eq '' ) {
352
        $csv_fh = *STDOUT;
353
    } else {
354
        open $csv_fh, ">", $csvfilename or die "unable to open $csvfilename: $!";
355
    }
356
    if ( $csv->combine(qw(name surname address1 address2 zipcode city country email phone cardnumber itemcount itemsinfo branchname letternumber)) ) {
357
        print $csv_fh $csv->string, "\n";
358
    } else {
359
        $verbose and warn 'combine failed on argument: ' . $csv->error_input;
360
    }
361
}
362
363
our $fh;
364
if ( defined $htmlfilename ) {
365
  if ( $htmlfilename eq '' ) {
366
    $fh = *STDOUT;
367
  } else {
368
    my $today = DateTime->now(time_zone => C4::Context->tz );
369
    open $fh, ">:encoding(UTF-8)",File::Spec->catdir ($htmlfilename,"notices-".$today->ymd().".html");
370
  }
371
372
  print $fh "<html>\n";
373
  print $fh "<head>\n";
374
  print $fh "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
375
  print $fh "<style type='text/css'>\n";
376
  print $fh "pre {page-break-after: always;}\n";
377
  print $fh "pre {white-space: pre-wrap;}\n";
378
  print $fh "pre {white-space: -moz-pre-wrap;}\n";
379
  print $fh "pre {white-space: -o-pre-wrap;}\n";
380
  print $fh "pre {word-wrap: break-work;}\n";
381
  print $fh "</style>\n";
382
  print $fh "</head>\n";
383
  print $fh "<body>\n";
384
}
385
elsif ( defined $text_filename ) {
386
  if ( $text_filename eq '' ) {
387
    $fh = *STDOUT;
388
  } else {
389
    my $today = DateTime->now(time_zone => C4::Context->tz );
390
    open $fh, ">",File::Spec->catdir ($text_filename,"notices-".$today->ymd().".txt");
391
  }
392
}
393
394
foreach my $branchcode (@branchcodes) { #BEGIN BRANCH LOOP
395
    if ( C4::Context->preference('OverdueNoticeCalendar') ) {
396
        my $calendar = Koha::Calendar->new( branchcode => $branchcode );
397
        if ( $calendar->is_holiday($date_to_run) ) {
398
            next;
399
        }
400
    }
401
402
    my $letter = C4::Letters::getletter( 'reserves', $lettercode , $branchcode );
403
    unless ($letter) {
404
        $verbose and warn qq|Message '$lettercode' content not found|;
405
        # might as well skip this branch, no borrowers are going to work.
406
        next;
407
    }
408
409
    my $library             = Koha::Libraries->find($branchcode);
410
    my $admin_email_address = $library->branchemail
411
      || C4::Context->preference('KohaAdminEmailAddress');
412
    my @output_chunks;    # may be sent to mail or stdout or csv file.
413
414
    $verbose and warn sprintf "branchcode : '%s' using %s\n", $branchcode, $admin_email_address;
415
416
    my $sth2 = $dbh->prepare( <<"END_SQL" );
417
SELECT biblio.*, items.*, reserves.*, biblioitems.itemtype, branchname
418
  FROM reserves,items,biblio, biblioitems, branches b
419
  WHERE reserves.found = 'W'
420
    AND items.itemnumber=reserves.itemnumber
421
    AND biblio.biblionumber   = items.biblionumber
422
    AND b.branchcode = items.homebranch
423
    AND biblio.biblionumber   = biblioitems.biblionumber
424
    AND reserves.borrowernumber = ?
425
    AND TO_DAYS($date)-TO_DAYS(reserves.waitingdate) >= $days
426
END_SQL
427
428
    my $borrower_sql = <<"END_SQL";
429
SELECT reserves.borrowernumber, firstname, surname, address, address2, city, zipcode, country, email, emailpro, B_email, smsalertnumber, phone, cardnumber, waitingdate
430
FROM   reserves,borrowers,categories
431
WHERE  reserves.borrowernumber=borrowers.borrowernumber
432
AND    borrowers.categorycode=categories.categorycode
433
AND    TO_DAYS($date)-TO_DAYS(reserves.waitingdate) >= 0
434
END_SQL
435
    my @borrower_parameters;
436
    if ($branchcode) {
437
        $borrower_sql .= ' AND reserves.branchcode=? ';
438
        push @borrower_parameters, $branchcode;
439
    }
440
441
    # $sth gets borrower info iff at least one overdue item has triggered the overdue action.
442
    my $sth = $dbh->prepare($borrower_sql);
443
    $sth->execute(@borrower_parameters);
444
    $verbose and warn $borrower_sql . "\n $branchcode " . "\n ($date, ".  $date_to_run->datetime() .")\nreturns " . $sth->rows . " rows";
445
    my $borrowernumber;
446
    while ( my $data = $sth->fetchrow_hashref ) { #BEGIN BORROWER LOOP
447
        # check the borrower has at least one item that matches
448
        my $days_waiting;
449
               if ( $use_calendar )
450
                {
451
                    my $calendar =
452
                      Koha::Calendar->new( branchcode => $branchcode );
453
                    $days_waiting =
454
                      $calendar->days_between( dt_from_string($data->{waitingdate}),
455
                        $date_to_run );
456
                }
457
                else {
458
                    $days_waiting =
459
                      $date_to_run->delta_days( dt_from_string($data->{waitingdate}) );
460
                }
461
                $days_waiting = $days_waiting->in_units('days');
462
                if ($days) { next unless ( $days_waiting == $days ); }
463
464
                if (defined $borrowernumber && $borrowernumber eq $data->{'borrowernumber'}){
465
                # we have already dealt with this borrower
466
                    $verbose and warn "already dealt with this borrower $borrowernumber";
467
                    next;
468
                }
469
                $borrowernumber = $data->{'borrowernumber'};
470
                my $borr =
471
                    $data->{'firstname'} . ', '
472
                  . $data->{'surname'} . ' ('
473
                  . $borrowernumber . ')';
474
                $verbose
475
                  and warn "borrower $borr has holds triggering notice.";
476
477
                @emails_to_use = ();
478
                my $notice_email =
479
                    C4::Members::GetNoticeEmailAddress($borrowernumber);
480
                unless ($nomail) {
481
                    if (@emails) {
482
                        foreach (@emails) {
483
                            push @emails_to_use, $data->{$_} if ( $data->{$_} );
484
                        }
485
                    }
486
                    else {
487
                        push @emails_to_use, $notice_email if ($notice_email);
488
                    }
489
                }
490
491
                my @params = ($borrowernumber);
492
                $verbose and warn "STH2 PARAMS: borrowernumber = $borrowernumber";
493
494
                $sth2->execute(@params);
495
                my $holdcount = 0;
496
                my $titles = "";
497
                my @holds = ();
498
499
                my $j = 0;
500
                my $exceededPrintNoticesMaxLines = 0;
501
                while ( my $hold_info = $sth2->fetchrow_hashref() ) { #BEGIN HOLDS LOOP
502
                    if ( $use_calendar ) {
503
                        my $calendar =
504
                          Koha::Calendar->new( branchcode => $branchcode );
505
                        $days_waiting =
506
                          $calendar->days_between(
507
                            dt_from_string( $hold_info->{waitingdate} ), $date_to_run );
508
                    }
509
                    else {
510
                        $days_waiting =
511
                          $date_to_run->delta_days(
512
                            dt_from_string( $hold_info->{waitingdate} ) );
513
                    }
514
                    $days_waiting = $days_waiting->in_units('days');
515
                    unless ($listall) { next unless ( $days_waiting == $days ); }
516
517
                    if ( ( scalar(@emails_to_use) == 0 || $nomail ) && $PrintNoticesMaxLines && $j >= $PrintNoticesMaxLines ) {
518
                      $exceededPrintNoticesMaxLines = 1;
519
                      last;
520
                    }
521
                    $j++;
522
                    my @hold_info = map { $_ =~ /^date|date$/ ?
523
                                           eval { output_pref( { dt => dt_from_string( $hold_info->{$_} ), dateonly => 1 } ); }
524
                                           :
525
                                           $hold_info->{$_} || '' } @item_content_fields;
526
                    $titles .= join("\t", @hold_info) . "\n";
527
                    $holdcount++;
528
                    push @holds, $hold_info;
529
                } #END HOLD LOOP
530
                $sth2->finish;
531
532
                @message_transport_types = ('email') unless ( scalar @message_transport_types > 0 );
533
534
535
                my $print_sent = 0; # A print notice is not yet sent for this patron
536
                for my $mtt ( @message_transport_types ) {
537
                    my $effective_mtt = $mtt;
538
                    if ( ($mtt eq 'email' and not scalar @emails_to_use) or ($mtt eq 'sms' and not $data->{smsalertnumber}) ) {
539
                        # email or sms is requested but not exist, do a print.
540
                        $effective_mtt = 'print';
541
                    }
542
543
                    my $letter_exists = C4::Letters::getletter( 'reserves', $lettercode, $branchcode, $effective_mtt ) ? 1 : 0;
544
                    my $letter = GetPreparedLetter(
545
                           module          => 'reserves',
546
                            letter_code     => $lettercode,
547
                            borrowernumber  => $borrowernumber,
548
                            branchcode      => $branchcode,
549
                            repeat           => { item => \@holds},
550
                            tables          => { 'borrowers' => $borrowernumber, 'branches' => $branchcode },
551
                            substitute      => {
552
                                               'items.content' => $titles,
553
                                               'count'         => $holdcount,
554
                                               },
555
                            # If there is no template defined for the requested letter
556
                            # Fallback on email
557
                            message_transport_type => $letter_exists ? $effective_mtt : 'email',
558
559
                    );
560
                    unless ($letter) {
561
                        $verbose and warn qq|Message '$lettercode' content not found|;
562
                        # this transport doesn't have a configured notice, so try another
563
                        next;
564
                    }
565
566
                    if ( $exceededPrintNoticesMaxLines ) {
567
                      $letter->{'content'} .= "List too long for form; please check your account online for a complete list of your waiting holds.";
568
                    }
569
570
                    my @misses = grep { /./ } map { /^([^>]*)[>]+/; ( $1 || '' ); } split /\</, $letter->{'content'};
571
                    if (@misses) {
572
                        $verbose and warn "The following terms were not matched and replaced: \n\t" . join "\n\t", @misses;
573
                    }
574
575
                    if ($nomail) {
576
                        push @output_chunks,
577
                          prepare_letter_for_printing(
578
                          {   letter         => $letter,
579
                              borrowernumber => $borrowernumber,
580
                              firstname      => $data->{'firstname'},
581
                              lastname       => $data->{'surname'},
582
                              address1       => $data->{'address'},
583
                              address2       => $data->{'address2'},
584
                              city           => $data->{'city'},
585
                              phone          => $data->{'phone'},
586
                              cardnumber     => $data->{'cardnumber'},
587
                              branchname     => $library->branchname,
588
                              postcode       => $data->{'zipcode'},
589
                              country        => $data->{'country'},
590
                              email          => $notice_email,
591
                              itemcount      => $holdcount,
592
                              titles         => $titles,
593
                              outputformat   => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : defined $text_filename ? 'text' : '',
594
                            }
595
                          );
596
                    } else {
597
                        if ( ($mtt eq 'email' and not scalar @emails_to_use) or ($mtt eq 'sms' and not $data->{smsalertnumber}) ) {
598
                            push @output_chunks,
599
                              prepare_letter_for_printing(
600
                              {   letter         => $letter,
601
                                  borrowernumber => $borrowernumber,
602
                                  firstname      => $data->{'firstname'},
603
                                  lastname       => $data->{'surname'},
604
                                  address1       => $data->{'address'},
605
                                  address2       => $data->{'address2'},
606
                                  city           => $data->{'city'},
607
                                  postcode       => $data->{'zipcode'},
608
                                  country        => $data->{'country'},
609
                                  email          => $notice_email,
610
                                  itemcount      => $holdcount,
611
                                  titles         => $titles,
612
                                  outputformat   => defined $csvfilename ? 'csv' : defined $htmlfilename ? 'html' : defined $text_filename ? 'text' : '',
613
                                }
614
                              );
615
                        }
616
                        unless ( $effective_mtt eq 'print' and $print_sent == 1 ) {
617
                            # Just sent a print if not already done.
618
                            C4::Letters::EnqueueLetter(
619
                                {   letter                 => $letter,
620
                                    borrowernumber         => $borrowernumber,
621
                                    message_transport_type => $effective_mtt,
622
                                    from_address           => $admin_email_address,
623
                                    to_address             => join(',', @emails_to_use),
624
                                }
625
                            );
626
                            # A print notice should be sent only once per overdue level.
627
                            # Without this check, a print could be sent twice or more if the library checks sms and email and print and the patron has no email or sms number.
628
                            $print_sent = 1 if $effective_mtt eq 'print';
629
                        }
630
                    }
631
                }
632
            } #END BORROWER LOOP
633
            $sth->finish;
634
635
    if (@output_chunks) {
636
        if ( defined $csvfilename ) {
637
            print $csv_fh @output_chunks;
638
        }
639
        elsif ( defined $htmlfilename ) {
640
            print $fh @output_chunks;
641
        }
642
        elsif ( defined $text_filename ) {
643
            print $fh @output_chunks;
644
        }
645
        elsif ($nomail){
646
                local $, = "\f";    # pagebreak
647
                print @output_chunks;
648
        }
649
        # Generate the content of the csv with headers
650
        my $content;
651
        if ( defined $csvfilename ) {
652
            my $delimiter = C4::Context->preference('delimiter') || ';';
653
            $content = join($delimiter, qw(title name surname address1 address2 zipcode city country email itemcount itemsinfo due_date issue_date)) . "\n";
654
        }
655
        else {
656
            $content = "";
657
        }
658
        $content .= join( "\n", @output_chunks );
659
660
        my $attachment = {
661
            filename => defined $csvfilename ? 'attachment.csv' : 'attachment.txt',
662
            type => 'text/plain',
663
            content => $content,
664
        };
665
666
        my $letter = {
667
            title   => 'Overdue Notices',
668
            content => 'These messages were not sent directly to the patrons.',
669
        };
670
        C4::Letters::EnqueueLetter(
671
            {   letter                 => $letter,
672
                borrowernumber         => undef,
673
                message_transport_type => 'email',
674
                attachments            => [$attachment],
675
                to_address             => $admin_email_address,
676
            }
677
        );
678
    }
679
680
} #END BRANCH LOOP
681
if ($csvfilename) {
682
    # note that we're not testing on $csv_fh to prevent closing
683
    # STDOUT.
684
    close $csv_fh;
685
}
686
687
if ( defined $htmlfilename ) {
688
  print $fh "</body>\n";
689
  print $fh "</html>\n";
690
  close $fh;
691
} elsif ( defined $text_filename ) {
692
  close $fh;
693
}
694
695
=head1 INTERNAL METHODS
696
697
These methods are internal to the operation of holds_reminder.pl.
698
699
=head2 prepare_letter_for_printing
700
701
returns a string of text appropriate for printing in the event that an
702
holds reminder notice will not be sent to the patron's email
703
address. Depending on the desired output format, this may be a CSV
704
string, or a human-readable representation of the notice.
705
706
required parameters:
707
  letter
708
  borrowernumber
709
710
optional parameters:
711
  outputformat
712
713
=cut
714
715
sub prepare_letter_for_printing {
716
    my $params = shift;
717
718
    return unless ref $params eq 'HASH';
719
720
    foreach my $required_parameter (qw( letter borrowernumber )) {
721
        return unless defined $params->{$required_parameter};
722
    }
723
724
    my $return;
725
    chomp $params->{titles};
726
    if ( exists $params->{'outputformat'} && $params->{'outputformat'} eq 'csv' ) {
727
        if ($csv->combine(
728
                $params->{'firstname'}, $params->{'lastname'}, $params->{'address1'},  $params->{'address2'}, $params->{'postcode'},
729
                $params->{'city'}, $params->{'country'}, $params->{'email'}, $params->{'phone'}, $params->{'cardnumber'},
730
                $params->{'itemcount'}, $params->{'titles'}, $params->{'branchname'}
731
            )
732
          ) {
733
            return $csv->string, "\n";
734
        } else {
735
            $verbose and warn 'combine failed on argument: ' . $csv->error_input;
736
        }
737
    } elsif ( exists $params->{'outputformat'} && $params->{'outputformat'} eq 'html' ) {
738
      $return = "<pre>\n";
739
      $return .= "$params->{'letter'}->{'content'}\n";
740
      $return .= "\n</pre>\n";
741
    } else {
742
        $return .= "$params->{'letter'}->{'content'}\n";
743
744
    }
745
    return $return;
746
}
747

Return to bug 15986