Kyle M Hall
47e4f3ed84
There is a flaw in C4::Members::Messaging::GetMessagingPreferences where the system assumes that every transport will use the same letter. This is not necessarily true. Even with the default preferences of just 'email' and 'sms', we should be able to have different letters for each, as one has a maximum character length ( sms ) and one does not. GetMessagingPreferences currently uses the letter code of the last result of its query as the letter code for every transport type. The returned data is a hashref with a key 'transport_types' that is an array of transport_types this borrower has selected for the given alert. This commit modifies GetMessagingPreferences such that the the 'transport_types' array is now a hash where the name of the transport type is now a key to the value of the letter code set for that transport type. It also modifies code calling GetMessagingPreferences where necessary, and as a side benefit will correctly get the letter codes for email and sms correctly, if they are defined differently. http://bugs.koha-community.org/show_bug.cgi?id=4246 Signed-off-by: Nicole C. Engard <nengard@bywatersolutions.com> In use in production by two libraries: Middletown and Washoe who give their sign off but don't have git to do so. Signed-off-by: Paul Poulain <paul.poulain@biblibre.com>
358 lines
15 KiB
Perl
Executable file
358 lines
15 KiB
Perl
Executable file
#!/usr/bin/perl
|
|
|
|
# Copyright 2008 LibLime
|
|
#
|
|
# This file is part of Koha.
|
|
#
|
|
# Koha is free software; you can redistribute it and/or modify it under the
|
|
# terms of the GNU General Public License as published by the Free Software
|
|
# Foundation; either version 2 of the License, or (at your option) any later
|
|
# version.
|
|
#
|
|
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# with Koha; if not, write to the Free Software Foundation, Inc.,
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
=head1 NAME
|
|
|
|
advance_notices.pl - cron script to put item due reminders into message queue
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
./advance_notices.pl -c
|
|
|
|
or, in crontab:
|
|
0 1 * * * advance_notices.pl -c
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This script prepares pre-due and item due reminders to be sent to
|
|
patrons. It queues them in the message queue, which is processed by
|
|
the process_message_queue.pl cronjob. The type and timing of the
|
|
messages can be configured by the patrons in their "My Alerts" tab in
|
|
the OPAC.
|
|
|
|
=cut
|
|
|
|
use strict;
|
|
use warnings;
|
|
use Getopt::Long;
|
|
use Data::Dumper;
|
|
BEGIN {
|
|
# find Koha's Perl modules
|
|
# test carefully before changing this
|
|
use FindBin;
|
|
eval { require "$FindBin::Bin/../kohalib.pl" };
|
|
}
|
|
use C4::Biblio;
|
|
use C4::Context;
|
|
use C4::Letters;
|
|
use C4::Members;
|
|
use C4::Members::Messaging;
|
|
use C4::Overdues;
|
|
use Koha::DateUtils;
|
|
|
|
|
|
# These are defaults for command line options.
|
|
my $confirm; # -c: Confirm that the user has read and configured this script.
|
|
# my $confirm = 1; # -c: Confirm that the user has read and configured this script.
|
|
my $nomail; # -n: No mail. Will not send any emails.
|
|
my $mindays = 0; # -m: Maximum number of days in advance to send notices
|
|
my $maxdays = 30; # -e: the End of the time period
|
|
my $verbose = 0; # -v: verbose
|
|
my $itemscontent = join(',',qw( issuedate title barcode author ));
|
|
|
|
GetOptions( 'c' => \$confirm,
|
|
'n' => \$nomail,
|
|
'm:i' => \$maxdays,
|
|
'v' => \$verbose,
|
|
'itemscontent=s' => \$itemscontent,
|
|
);
|
|
my $usage = << 'ENDUSAGE';
|
|
|
|
This script prepares pre-due and item due reminders to be sent to
|
|
patrons. It queues them in the message queue, which is processed by
|
|
the process_message_queue.pl cronjob.
|
|
See the comments in the script for directions on changing the script.
|
|
This script has the following parameters :
|
|
-c Confirm and remove this help & warning
|
|
-m maximum number of days in advance to send advance notices.
|
|
-n send No mail. Instead, all mail messages are printed on screen. Usefull for testing purposes.
|
|
-v verbose
|
|
ENDUSAGE
|
|
|
|
# Since advance notice options are not visible in the web-interface
|
|
# unless EnhancedMessagingPreferences is on, let the user know that
|
|
# this script probably isn't going to do much
|
|
if ( ! C4::Context->preference('EnhancedMessagingPreferences') ) {
|
|
warn <<'END_WARN';
|
|
|
|
The "EnhancedMessagingPreferences" syspref is off.
|
|
Therefore, it is unlikely that this script will actually produce any messages to be sent.
|
|
To change this, edit the "EnhancedMessagingPreferences" syspref.
|
|
|
|
END_WARN
|
|
}
|
|
|
|
unless ($confirm) {
|
|
print $usage;
|
|
print "Do you wish to continue? (y/n)";
|
|
chomp($_ = <STDIN>);
|
|
exit unless (/^y/i);
|
|
|
|
}
|
|
|
|
# The fields that will be substituted into <<items.content>>
|
|
my @item_content_fields = split(/,/,$itemscontent);
|
|
|
|
warn 'getting upcoming due issues' if $verbose;
|
|
my $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => $maxdays } );
|
|
warn 'found ' . scalar( @$upcoming_dues ) . ' issues' if $verbose;
|
|
|
|
# hash of borrowernumber to number of items upcoming
|
|
# for patrons wishing digests only.
|
|
my $upcoming_digest;
|
|
my $due_digest;
|
|
|
|
my $dbh = C4::Context->dbh();
|
|
my $sth = $dbh->prepare(<<'END_SQL');
|
|
SELECT biblio.*, items.*, issues.*
|
|
FROM issues,items,biblio
|
|
WHERE items.itemnumber=issues.itemnumber
|
|
AND biblio.biblionumber=items.biblionumber
|
|
AND issues.borrowernumber = ?
|
|
AND issues.itemnumber = ?
|
|
AND (TO_DAYS(date_due)-TO_DAYS(NOW()) = ?)
|
|
END_SQL
|
|
|
|
my $admin_adress = C4::Context->preference('KohaAdminEmailAddress');
|
|
|
|
UPCOMINGITEM: foreach my $upcoming ( @$upcoming_dues ) {
|
|
warn 'examining ' . $upcoming->{'itemnumber'} . ' upcoming due items' if $verbose;
|
|
# warn( Data::Dumper->Dump( [ $upcoming ], [ 'overdue' ] ) );
|
|
|
|
my $from_address = $upcoming->{branchemail} || $admin_adress;
|
|
|
|
my $letter;
|
|
my $borrower_preferences;
|
|
if ( 0 == $upcoming->{'days_until_due'} ) {
|
|
# This item is due today. Send an 'item due' message.
|
|
$borrower_preferences = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $upcoming->{'borrowernumber'},
|
|
message_name => 'item_due' } );
|
|
# warn( Data::Dumper->Dump( [ $borrower_preferences ], [ 'borrower_preferences' ] ) );
|
|
next unless $borrower_preferences;
|
|
|
|
if ( $borrower_preferences->{'wants_digest'} ) {
|
|
# cache this one to process after we've run through all of the items.
|
|
my $digest = $due_digest->{$upcoming->{'borrowernumber'}} ||= {};
|
|
$digest->{email} ||= $from_address;
|
|
$digest->{count}++;
|
|
} else {
|
|
my $biblio = C4::Biblio::GetBiblioFromItemNumber( $upcoming->{'itemnumber'} );
|
|
my $letter_type = 'DUE';
|
|
$sth->execute($upcoming->{'borrowernumber'},$upcoming->{'itemnumber'},'0');
|
|
my $titles = "";
|
|
while ( my $item_info = $sth->fetchrow_hashref()) {
|
|
my @item_info = map { $_ =~ /^date|date$/ ? format_date($item_info->{$_}) : $item_info->{$_} || '' } @item_content_fields;
|
|
$titles .= join("\t",@item_info) . "\n";
|
|
}
|
|
|
|
$letter = parse_letter( { letter_code => $letter_type,
|
|
borrowernumber => $upcoming->{'borrowernumber'},
|
|
branchcode => $upcoming->{'branchcode'},
|
|
biblionumber => $biblio->{'biblionumber'},
|
|
itemnumber => $upcoming->{'itemnumber'},
|
|
substitute => { 'items.content' => $titles }
|
|
} )
|
|
or die "no letter of type '$letter_type' found. Please see sample_notices.sql";
|
|
}
|
|
} else {
|
|
$borrower_preferences = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $upcoming->{'borrowernumber'},
|
|
message_name => 'advance_notice' } );
|
|
# warn( Data::Dumper->Dump( [ $borrower_preferences ], [ 'borrower_preferences' ] ) );
|
|
next UPCOMINGITEM unless $borrower_preferences && exists $borrower_preferences->{'days_in_advance'};
|
|
next UPCOMINGITEM unless $borrower_preferences->{'days_in_advance'} == $upcoming->{'days_until_due'};
|
|
|
|
if ( $borrower_preferences->{'wants_digest'} ) {
|
|
# cache this one to process after we've run through all of the items.
|
|
my $digest = $upcoming_digest->{$upcoming->{'borrowernumber'}} ||= {};
|
|
$digest->{email} ||= $from_address;
|
|
$digest->{count}++;
|
|
} else {
|
|
my $biblio = C4::Biblio::GetBiblioFromItemNumber( $upcoming->{'itemnumber'} );
|
|
my $letter_type = 'PREDUE';
|
|
$sth->execute($upcoming->{'borrowernumber'},$upcoming->{'itemnumber'},$borrower_preferences->{'days_in_advance'});
|
|
my $titles = "";
|
|
while ( my $item_info = $sth->fetchrow_hashref()) {
|
|
my @item_info = map { $_ =~ /^date|date$/ ? format_date($item_info->{$_}) : $item_info->{$_} || '' } @item_content_fields;
|
|
$titles .= join("\t",@item_info) . "\n";
|
|
}
|
|
|
|
$letter = parse_letter( { letter_code => $letter_type,
|
|
borrowernumber => $upcoming->{'borrowernumber'},
|
|
branchcode => $upcoming->{'branchcode'},
|
|
biblionumber => $biblio->{'biblionumber'},
|
|
itemnumber => $upcoming->{'itemnumber'},
|
|
substitute => { 'items.content' => $titles }
|
|
} )
|
|
or die "no letter of type '$letter_type' found. Please see sample_notices.sql";
|
|
}
|
|
}
|
|
|
|
# If we have prepared a letter, send it.
|
|
if ($letter) {
|
|
if ($nomail) {
|
|
local $, = "\f";
|
|
print $letter->{'content'};
|
|
}
|
|
else {
|
|
foreach my $transport ( keys %{$borrower_preferences->{'transports'}} ) {
|
|
C4::Letters::EnqueueLetter( { letter => $letter,
|
|
borrowernumber => $upcoming->{'borrowernumber'},
|
|
from_address => $from_address,
|
|
message_transport_type => $transport } );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# warn( Data::Dumper->Dump( [ $upcoming_digest ], [ 'upcoming_digest' ] ) );
|
|
|
|
# Now, run through all the people that want digests and send them
|
|
|
|
$sth = $dbh->prepare(<<'END_SQL');
|
|
SELECT biblio.*, items.*, issues.*
|
|
FROM issues,items,biblio
|
|
WHERE items.itemnumber=issues.itemnumber
|
|
AND biblio.biblionumber=items.biblionumber
|
|
AND issues.borrowernumber = ?
|
|
AND (TO_DAYS(date_due)-TO_DAYS(NOW()) = ?)
|
|
END_SQL
|
|
|
|
PATRON: while ( my ( $borrowernumber, $digest ) = each %$upcoming_digest ) {
|
|
my $count = $digest->{count};
|
|
my $from_address = $digest->{email};
|
|
|
|
my $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $borrowernumber,
|
|
message_name => 'advance_notice' } );
|
|
# warn( Data::Dumper->Dump( [ $borrower_preferences ], [ 'borrower_preferences' ] ) );
|
|
next PATRON unless $borrower_preferences; # how could this happen?
|
|
|
|
|
|
my $letter_type = 'PREDUEDGST';
|
|
|
|
$sth->execute($borrowernumber,$borrower_preferences->{'days_in_advance'});
|
|
my $titles = "";
|
|
while ( my $item_info = $sth->fetchrow_hashref()) {
|
|
my @item_info = map { $_ =~ /^date|date$/ ? format_date($item_info->{$_}) : $item_info->{$_} || '' } @item_content_fields;
|
|
$titles .= join("\t",@item_info) . "\n";
|
|
}
|
|
my $letter = parse_letter( { letter_code => $letter_type,
|
|
borrowernumber => $borrowernumber,
|
|
substitute => { count => $count,
|
|
'items.content' => $titles
|
|
}
|
|
} )
|
|
or die "no letter of type '$letter_type' found. Please see sample_notices.sql";
|
|
if ($nomail) {
|
|
local $, = "\f";
|
|
print $letter->{'content'};
|
|
}
|
|
else {
|
|
foreach my $transport ( keys %{$borrower_preferences->{'transports'}} ) {
|
|
C4::Letters::EnqueueLetter( { letter => $letter,
|
|
borrowernumber => $borrowernumber,
|
|
from_address => $from_address,
|
|
message_transport_type => $transport } );
|
|
}
|
|
}
|
|
}
|
|
|
|
# Now, run through all the people that want digests and send them
|
|
PATRON: while ( my ( $borrowernumber, $digest ) = each %$due_digest ) {
|
|
my $count = $digest->{count};
|
|
my $from_address = $digest->{email};
|
|
|
|
my $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $borrowernumber,
|
|
message_name => 'item_due' } );
|
|
# warn( Data::Dumper->Dump( [ $borrower_preferences ], [ 'borrower_preferences' ] ) );
|
|
next PATRON unless $borrower_preferences; # how could this happen?
|
|
|
|
my $letter_type = 'DUEDGST';
|
|
$sth->execute($borrowernumber,'0');
|
|
my $titles = "";
|
|
while ( my $item_info = $sth->fetchrow_hashref()) {
|
|
my @item_info = map { $_ =~ /^date|date$/ ? format_date($item_info->{$_}) : $item_info->{$_} || '' } @item_content_fields;
|
|
$titles .= join("\t",@item_info) . "\n";
|
|
}
|
|
my $letter = parse_letter( { letter_code => $letter_type,
|
|
borrowernumber => $borrowernumber,
|
|
substitute => { count => $count,
|
|
'items.content' => $titles
|
|
}
|
|
} )
|
|
or die "no letter of type '$letter_type' found. Please see sample_notices.sql";
|
|
|
|
if ($nomail) {
|
|
local $, = "\f";
|
|
print $letter->{'content'};
|
|
}
|
|
else {
|
|
foreach my $transport ( keys %{$borrower_preferences->{'transports'}} ) {
|
|
C4::Letters::EnqueueLetter( { letter => $letter,
|
|
borrowernumber => $borrowernumber,
|
|
from_address => $from_address,
|
|
message_transport_type => $transport } );
|
|
}
|
|
}
|
|
}
|
|
|
|
=head1 METHODS
|
|
|
|
=head2 parse_letter
|
|
|
|
=cut
|
|
|
|
sub parse_letter {
|
|
my $params = shift;
|
|
foreach my $required ( qw( letter_code borrowernumber ) ) {
|
|
return unless exists $params->{$required};
|
|
}
|
|
|
|
my %table_params = ( 'borrowers' => $params->{'borrowernumber'} );
|
|
|
|
if ( my $p = $params->{'branchcode'} ) {
|
|
$table_params{'branches'} = $p;
|
|
}
|
|
if ( my $p = $params->{'itemnumber'} ) {
|
|
$table_params{'issues'} = $p;
|
|
$table_params{'items'} = $p;
|
|
}
|
|
if ( my $p = $params->{'biblionumber'} ) {
|
|
$table_params{'biblio'} = $p;
|
|
$table_params{'biblioitems'} = $p;
|
|
}
|
|
|
|
return C4::Letters::GetPreparedLetter (
|
|
module => 'circulation',
|
|
letter_code => $params->{'letter_code'},
|
|
branchcode => $table_params{'branches'},
|
|
substitute => $params->{'substitute'},
|
|
tables => \%table_params,
|
|
);
|
|
}
|
|
|
|
sub format_date {
|
|
my $date_string = shift;
|
|
my $dt=dt_from_string($date_string);
|
|
return output_pref($dt);
|
|
}
|
|
|
|
1;
|
|
|
|
__END__
|