From 7018c9bd707cbc5f783730e49d80bfc3e8c70247 Mon Sep 17 00:00:00 2001
From: Andrew Isherwood
Date: Wed, 8 May 2019 10:42:22 +0100
Subject: [PATCH] Bug 22818: Add generation and sending of notices
This patch adds the ability for ILL to send notices, both triggered by
staff and triggered by events.
Staff can trigger notices to patrons from the "Manage ILL request" screen:
- ILL request ready for pickup
- ILL request unavailable
- Place request with partners
The following notices to staff are triggered automatically:
- Request has been modified by patron
- Request has been cancelled by patron
Branches can now specify an "ILL email" address to which notices
intended to inform staff of changes to requests by patrons can be sent.
The sending of notices is controlled by a few new sysprefs:
- "ILLDefaultStaffEmail" - Fallback email address for staff ILL notices
to be sent to in the absence of a branch address
- "ILLSendStaffNotices" - To specify which staff notices should be sent
automatically when requests are manipulated by patrons
Patron notices are also controlled by the patron's messaging
preferences
---
Koha/Illrequest.pm | 351 ++++++++++++++++-----
Koha/Illrequest/Logger.pm | 41 ++-
ill/ill-requests.pl | 36 ++-
.../prog/en/modules/ill/ill-requests.tt | 39 +++
.../prog/en/modules/ill/log/patron_notice.tt | 6 +
opac/opac-illrequests.pl | 2 +
6 files changed, 402 insertions(+), 73 deletions(-)
create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/ill/log/patron_notice.tt
diff --git a/Koha/Illrequest.pm b/Koha/Illrequest.pm
index 14c4ce7c91..5775308899 100644
--- a/Koha/Illrequest.pm
+++ b/Koha/Illrequest.pm
@@ -27,6 +27,8 @@ use Mail::Sendmail;
use Try::Tiny;
use DateTime;
+use C4::Letters;
+use C4::Members;
use Koha::Database;
use Koha::Email;
use Koha::Exceptions::Ill;
@@ -243,6 +245,7 @@ sub status_alias {
Overloaded getter/setter for request status,
also nullifies status_alias and records the fact that the status has changed
+and sends a notice if appropriate
=cut
@@ -276,6 +279,10 @@ sub status {
});
}
delete $self->{previous_status};
+ # If status has changed to cancellation requested, send a notice
+ if ($new_status eq 'CANCREQ') {
+ $self->send_staff_notice('ILL_REQUEST_CANCEL');
+ }
return $ret;
} else {
return $current_status;
@@ -1039,38 +1046,12 @@ sub generic_confirm {
my $branch = Koha::Libraries->find($params->{current_branchcode})
|| die "Invalid current branchcode. Are you logged in as the database user?";
if ( !$params->{stage}|| $params->{stage} eq 'init' ) {
- my $draft->{subject} = "ILL Request";
- $draft->{body} = <metadata;
- while (my ($title, $value) = each %{$details}) {
- $draft->{body} .= " - " . $title . ": " . $value . "\n"
- if $value;
- }
- $draft->{body} .= <$_ }
- qw/ branchname branchaddress1 branchaddress2 branchaddress3
- branchzip branchcity branchstate branchcountry branchphone
- branchillemail branchemail /;
- my $address = "";
- foreach my $line ( @address ) {
- $address .= $line . "\n" if $line;
- }
-
- $draft->{body} .= $address;
+ # Get the message body from the notice definition
+ my $letter = $self->get_notice({
+ notice_code => 'ILL_PARTNER_REQ',
+ transport => 'email'
+ });
my $partners = Koha::Patrons->search({
categorycode => $self->_config->partner_code
@@ -1082,7 +1063,10 @@ EOF
method => 'generic_confirm',
stage => 'draft',
value => {
- draft => $draft,
+ draft => {
+ subject => $letter->{title},
+ body => $letter->{content}
+ },
partners => $partners,
}
};
@@ -1098,57 +1082,282 @@ EOF
"No target email addresses found. Either select at least one partner or check your ILL partner library records.")
if ( !$to );
# Create the from, replyto and sender headers
- my $from = $branch->branchemail;
+ my $from = $branch->branchillemail || $branch->branchemail;
my $replyto = $branch->branchreplyto || $from;
Koha::Exceptions::Ill::NoLibraryEmail->throw(
"Your library has no usable email address. Please set it.")
if ( !$from );
- # Create the email
- my $message = Koha::Email->new;
- my %mail = $message->create_message_headers(
- {
- to => $to,
- from => $from,
- replyto => $replyto,
- subject => Encode::encode( "utf8", $params->{subject} ),
- message => Encode::encode( "utf8", $params->{body} ),
- contenttype => 'text/plain',
+ # So we get a notice hashref, then substitute the possibly
+ # modified title and body from the draft stage
+ my $letter = $self->get_notice({
+ notice_code => 'ILL_PARTNER_REQ',
+ transport => 'email'
+ });
+ $letter->{title} = $params->{subject};
+ $letter->{content} = $params->{body};
+
+ # Send the email
+ my $params = {
+ letter => $letter,
+ borrowernumber => $self->borrowernumber,
+ message_transport_type => 'email',
+ to_address => $to,
+ from_address => $from
+ };
+
+ if ($letter) {
+ my $result = C4::Letters::EnqueueLetter($params);
+ if ( $result ) {
+ $self->status("GENREQ")->store;
+ $self->_backend_capability(
+ 'set_requested_partners',
+ {
+ request => $self,
+ to => $to
+ }
+ );
+ return {
+ error => 0,
+ status => '',
+ message => '',
+ method => 'generic_confirm',
+ stage => 'commit',
+ next => 'illview',
+ };
}
- );
- # Send it
- my $result = sendmail(%mail);
- if ( $result ) {
- $self->status("GENREQ")->store;
- $self->_backend_capability(
- 'set_requested_partners',
- {
- request => $self,
- to => $to
- }
- );
- return {
- error => 0,
- status => '',
- message => '',
- method => 'generic_confirm',
- stage => 'commit',
- next => 'illview',
- };
- } else {
- return {
- error => 1,
- status => 'email_failed',
- message => $Mail::Sendmail::error,
- method => 'generic_confirm',
- stage => 'draft',
- };
}
+ return {
+ error => 1,
+ status => 'email_failed',
+ message => 'Email queueing failed',
+ method => 'generic_confirm',
+ stage => 'draft',
+ };
} else {
die "Unknown stage, should not have happened."
}
}
+=head3 get_staff_to_address
+
+ my $email = $request->get_staff_to_address();
+
+Get the email address to which staff notices should be sent
+
+=cut
+
+sub get_staff_to_address {
+ my ( $self ) = @_;
+
+ # The various places we can get an ILL staff email address from
+ # (In order of preference)
+ #
+ # Dedicated branch address
+ my $library = Koha::Libraries->find( $self->branchcode );
+ my $branch_ill_to = $library->branchillemail;
+ # General purpose ILL address from syspref
+ my $syspref = C4::Context->preference("ILLDefaultStaffEmail");
+ # Branch general email address
+ my $branch_to = $library->branchemail;
+ # Last resort
+ my $koha_admin = C4::Context->preference('KohaAdminEmailAddress');
+
+ my $to;
+ if ($branch_ill_to) {
+ $to = $branch_ill_to;
+ } elsif ($syspref) {
+ $to = $syspref;
+ } elsif ($branch_to) {
+ $to = $branch_to;
+ } elsif ($koha_admin) {
+ $to = $koha_admin;
+ }
+
+ # $to will not be defined if we didn't find a usable address
+ return $to;
+}
+
+=head3 send_patron_notice
+
+ my $result = $request->send_patron_notice($notice_code);
+
+Send a specified notice regarding this request to a patron
+
+=cut
+
+sub send_patron_notice {
+ my ( $self, $notice_code ) = @_;
+
+ # We need a notice code
+ if (!$notice_code) {
+ return {
+ error => 'notice_no_type'
+ };
+ }
+
+ # Map from the notice code to the messaging preference
+ my %message_name = (
+ ILL_PICKUP_READY => 'Ill_ready',
+ ILL_REQUEST_UNAVAIL => 'Ill_unavailable'
+ );
+
+ # Get the patron's messaging preferences
+ my $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences({
+ borrowernumber => $self->borrowernumber,
+ message_name => $message_name{$notice_code}
+ });
+ my @transports = keys %{ $borrower_preferences->{transports} };
+
+ # Send the notice to the patron via the chosen transport methods
+ # and record the results
+ my @success = ();
+ my @fail = ();
+ for my $transport (@transports) {
+ my $letter = $self->get_notice({
+ notice_code => $notice_code,
+ transport => $transport
+ });
+ if ($letter) {
+ my $result = C4::Letters::EnqueueLetter({
+ letter => $letter,
+ borrowernumber => $self->borrowernumber,
+ message_transport_type => $transport,
+ });
+ if ($result) {
+ push @success, $transport;
+ } else {
+ push @fail, $transport;
+ }
+ } else {
+ push @fail, $transport;
+ }
+ }
+ if (scalar @success > 0) {
+ my $logger = Koha::Illrequest::Logger->new;
+ $logger->log_patron_notice({
+ request => $self,
+ notice_code => $notice_code
+ });
+ }
+ return {
+ result => {
+ success => \@success,
+ fail => \@fail
+ }
+ };
+}
+
+=head3 send_staff_notice
+
+ my $result = $request->send_staff_notice($notice_code);
+
+Send a specified notice regarding this request to staff
+
+=cut
+
+sub send_staff_notice {
+ my ( $self, $notice_code ) = @_;
+
+ # We need a notice code
+ if (!$notice_code) {
+ return {
+ error => 'notice_no_type'
+ };
+ }
+
+ # Get the staff notices that have been assigned for sending in
+ # the syspref
+ my $staff_to_send = C4::Context->preference('ILLSendStaffNotices');
+
+ # If it hasn't been enabled in the syspref, we don't want to send it
+ if ($staff_to_send !~ /\b$notice_code\b/) {
+ return {
+ error => 'notice_not_enabled'
+ };
+ }
+
+ my $letter = $self->get_notice({
+ notice_code => $notice_code,
+ transport => 'email'
+ });
+
+ # Try and get an address to which to send staff notices
+ my $to_address = scalar $self->get_staff_to_address;
+
+ my $params = {
+ letter => $letter,
+ borrowernumber => $self->borrowernumber,
+ message_transport_type => 'email',
+ };
+
+ if ($to_address) {
+ $params->{to_address} = $to_address;
+ $params->{from_address} = $to_address;
+ } else {
+ return {
+ error => 'notice_no_create'
+ };
+ }
+
+ if ($letter) {
+ C4::Letters::EnqueueLetter($params)
+ or warn "can't enqueue letter $letter";
+ return {
+ success => 'notice_queued'
+ };
+ } else {
+ return {
+ error => 'notice_no_create'
+ };
+ }
+}
+
+=head3 get_notice
+
+ my $notice = $request->get_notice($params);
+
+Return a compiled notice hashref for the passed notice code
+and transport type
+
+=cut
+
+sub get_notice {
+ my ( $self, $params ) = @_;
+
+ my $title = $self->illrequestattributes->find(
+ { type => 'title' }
+ );
+ my $author = $self->illrequestattributes->find(
+ { type => 'author' }
+ );
+ my $metahash = $self->metadata;
+ my @metaarray = ();
+ while (my($key, $value) = each %{$metahash}) {
+ push @metaarray, "- $key: $value" if $value;
+ }
+ my $metastring = join("\n", @metaarray);
+ my $letter = C4::Letters::GetPreparedLetter(
+ module => 'ill',
+ letter_code => $params->{notice_code},
+ message_transport_type => $params->{transport},
+ lang => $self->patron->lang,
+ tables => {
+ illrequests => $self->illrequest_id,
+ borrowers => $self->borrowernumber,
+ biblio => $self->biblio_id,
+ branches => $self->branchcode,
+ },
+ substitute => {
+ ill_bib_title => $title ? $title->value : 'N/A',
+ ill_bib_author => $author ? $author->value : 'N/A',
+ ill_full_metadata => $metastring
+ }
+ );
+
+ return $letter;
+}
+
=head3 id_prefix
my $prefix = $record->id_prefix;
diff --git a/Koha/Illrequest/Logger.pm b/Koha/Illrequest/Logger.pm
index 0625370237..0faa134ec9 100644
--- a/Koha/Illrequest/Logger.pm
+++ b/Koha/Illrequest/Logger.pm
@@ -26,6 +26,7 @@ use C4::Context;
use C4::Templates;
use C4::Log qw( logaction );
use Koha::ActionLogs;
+use Koha::Notice::Template;
=head1 NAME
@@ -62,6 +63,9 @@ sub new {
$self->{loggers} = {
status => sub {
$self->log_status_change(@_);
+ },
+ patron_notice => sub {
+ $self->log_patron_notice(@_);
}
};
@@ -69,7 +73,8 @@ sub new {
C4::Templates::_get_template_file('ill/log/', 'intranet');
$self->{templates} = {
- STATUS_CHANGE => $base . 'status_change.tt'
+ STATUS_CHANGE => $base . 'status_change.tt',
+ PATRON_NOTICE => $base . 'patron_notice.tt'
};
bless $self, $class;
@@ -102,6 +107,31 @@ sub log_maybe {
}
}
+=head3 log_patron_notice
+
+ Koha::IllRequest::Logger->log_patron_notice($params);
+
+Receive a hashref containing a request object and params to log,
+and log it
+
+=cut
+
+sub log_patron_notice {
+ my ( $self, $params ) = @_;
+
+ if (defined $params->{request} && defined $params->{notice_code}) {
+ $self->log_something({
+ modulename => 'ILL',
+ actionname => 'PATRON_NOTICE',
+ objectnumber => $params->{request}->id,
+ infos => to_json({
+ log_origin => 'core',
+ notice_code => $params->{notice_code}
+ })
+ });
+ }
+}
+
=head3 log_status_change
Koha::IllRequest::Logger->log_status_change($params);
@@ -209,6 +239,14 @@ sub get_request_logs {
{ order_by => { -desc => "timestamp" } }
)->unblessed;
+ # Populate a lookup table for all ILL notice types
+ my $notice_types = Koha::Notice::Templates->search({
+ module => 'ill'
+ })->unblessed;
+ my $notice_hash;
+ foreach my $notice(@{$notice_types}) {
+ $notice_hash->{$notice->{code}} = $notice;
+ }
# Populate a lookup table for status aliases
my $aliases = GetAuthorisedValues('ILLSTATUS');
my $alias_hash;
@@ -216,6 +254,7 @@ sub get_request_logs {
$alias_hash->{$alias->{authorised_value}} = $alias;
}
foreach my $log(@{$logs}) {
+ $log->{notice_types} = $notice_hash;
$log->{aliases} = $alias_hash;
$log->{info} = from_json($log->{info});
$log->{template} = $self->get_log_template({
diff --git a/ill/ill-requests.pl b/ill/ill-requests.pl
index 010c61e618..e8137121f9 100755
--- a/ill/ill-requests.pl
+++ b/ill/ill-requests.pl
@@ -23,6 +23,7 @@ use CGI;
use C4::Auth;
use C4::Output;
+use Koha::Notice::Templates;
use Koha::AuthorisedValues;
use Koha::Illcomment;
use Koha::Illrequests;
@@ -70,12 +71,28 @@ if ( $backends_available ) {
# View the details of an ILL
my $request = Koha::Illrequests->find($params->{illrequest_id});
+ # Get the details for notices that can be sent from here
+ my $notices = Koha::Notice::Templates->search(
+ {
+ module => 'ill',
+ code => { -in => [ 'ILL_PICKUP_READY' ,'ILL_REQUEST_UNAVAIL' ] },
+ },
+ {
+ columns => [ qw/code name/ ],
+ distinct => 1
+ }
+ )->unblessed;
+
$template->param(
+ notices => $notices,
request => $request,
csrf_token => Koha::Token->new->generate_csrf({
session_id => scalar $cgi->cookie('CGISESSID'),
}),
- ( $params->{error} ? ( error => $params->{error} ) : () ),
+ ( $params->{tran_error} ?
+ ( tran_error => $params->{tran_error} ) : () ),
+ ( $params->{tran_success} ?
+ ( tran_success => $params->{tran_success} ) : () ),
);
} elsif ( $op eq 'create' ) {
@@ -306,6 +323,23 @@ if ( $backends_available ) {
);
exit;
+ } elsif ( $op eq "send_notice" ) {
+ my $illrequest_id = $params->{illrequest_id};
+ my $request = Koha::Illrequests->find($illrequest_id);
+ my $ret = $request->send_patron_notice($params->{notice_code});
+ my $append = '';
+ if ($ret->{result} && scalar @{$ret->{result}->{success}} > 0) {
+ $append .= '&tran_success=' . join(',', @{$ret->{result}->{success}});
+ }
+ if ($ret->{result} && scalar @{$ret->{result}->{fail}} > 0) {
+ $append .= '&tran_fail=' . join(',', @{$ret->{result}->{fail}}.join(','));
+ }
+ # Redirect to view the whole request
+ print $cgi->redirect(
+ "/cgi-bin/koha/ill/ill-requests.pl?method=illview&illrequest_id=".
+ scalar $params->{illrequest_id} . $append
+ );
+ exit;
} else {
my $request = Koha::Illrequests->find($params->{illrequest_id});
my $backend_result = $request->custom_capability($op, $params);
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt
index 88ee777a09..32a6d772f0 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt
@@ -92,6 +92,10 @@
[% END %]
+ [% IF whole.success %]
+ [% whole.success | html %]
+ [% END %]
+
[% IF query_type == 'create' %]
New ILL request
[% PROCESS $whole.template %]
@@ -279,6 +283,31 @@
[% END %]
[% END %]
+ [% IF tran_success %]
+ [% succ_methods = [] %]
+ [% IF tran_success.match('email') %]
+ [% succ_methods.push('email') %]
+ [% END %]
+ [% IF tran_success.match('sms') %]
+ [% succ_methods.push('SMS') %]
+ [% END %]
+
+ The requested notice was queued for delivery by [% succ_methods.join(', ') | html %]
+
+ [% END %]
+ [% IF tran_fail %]
+ [% fail_methods = [] %]
+ [% IF tran_fail.match('email') %]
+ [% fail_methods.push('email') %]
+ [% END %]
+ [% IF tran_fail.match('sms') %]
+ [% fail_methods.push('SMS') %]
+ [% END %]
+
+ The requested notice was NOT queued for delivery by [% fail_methods.join(', ') | html %]
+
+ [% END %]
+
Manage ILL request