From 2a56125c3713d10c9e826fd90531d3bb3c620c84 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 Sponsored-by: PTFS Europe Signed-off-by: Niamh Walker-Headon Signed-off-by: Martin Renvoize Signed-off-by: Katrin Fischer --- Koha/Illrequest.pm | 350 ++++++++++++++---- 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, 400 insertions(+), 74 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 18348663d1..61d1b43948 100644 --- a/Koha/Illrequest.pm +++ b/Koha/Illrequest.pm @@ -25,6 +25,8 @@ use Encode qw( encode ); use Try::Tiny; use DateTime; +use C4::Letters; +use C4::Members; use Koha::Database; use Koha::DateUtils qw/ dt_from_string /; use Koha::Email; @@ -248,6 +250,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 @@ -281,6 +284,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; @@ -1287,38 +1294,11 @@ sub generic_confirm { my $library = 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 @@ -1330,7 +1310,10 @@ EOF method => 'generic_confirm', stage => 'draft', value => { - draft => $draft, + draft => { + subject => $letter->{title}, + body => $letter->{content} + }, 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.") if ( !$to ); # Create the from, replyto and sender headers - my $from = $library->branchemail; - my $reply_to = $library->branchreplyto || $from; + 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 $email = Koha::Email->create( - { - to => $to, - from => $from, - reply_to => $reply_to, - subject => $params->{subject}, - text_body => $params->{body}, + # 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', + }; } - ); + } + 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 - try { +=head3 get_staff_to_address - $email->send_or_die({ transport => $library->smtp_server->transport }); + my $email = $request->get_staff_to_address(); - $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', - }; +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; } - catch { - return { - error => 1, - status => 'email_failed', - message => "$_", - method => 'generic_confirm', - stage => 'draft', - }; + } + 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 { - 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 diff --git a/Koha/Illrequest/Logger.pm b/Koha/Illrequest/Logger.pm index a899a8db22..971b3a8b76 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(@_); } }; @@ -70,7 +74,8 @@ sub new { C4::Templates::_get_template_file('ill/log/', 'intranet', $query); $self->{templates} = { - STATUS_CHANGE => $base . 'status_change.tt' + STATUS_CHANGE => $base . 'status_change.tt', + PATRON_NOTICE => $base . 'patron_notice.tt' }; 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 Koha::IllRequest::Logger->log_status_change($params); @@ -210,6 +240,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 = C4::Koha::GetAuthorisedValues('ILLSTATUS'); my $alias_hash; @@ -217,6 +255,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 df80dd5406..dfc27efcba 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; @@ -72,12 +73,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' ) { @@ -380,6 +397,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 86c81afbd3..b6a40a17f0 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 @@ -99,6 +99,10 @@

[% END %] + [% IF whole.success %] +

[% whole.success | html %]

+ [% END %] + [% IF query_type == 'create' %]

New ILL request

[% PROCESS $whole.template %] @@ -462,6 +466,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