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

(-)a/misc/cronjobs/courtesy_notices.pl (-1 / +422 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# Copyright 2008 LibLime
4
# Copyright 2015 CINECA
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
=head1 NAME
22
23
courtesy_notices.pl - cron script to put reminders about due date of loans into message queue
24
25
=head1 SYNOPSIS
26
27
./courtesy_notices.pl -c
28
29
or, in crontab:
30
0 1 * * * courtesy_notices.pl -c
31
32
=head1 DESCRIPTION
33
34
This script prepares pre-due digest reminders to be sent to
35
patrons. It queues them in the message queue, which is processed by
36
the process_message_queue.pl cronjob. You can configure the script about:
37
-- Days before due date (default 7)
38
-- Limit notices about specific libraris (default all)
39
-- Name of the letter (default 'PREDUEDGST')
40
-- Fields to describe loans (default due date,title,author,barcode)
41
42
To be used with EnhancedMessagingPreferences = 'OFF'
43
44
=cut
45
46
use strict;
47
use warnings;
48
use Getopt::Long;
49
use Pod::Usage;
50
use Data::Dumper;
51
BEGIN {
52
    # find Koha's Perl modules
53
    # test carefully before changing this
54
    use FindBin;
55
    eval { require "$FindBin::Bin/../kohalib.pl" };
56
}
57
use C4::Biblio;
58
use C4::Context;
59
use C4::Letters;
60
use C4::Members;
61
use C4::Members::Messaging;
62
use C4::Overdues;
63
use Koha::DateUtils;
64
65
=head1 NAME
66
67
courtesy_notices.pl  - prepare messages to be sent to patrons for nearly due items
68
69
=head1 SYNOPSIS
70
71
courtesy_notices.pl
72
  [ -n ][ --days <number of days> ][ --itemscontent <comma separated field list> ]
73
  [ --libs <comma separated libraries codes list> ][ --notice <notice code> ][ -c ]
74
75
=head1 OPTIONS
76
77
=over 8
78
79
=item B<--help>
80
81
Print a brief help message and exits.
82
83
=item B<--man>
84
85
Prints the manual page and exits.
86
87
=item B<-v>
88
89
Verbose. Without this flag set, only fatal errors are reported.
90
91
=item B<-n>
92
93
Do not send any email. Courtesy notices that would have been sent to
94
the patrons are printed to standard out.
95
96
=item B<--days>
97
98
Defines exact day befoure due date to send courtesy notices.
99
Default value is 7.
100
101
=item B<--itemscontent>
102
103
Comma separated list of fields that get substituted into templates in
104
places of the E<lt>E<lt>items.contentE<gt>E<gt> placeholder. This
105
defaults to due date,title,author,barcode
106
107
Other possible values come from fields in the biblios, items and
108
issues tables.
109
110
=item B<--libs>
111
112
Comma separated list of libraries's codes. If used, only issues connect with listed
113
libraries create courtesy notices. If not used, the deafaul is that issues of all libraries
114
create courtesy notices
115
116
=item B<--notice>
117
118
The code of notice template used. The template need to be a digest.
119
Default value 'PREDUEDGST'
120
121
=item B<-c>
122
Confirm the setup. Without this flag the script does nothing and show the help 
123
124
=back
125
126
=head1 DESCRIPTION
127
128
This script is designed to alert patrons about coming due
129
130
=head2 Configuration
131
132
This script is to be used when EnhancedMessagingPreferences = 'OFF' and you want to
133
create courtesy notices with a setup at library level.
134
You can use the scripts options to setup it.
135
136
=head2 Outgoing emails
137
138
Messages are prepared for each patron with due items a specific day before due date.
139
140
These emails are staged in the outgoing message queue, as are messages
141
produced by other features of Koha. This message queue must be
142
processed regularly by the
143
F<misc/cronjobs/process_message_queue.pl> program.
144
145
In the event that the C<-n> flag is passed to this program, no emails
146
are sent. Instead, messages are sent on standard output from this
147
program. They may be redirected to a file if desired.
148
149
=head2 Templates
150
151
Templates can contain variables enclosed in double angle brackets like
152
E<lt>E<lt>thisE<gt>E<gt>. Those variables will be replaced with values
153
specific to the overdue items or relevant patron. Available variables
154
are:
155
156
=over
157
158
=item E<lt>E<lt>items.contentE<gt>E<gt>
159
160
one line for each item, each line containing a tab separated list of
161
title, author, barcode, issuedate
162
163
=item E<lt>E<lt>borrowers.*E<gt>E<gt>
164
165
any field from the borrowers table
166
167
=item E<lt>E<lt>branches.*E<gt>E<gt>
168
169
any field from the branches table
170
171
=back
172
173
=head1 SEE ALSO
174
175
The F<misc/cronjobs/overdue_notices.pl> program allows you to send
176
messages to patrons when their messages are overdue.
177
=cut
178
179
# These are defaults for command line options.
180
my $confirm;                                                       # -c: Confirm that the user has read and configured this script.
181
my $nomail;                                                        # -n: No mail. Will not send any emails.
182
my $day_all_pre   = 7;                                             # --days: When to send (days befoure).
183
my $verbose       = 0;                                             # -v: Verbose.
184
my $notice_format = 'PREDUEDGST';                                  # --notice: The code of the notice.
185
my $libraries;                                                     # --libs: Codes of libraries to select issues.
186
my $itemscontent = join(',',qw( date_due title author barcode ));
187
my $help    = 0;
188
my $man     = 0;
189
190
my $send_all_pre = 'Y';
191
my $send_all_format = 'digest';
192
193
GetOptions(
194
            'help|?'            => \$help,
195
            'man'               => \$man,
196
            'c'                 => \$confirm,
197
            'n'                 => \$nomail,
198
            'days:i'            => \$day_all_pre,
199
            'v'                 => \$verbose,
200
            'itemscontent=s'    => \$itemscontent,
201
            'libs=s'            => \$libraries,
202
            'notice=s'          => \$notice_format,
203
       )or pod2usage(2);
204
pod2usage(1) if $help;
205
pod2usage( -verbose => 2 ) if $man;;
206
207
#Stop if EnhancedMessagingPreferences is ON
208
if ( C4::Context->preference('EnhancedMessagingPreferences') ) {
209
    warn <<'END_WARN';
210
211
The "EnhancedMessagingPreferences" syspref is ON.
212
This script is developed to be use when "EnhancedMessagingPreferences" syspref is OFF.
213
The script stop here. It do nothing.
214
215
END_WARN
216
}
217
unless ($confirm) {
218
     pod2usage(1);
219
}
220
221
#Check on apostrophe in libs
222
if ($libraries =~ m/'/){
223
    warn <<'END_WARN';
224
Not supported apostrophe on librabry codes.
225
Script stopped.
226
END_WARN
227
   exit;
228
}
229
#Setup the string to use in the 'IN' clause of MySQL
230
if ($libraries) {
231
   $libraries = "'$libraries'";
232
   $libraries=~ s/,/','/g;
233
}
234
235
236
237
# The fields that will be substituted into <<items.content>>
238
my @item_content_fields = split(/,/,$itemscontent);
239
my $upcoming_dues ='';
240
warn 'getting upcoming due issues' if $verbose;
241
242
if ($send_all_pre eq 'Y')  {
243
    $upcoming_dues = SpecificDayComingDueIssues( { days_in_advance => $day_all_pre, libs =>$libraries } );
244
}else{
245
    warn <<'END_WARN';
246
Config not supported
247
END_WARN
248
   exit;
249
}
250
251
warn 'found ' . scalar( @$upcoming_dues ) . ' issues' if $verbose;
252
253
# Force digest and letter format
254
my $upcoming_digest;
255
my $due_digest;
256
257
my $admin_adress = C4::Context->preference('KohaAdminEmailAddress');
258
259
my $borrower_preferences = {};
260
$borrower_preferences->{'transports'} = {'email' => $notice_format};
261
$borrower_preferences->{'wants_digest'} = 'Y';
262
263
foreach my $upcoming ( @$upcoming_dues ) {
264
    my $from_address = $upcoming->{branchemail} || $admin_adress;
265
    
266
    my $digest = $upcoming_digest->{$upcoming->{'borrowernumber'}} ||= {};
267
    $digest->{email} ||= $from_address;
268
    $digest->{count}++;
269
}
270
271
272
# Now, run through all the people, forcing digest and send them
273
my $dbh = C4::Context->dbh();
274
275
my $sel_issues = '';
276
if ($libraries) { $sel_issues ='AND issues.branchcode IN ('.$libraries.')'};
277
278
my $sth = $dbh->prepare( qq{
279
SELECT biblio.*, items.*, issues.*
280
  FROM issues,items,biblio
281
  WHERE items.itemnumber=issues.itemnumber
282
    AND biblio.biblionumber=items.biblionumber
283
    AND issues.borrowernumber = ?
284
    $sel_issues
285
    AND (TO_DAYS(date_due)-TO_DAYS(NOW()) = ?)
286
});
287
288
PATRON: while ( my ( $borrowernumber, $digest ) = each  %$upcoming_digest ) {
289
    my $count = $digest->{count};
290
    my $from_address = $digest->{email};
291
    my $letter_type = $notice_format;
292
293
    $sth->execute($borrowernumber,$day_all_pre);
294
    my $titles = "";
295
    while ( my $item_info = $sth->fetchrow_hashref()) {
296
      my @item_info = map { $_ =~ /^date|date$/ ? format_date($item_info->{$_}) : $item_info->{$_} || '' } @item_content_fields;
297
      $titles .= join("\t",@item_info) . "\n";
298
    }
299
300
    ## Get branch info for borrowers home library.
301
    my %branch_info = get_branch_info( $borrowernumber );
302
303
    my $letter = parse_letter( { letter_code    => $letter_type,
304
                              borrowernumber => $borrowernumber,
305
                              substitute     => { count => $count,
306
                                                  'items.content' => $titles,
307
                                                  %branch_info,
308
                                                }
309
                         } )
310
      or die "no letter of type '$letter_type' found. Please see sample_notices.sql";
311
    if ($nomail) {
312
      local $, = "\f";
313
      print $letter->{'content'};
314
    }
315
    else {
316
      foreach my $transport ( keys %{$borrower_preferences->{'transports'}} ) {
317
        C4::Letters::EnqueueLetter( { letter                 => $letter,
318
                                      borrowernumber         => $borrowernumber,
319
                                      from_address           => $from_address,
320
                                      message_transport_type => $transport } );
321
      }
322
    }
323
}
324
325
326
=head1 METHODS
327
328
=head2 parse_letter
329
330
=cut
331
332
sub parse_letter {
333
    my $params = shift;
334
    foreach my $required ( qw( letter_code borrowernumber ) ) {
335
        return unless exists $params->{$required};
336
    }
337
338
    my %table_params = ( 'borrowers' => $params->{'borrowernumber'} );
339
340
    if ( my $p = $params->{'branchcode'} ) {
341
        $table_params{'branches'} = $p;
342
    }
343
    if ( my $p = $params->{'itemnumber'} ) {
344
        $table_params{'issues'} = $p;
345
        $table_params{'items'} = $p;
346
    }
347
    if ( my $p = $params->{'biblionumber'} ) {
348
        $table_params{'biblio'} = $p;
349
        $table_params{'biblioitems'} = $p;
350
    }
351
352
    return C4::Letters::GetPreparedLetter (
353
        module => 'circulation',
354
        letter_code => $params->{'letter_code'},
355
        branchcode => $table_params{'branches'},
356
        substitute => $params->{'substitute'},
357
        tables     => \%table_params,
358
    );
359
}
360
361
sub format_date {
362
    my $date_string = shift;
363
    my $dt=dt_from_string($date_string);
364
    return output_pref($dt);
365
}
366
367
=head2 get_branch_info
368
369
=cut
370
371
sub get_branch_info {
372
    my ( $borrowernumber ) = @_;
373
374
    ## Get branch info for borrowers home library.
375
    my $borrower_details = C4::Members::GetMember( borrowernumber => $borrowernumber );
376
    my $borrower_branchcode = $borrower_details->{'branchcode'};
377
    my $branch = C4::Branch::GetBranchDetail( $borrower_branchcode );
378
    my %branch_info;
379
    foreach my $key( keys %$branch ) {
380
        $branch_info{"branches.$key"} = $branch->{$key};
381
    }
382
383
    return %branch_info;
384
}
385
386
387
sub SpecificDayComingDueIssues {
388
    my $params = shift;
389
    my $sel_issues = 'returndate is NULL';
390
    
391
    $params->{'days_in_advance'} = 7 unless exists $params->{'days_in_advance'};
392
    if ($params->{'libs'}) {
393
        $sel_issues ='returndate is NULL and issues.branchcode IN ('.$params->{'libs'}.') ';
394
    }else{
395
        $sel_issues = 'returndate is NULL';
396
    }
397
    
398
    my $dbh = C4::Context->dbh;
399
 
400
    my $statement = <<"END_SQL";
401
SELECT issues.*, items.itype as itemtype, items.homebranch, TO_DAYS( date_due )-TO_DAYS( NOW() ) as days_until_due, branches.branchemail
402
FROM issues
403
LEFT JOIN items USING (itemnumber)
404
LEFT OUTER JOIN branches USING (branchcode)
405
WHERE $sel_issues
406
HAVING days_until_due > 0 AND days_until_due = ?
407
END_SQL
408
 
409
    my @bind_parameters = ( $params->{'days_in_advance'} );
410
 
411
    my $sth = $dbh->prepare( $statement );
412
    $sth->execute( @bind_parameters );
413
    my $day_upcoming_dues = $sth->fetchall_arrayref({});
414
    $sth->finish;
415
 
416
    return $day_upcoming_dues;
417
}
418
419
420
1;
421
422
__END__

Return to bug 14519