Browse Source

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

Sponsored-by: PTFS Europe
Signed-off-by: Niamh Walker-Headon <Niamh.Walker-Headon@it-tallaght.ie>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>

Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
20.11.x
Andrew Isherwood 5 years ago
committed by Jonathan Druart
parent
commit
1a7f09709a
  1. 350
      Koha/Illrequest.pm
  2. 41
      Koha/Illrequest/Logger.pm
  3. 36
      ill/ill-requests.pl
  4. 39
      koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt
  5. 6
      koha-tmpl/intranet-tmpl/prog/en/modules/ill/log/patron_notice.tt
  6. 2
      opac/opac-illrequests.pl

350
Koha/Illrequest.pm

@ -25,6 +25,8 @@ use Encode qw( encode );
use Try::Tiny; use Try::Tiny;
use DateTime; use DateTime;
use C4::Letters;
use C4::Members;
use Koha::Database; use Koha::Database;
use Koha::DateUtils qw/ dt_from_string /; use Koha::DateUtils qw/ dt_from_string /;
use Koha::Email; use Koha::Email;
@ -248,6 +250,7 @@ sub status_alias {
Overloaded getter/setter for request status, Overloaded getter/setter for request status,
also nullifies status_alias and records the fact that the status has changed also nullifies status_alias and records the fact that the status has changed
and sends a notice if appropriate
=cut =cut
@ -281,6 +284,10 @@ sub status {
}); });
} }
delete $self->{previous_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; return $ret;
} else { } else {
return $current_status; return $current_status;
@ -1287,38 +1294,11 @@ sub generic_confirm {
my $library = Koha::Libraries->find($params->{current_branchcode}) my $library = Koha::Libraries->find($params->{current_branchcode})
|| die "Invalid current branchcode. Are you logged in as the database user?"; || die "Invalid current branchcode. Are you logged in as the database user?";
if ( !$params->{stage}|| $params->{stage} eq 'init' ) { if ( !$params->{stage}|| $params->{stage} eq 'init' ) {
my $draft->{subject} = "ILL Request"; # Get the message body from the notice definition
$draft->{body} = <<EOF; my $letter = $self->get_notice({
Dear Sir/Madam, notice_code => 'ILL_PARTNER_REQ',
transport => 'email'
We would like to request an interlibrary loan for a title matching the });
following description:
EOF
my $details = $self->metadata;
while (my ($title, $value) = each %{$details}) {
$draft->{body} .= " - " . $title . ": " . $value . "\n"
if $value;
}
$draft->{body} .= <<EOF;
Please let us know if you are able to supply this to us.
Kind Regards
EOF
my @address = map { $library->$_ }
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;
my $partners = Koha::Patrons->search({ my $partners = Koha::Patrons->search({
categorycode => $self->_config->partner_code categorycode => $self->_config->partner_code
@ -1330,7 +1310,10 @@ EOF
method => 'generic_confirm', method => 'generic_confirm',
stage => 'draft', stage => 'draft',
value => { value => {
draft => $draft, draft => {
subject => $letter->{title},
body => $letter->{content}
},
partners => $partners, partners => $partners,
} }
}; };
@ -1346,57 +1329,280 @@ EOF
"No target email addresses found. Either select at least one partner or check your ILL partner library records.") "No target email addresses found. Either select at least one partner or check your ILL partner library records.")
if ( !$to ); if ( !$to );
# Create the from, replyto and sender headers # Create the from, replyto and sender headers
my $from = $library->branchemail; my $from = $branch->branchillemail || $branch->branchemail;
my $reply_to = $library->branchreplyto || $from; my $replyto = $branch->branchreplyto || $from;
Koha::Exceptions::Ill::NoLibraryEmail->throw( Koha::Exceptions::Ill::NoLibraryEmail->throw(
"Your library has no usable email address. Please set it.") "Your library has no usable email address. Please set it.")
if ( !$from ); if ( !$from );
# Create the email # So we get a notice hashref, then substitute the possibly
my $email = Koha::Email->create( # modified title and body from the draft stage
{ my $letter = $self->get_notice({
to => $to, notice_code => 'ILL_PARTNER_REQ',
from => $from, transport => 'email'
reply_to => $reply_to, });
subject => $params->{subject}, $letter->{title} = $params->{subject};
text_body => $params->{body}, $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',
};
} }
); }
return {
error => 1,
status => 'email_failed',
message => 'Email queueing failed',
method => 'generic_confirm',
stage => 'draft',
};
} else {
die "Unknown stage, should not have happened."
}
}
# Send it =head3 get_staff_to_address
try {
$email->send_or_die({ transport => $library->smtp_server->transport }); my $email = $request->get_staff_to_address();
$self->status("GENREQ")->store; Get the email address to which staff notices should be sent
$self->_backend_capability(
'set_requested_partners', =cut
{
request => $self, sub get_staff_to_address {
to => $to my ( $self ) = @_;
}
); # The various places we can get an ILL staff email address from
return { # (In order of preference)
error => 0, #
status => '', # Dedicated branch address
message => '', my $library = Koha::Libraries->find( $self->branchcode );
method => 'generic_confirm', my $branch_ill_to = $library->branchillemail;
stage => 'commit', # General purpose ILL address from syspref
next => 'illview', 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;
} }
catch { }
return { if (scalar @success > 0) {
error => 1, my $logger = Koha::Illrequest::Logger->new;
status => 'email_failed', $logger->log_patron_notice({
message => "$_", request => $self,
method => 'generic_confirm', notice_code => $notice_code
stage => 'draft', });
}; }
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 { } else {
die "Unknown stage, should not have happened." 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 =head3 id_prefix

41
Koha/Illrequest/Logger.pm

@ -26,6 +26,7 @@ use C4::Context;
use C4::Templates; use C4::Templates;
use C4::Log qw( logaction ); use C4::Log qw( logaction );
use Koha::ActionLogs; use Koha::ActionLogs;
use Koha::Notice::Template;
=head1 NAME =head1 NAME
@ -62,6 +63,9 @@ sub new {
$self->{loggers} = { $self->{loggers} = {
status => sub { status => sub {
$self->log_status_change(@_); $self->log_status_change(@_);
},
patron_notice => sub {
$self->log_patron_notice(@_);
} }
}; };
@ -70,7 +74,8 @@ sub new {
C4::Templates::_get_template_file('ill/log/', 'intranet', $query); C4::Templates::_get_template_file('ill/log/', 'intranet', $query);
$self->{templates} = { $self->{templates} = {
STATUS_CHANGE => $base . 'status_change.tt' STATUS_CHANGE => $base . 'status_change.tt',
PATRON_NOTICE => $base . 'patron_notice.tt'
}; };
bless $self, $class; bless $self, $class;
@ -103,6 +108,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 =head3 log_status_change
Koha::IllRequest::Logger->log_status_change($params); Koha::IllRequest::Logger->log_status_change($params);
@ -210,6 +240,14 @@ sub get_request_logs {
{ order_by => { -desc => "timestamp" } } { order_by => { -desc => "timestamp" } }
)->unblessed; )->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 # Populate a lookup table for status aliases
my $aliases = C4::Koha::GetAuthorisedValues('ILLSTATUS'); my $aliases = C4::Koha::GetAuthorisedValues('ILLSTATUS');
my $alias_hash; my $alias_hash;
@ -217,6 +255,7 @@ sub get_request_logs {
$alias_hash->{$alias->{authorised_value}} = $alias; $alias_hash->{$alias->{authorised_value}} = $alias;
} }
foreach my $log(@{$logs}) { foreach my $log(@{$logs}) {
$log->{notice_types} = $notice_hash;
$log->{aliases} = $alias_hash; $log->{aliases} = $alias_hash;
$log->{info} = from_json($log->{info}); $log->{info} = from_json($log->{info});
$log->{template} = $self->get_log_template({ $log->{template} = $self->get_log_template({

36
ill/ill-requests.pl

@ -23,6 +23,7 @@ use CGI;
use C4::Auth; use C4::Auth;
use C4::Output; use C4::Output;
use Koha::Notice::Templates;
use Koha::AuthorisedValues; use Koha::AuthorisedValues;
use Koha::Illcomment; use Koha::Illcomment;
use Koha::Illrequests; use Koha::Illrequests;
@ -72,12 +73,28 @@ if ( $backends_available ) {
# View the details of an ILL # View the details of an ILL
my $request = Koha::Illrequests->find($params->{illrequest_id}); 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( $template->param(
notices => $notices,
request => $request, request => $request,
csrf_token => Koha::Token->new->generate_csrf({ csrf_token => Koha::Token->new->generate_csrf({
session_id => scalar $cgi->cookie('CGISESSID'), 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' ) { } elsif ( $op eq 'create' ) {
@ -380,6 +397,23 @@ if ( $backends_available ) {
); );
exit; 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 { } else {
my $request = Koha::Illrequests->find($params->{illrequest_id}); my $request = Koha::Illrequests->find($params->{illrequest_id});
my $backend_result = $request->custom_capability($op, $params); my $backend_result = $request->custom_capability($op, $params);

39
koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt

@ -99,6 +99,10 @@
</p> </p>
[% END %] [% END %]
[% IF whole.success %]
<p>[% whole.success | html %]</p>
[% END %]
[% IF query_type == 'create' %] [% IF query_type == 'create' %]
<h1>New ILL request</h1> <h1>New ILL request</h1>
[% PROCESS $whole.template %] [% PROCESS $whole.template %]
@ -462,6 +466,31 @@
[% END %] [% END %]
[% 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 %]
<div class="alert">
The requested notice was queued for delivery by [% succ_methods.join(', ') | html %]
</div>
[% 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 %]
<div class="alert">
The requested notice was NOT queued for delivery by [% fail_methods.join(', ') | html %]
</div>
[% END %]
<h1>Manage ILL request</h1> <h1>Manage ILL request</h1>
<div id="request-toolbar" class="btn-toolbar"> <div id="request-toolbar" class="btn-toolbar">
<a title="Edit request" id="ill-toolbar-btn-edit-action" class="btn btn-default" href="/cgi-bin/koha/ill/ill-requests.pl?method=edit_action&amp;illrequest_id=[% request.illrequest_id | html %]"> <a title="Edit request" id="ill-toolbar-btn-edit-action" class="btn btn-default" href="/cgi-bin/koha/ill/ill-requests.pl?method=edit_action&amp;illrequest_id=[% request.illrequest_id | html %]">
@ -523,6 +552,16 @@
</a> </a>
[% END %] [% END %]
[% END %] [% END %]
<div class="dropdown btn-group">
<button class="btn btn-default dropdown-toggle" type="button" id="ill-notice-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<i class="fa fa-envelope"></i> Send notice to patron <span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="ill-notice-dropdown">
[% FOREACH notice IN notices %]
<li><a href="/cgi-bin/koha/ill/ill-requests.pl?method=send_notice&amp;illrequest_id=[% request.illrequest_id | uri %]&amp;notice_code=[% notice.code | uri %]">[% notice.name | html %]</a></li>
[% END %]
</ul>
</div>
<a title="Display supplier metadata" id="ill-request-display-metadata" class="btn btn-default pull-right" href="#"> <a title="Display supplier metadata" id="ill-request-display-metadata" class="btn btn-default pull-right" href="#">
<span class="fa fa-eye"></span> <span class="fa fa-eye"></span>
Display supplier metadata Display supplier metadata

6
koha-tmpl/intranet-tmpl/prog/en/modules/ill/log/patron_notice.tt

@ -0,0 +1,6 @@
[% USE KohaDates %]
<p>
[% log.timestamp | $KohaDates with_hours => 1 %] : <b>Patron notice sent: </b>
[% notice_code = log.info.notice_code %]
&quot;[% log.notice_types.$notice_code.name | html %]&quot;
</p>

2
opac/opac-illrequests.pl

@ -85,6 +85,8 @@ if ( $op eq 'list' ) {
illrequest_id => $params->{illrequest_id} illrequest_id => $params->{illrequest_id}
}); });
$request->notesopac($params->{notesopac})->store; $request->notesopac($params->{notesopac})->store;
# Send a notice to staff alerting them of the update
$request->send_staff_notice('ILL_REQUEST_MODIFIED');
print $query->redirect( print $query->redirect(
'/cgi-bin/koha/opac-illrequests.pl?method=view&illrequest_id=' . '/cgi-bin/koha/opac-illrequests.pl?method=view&illrequest_id=' .
$params->{illrequest_id} . $params->{illrequest_id} .

Loading…
Cancel
Save