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

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

Return to bug 15986