Bug 35604: ConfirmAuto workflow stage

Sponsored-by: NHS England
Sponsored-by: PTFS Europe Ltd
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
This commit is contained in:
Pedro Amorim 2024-01-05 15:24:07 +00:00 committed by Katrin Fischer
parent a4484edbde
commit c51d28e623
No known key found for this signature in database
GPG Key ID: 0EF6E2C03357A834
7 changed files with 553 additions and 1 deletions

View File

@ -0,0 +1,116 @@
package Koha::ILL::Request::Workflow::ConfirmAuto;
# Copyright 2023 PTFS Europe Ltd
#
# 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 3 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, see <http://www.gnu.org/licenses>.
use Modern::Perl;
use base qw(Koha::ILL::Request::Workflow);
use JSON qw( encode_json );
use Koha::ILL::Backends;
=head1 NAME
Koha::ILL::Request::Workflow::ConfirmAuto - Koha ILL ConfirmAuto Workflow
=head1 SYNOPSIS
Object-oriented class that provides the AutoILLBackendPriority confirmation screen
=head1 DESCRIPTION
This class provides the ability to verify if it should render the AutoILLBackendPriority
confirmation screen and handle the template params accordingly
=head1 API
=head2 Class Methods
=head3 show_confirm_auto
my $show_confirm_auto =
Koha::ILL::Request::Workflow::ConfirmAuto->show_confirm_auto($params);
Given $request, returns true if confirm auto should be shown
=cut
sub show_confirm_auto {
my ( $self, $request ) = @_;
return
# AutoILLBackendPriority is enabled
C4::Context->preference("AutoILLBackendPriority")
# Confirm auto has not yet been submitted
&& !$self->{metadata}->{confirm_auto_submitted}
# The form has been submitted and the backend is able to create the request
&& $request->_backend_capability( 'can_create_request', $self->{metadata} );
}
=head3 confirm_auto_template_params
Given $params, returns the template parameters for rendering the confirm auto screen
=cut
sub confirm_auto_template_params {
my ( $self, $params ) = @_;
$params->{method} = 'confirmautoill' if $self->{ui_context} eq 'staff';
delete $params->{stage} if $self->{ui_context} eq 'staff';
my @backends = $self->get_priority_backends();
return (
whole => $params,
metadata => $self->prep_metadata($params),
core_fields => Koha::ILL::Backend::Standard->_get_core_fields,
auto_backends_json => scalar encode_json( \@backends ),
$self->{ui_context} eq 'opac'
? (
illrequestsview => 1,
message => $params->{message},
op => 'confirmautoill',
)
: ()
);
}
=head3 get_priority_backends
Returns backends ordered by AutoILLBackendPriority
=cut
sub get_priority_backends {
my ( $self, $params ) = @_;
my @backends;
my @priority_enabled_backends = split ",", C4::Context->preference('AutoILLBackendPriority');
foreach my $backend (@priority_enabled_backends) {
my $loaded_backend = Koha::ILL::Request->new->load_backend($backend);
my $availability_check_info = $loaded_backend->_backend->availability_check_info( $self->{metadata} );
push @backends, $availability_check_info if $availability_check_info;
}
return @backends;
}
1;

View File

@ -31,6 +31,7 @@ use Koha::ILL::Request;
use Koha::ILL::Batches;
use Koha::ILL::Request::Workflow::Availability;
use Koha::ILL::Request::Workflow::TypeDisclaimer;
use Koha::ILL::Request::Workflow::ConfirmAuto;
use Koha::Libraries;
use Koha::Plugins;
@ -134,6 +135,7 @@ if ($backends_available) {
# Before request creation operations - Preparation
my $availability = Koha::ILL::Request::Workflow::Availability->new( $params, 'staff' );
my $type_disclaimer = Koha::ILL::Request::Workflow::TypeDisclaimer->new( $params, 'staff' );
my $confirm_auto = Koha::ILL::Request::Workflow::ConfirmAuto->new( $params, 'staff' );
# ILLCheckAvailability operation
if ( $availability->show_availability($request) ) {
@ -145,6 +147,11 @@ if ($backends_available) {
$op = 'typedisclaimer';
$template->param( $type_disclaimer->type_disclaimer_template_params($params) );
# ConfirmAuto operation
} elsif ( $confirm_auto->show_confirm_auto($request) ) {
$op = 'confirmautoill';
$template->param( $confirm_auto->confirm_auto_template_params($params) );
# Ready to create ILL request
} else {
my $backend_result = $request->backend_create($params);

View File

@ -46,6 +46,13 @@
[% WRAPPER breadcrumb_item bc_active= 1 %]
<span>Request type disclaimer</span>
[% END %]
[% ELSIF op == 'confirmautoill' %]
[% WRAPPER breadcrumb_item %]
<a href="/cgi-bin/koha/ill/ill-requests.pl">ILL requests</a>
[% END %]
[% WRAPPER breadcrumb_item bc_active= 1 %]
<span>Confirm automatic request</span>
[% END %]
[% ELSE %]
[% WRAPPER breadcrumb_item bc_active= 1 %]
<span>ILL requests</span>
@ -846,6 +853,39 @@
</fieldset>
</form>
</div>
[% ELSIF op == 'confirmautoill' %]
<!-- confirmautoill -->
<h1>Confirm automatic request</h1>
<div id="results" class="page-section">
<form method="post" id="confirmautoill-form">
<fieldset class="rows">
<h3>Confirm backend:</h3>
<p id="autoillbackends"></p>
<p id="autoillbackend-message" class="text-info"></p>
</fieldset>
<fieldset class="action">
[% FOREACH key IN whole.keys %]
[% value = whole.$key %]
[% IF key != 'method' && key != 'custom_key' && key != 'custom_value' %]
<input type="hidden" name="[% key | html %]" value="[% value | html %]" />
[% END %]
[% END %]
[% custom_keys = whole.custom_key.split('\0') %]
[% custom_values = whole.custom_value.split('\0') %]
[% i = 0 %]
[% FOREACH custom_key IN custom_keys %]
<input type="hidden" name="custom_key" value="[% custom_key | html %]" />
<input type="hidden" name="custom_value" value="[% custom_values.$i | html %]" />
[% i = i + 1 %]
[% END %]
<input type="hidden" name="method" value="create" />
<input type="hidden" name="stage" value="form" />
<input type="hidden" name="confirm_auto_submitted" value="1" />
<input type="submit" value="Confirm" />
<a class="cancel" href="ill-requests.pl">Cancel</a>
</fieldset>
</form>
</div>
[% ELSIF op == 'batch_list' || op == 'batch_create' %]
[% INCLUDE 'ill-batch.inc' %]
[% ELSE %]
@ -886,6 +926,11 @@
[% ELSE %]
var services = [];
[% END %]
[% IF auto_backends_json.length > 0 %]
var auto_backends = [% auto_backends_json | $raw %];
[% ELSE %]
var auto_backends = [];
[% END %]
[% IF metadata.length > 0 %]
var metadata = "[% metadata | $raw %]";
[% END %]
@ -918,6 +963,9 @@
[% IF (op == 'availability' || op == 'generic_confirm') && Koha.Preference('ILLCheckAvailability') %]
[% Asset.js("js/ill-availability.js") | $raw %]
[% END %]
[% IF (op == 'confirmautoill' && Koha.Preference('AutoILLBackendPriority')) %]
[% Asset.js("js/ill-autobackend.js") | $raw %]
[% END %]
[% IF op == 'availability' && Koha.Preference('ILLCheckAvailability') %]
<script>
$(document).ready(function () {

View File

@ -0,0 +1,169 @@
$(document).ready(function () {
let auto_ill_el = "#confirmautoill-form #autoillbackends";
let auto_ill_message_el = "#confirmautoill-form #autoillbackend-message";
confirmAutoInit();
getBackendsAvailability(auto_backends, metadata);
/**
* Retrieves the backend availability for a given auto backend and metadata.
*
* @param {Object} auto_backend - The auto backend object.
* @param {string} metadata - The metadata string.
* @return {Promise} A Promise that resolves to the JSON response.
*/
async function getBackendAvailability(auto_backend, metadata) {
return $.ajax({
url: auto_backend.endpoint + metadata,
type: "GET",
dataType: "json",
beforeSend: function () {
_addBackendPlaceholderEl(auto_backend.name);
_addBackendOption(auto_backend.name);
_addVerifyingMessage(auto_backend.name);
auto_backend.available = 0;
},
success: function (data) {
_addSuccessMessage(auto_backend.name);
auto_backend.available = 1;
},
error: function (request, textstatus) {
if (textstatus === "timeout") {
_addErrorMessage(
auto_backend.name,
__("Verification timed out.")
);
} else {
let message = "Error";
if (request.hasOwnProperty("responseJSON")) {
if (request.responseJSON.error) {
message = request.responseJSON.error;
} else if (request.responseJSON.errors) {
message = request.responseJSON.errors
.map(error => error.message)
.join(", ");
}
}
_addErrorMessage(auto_backend.name, message);
}
},
timeout: 10000,
});
}
/**
* Asynchronously checks the availability of multiple auto backends.
*
* @param {Array} auto_backends - An array of auto backends to check availability for.
* @param {Object} metadata - Additional metadata for the availability check.
* @return {void}
*/
function getBackendsAvailability(auto_backends, metadata) {
let promises = [];
for (const auto_backend of auto_backends) {
try {
const prom = getBackendAvailability(auto_backend, metadata);
promises.push(prom);
} catch (e) {
console.log(e);
}
}
Promise.allSettled(promises).then(() => {
let auto_backend = auto_backends.find(backend => backend.available);
if (typeof auto_backend === "undefined") {
_setAutoBackend("Standard");
} else {
_setAutoBackend(auto_backend.name);
}
$('#confirmautoill-form .action input[type="submit"]').prop(
"disabled",
false
);
});
_addBackendPlaceholderEl("Standard");
_addBackendOption("Standard");
}
function _addSuccessMessage(auto_backend_name) {
_removeVerifyingMessage(auto_backend_name);
$(auto_ill_el + " > #backend-" + auto_backend_name).append(
'<span class="text-success"><i class="fa-solid fa-check"></i> ' +
__("Available.").format(auto_backend_name) +
"</span>"
);
}
function _addErrorMessage(auto_backend_name, message) {
_removeVerifyingMessage(auto_backend_name);
$(auto_ill_el + " > #backend-" + auto_backend_name).append(
'<span class="text-danger"> <i class="fa-solid fa-xmark"></i> ' +
__("Not readily available:").format(auto_backend_name) +
" " +
message +
"</span>"
);
$(
auto_ill_el +
" > #backend-" +
auto_backend_name +
' input[type="radio"]'
).prop("disabled", true);
}
function _addBackendOption(auto_backend_name) {
$(auto_ill_el + " > #backend-" + auto_backend_name).append(
' <input type="radio" id="' +
auto_backend_name +
'" name="backend" value="' +
auto_backend_name +
'">' +
' <label for="' +
auto_backend_name +
'" class="radio">' +
auto_backend_name +
"</label> "
);
}
function _addVerifyingMessage(auto_backend_name) {
$(auto_ill_el + " > #backend-" + auto_backend_name).append(
'<span id="verifying-availabilty" class="text-info"><i id="issues-table-load-delay-spinner" class="fa fa-spinner fa-pulse fa-fw"></i> ' +
__("Verifying availability...").format(auto_backend_name) +
"</span>"
);
}
function _removeVerifyingMessage(auto_backend_name) {
$(
auto_ill_el +
" #backend-" +
auto_backend_name +
" #verifying-availabilty"
).remove();
}
function _setAutoBackend(auto_backend_name) {
$(
'#confirmautoill-form #autoillbackends input[id="' +
auto_backend_name +
'"]'
).prop("checked", true);
$(auto_ill_message_el).html(
__(
"The recommended backend for your request is <strong>%s</strong>."
).format(auto_backend_name)
);
}
function _addBackendPlaceholderEl(auto_backend_name) {
$(auto_ill_el).append('<div id="backend-' + auto_backend_name + '">');
}
function confirmAutoInit() {
$('#confirmautoill-form .action input[name="backend"]').remove();
$('#confirmautoill-form .action input[type="submit"]').prop(
"disabled",
true
);
$(auto_ill_message_el).html(__("Fetching backends availability..."));
}
});

View File

@ -46,6 +46,10 @@
[% WRAPPER breadcrumb_item bc_active= 1 %]
<span>Ill request disclaimer</span>
[% END %]
[% ELSIF op == 'confirmautoill' %]
[% WRAPPER breadcrumb_item bc_active= 1 %]
<span>Confirm automatic request</span>
[% END %]
[% END %]
[% ELSE %]
[% WRAPPER breadcrumb_item bc_active= 1 %]
@ -114,7 +118,7 @@
[% IF can_patron_place_ill_in_opac %]
<div id="illrequests-create-button" class="dropdown btn-group">
[% IF Koha.Preference('AutoILLBackendPriority') %]
<a id="ill-new" class="btn btn-primary" href="/cgi-bin/koha/opac-illrequests.pl?method=create&amp;backend=Standard"> <i class="fa fa-plus" aria-hidden="true"></i> Create a new request </a>
<a id="ill-new" class="btn btn-primary" href="/cgi-bin/koha/opac-illrequests.pl?method=create&amp;backend=Standard"> <i class="fa fa-plus" aria-hidden="true"></i> Create a new auto request </a>
[% ELSIF backends.size > 1 %]
<button class="btn btn-primary dropdown-toggle" type="button" id="ill-backend-dropdown" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<i class="fa fa-plus" aria-hidden="true"></i> Create a new request <span class="caret"></span>
@ -318,6 +322,34 @@
</fieldset>
</form>
</div>
[% ELSIF op == 'confirmautoill' %]
<h1>Confirming automatic request</h1>
<div id="results" class="page-section">
<form method="post" id="confirmautoill-form">
[% INCLUDE 'csrf-token.inc' %]
<fieldset class="rows">
<p id="autoillbackends"></p>
<p id="autoillbackend-message" class="text-info"></p>
</fieldset>
<fieldset class="action">
[% FOREACH key IN whole.keys %]
[% value = whole.$key %]
[% IF key != 'custom_key' && key != 'custom_value' && key != 'csrf_token' %]
<input type="hidden" name="[% key | html %]" value="[% value | html %]" />
[% END %]
[% END %]
[% custom_keys = whole.custom_key.split('\0') %]
[% custom_values = whole.custom_value.split('\0') %]
[% i = 0 %]
[% FOREACH custom_key IN custom_keys %]
<input type="hidden" name="custom_key" value="[% custom_key | html %]" />
<input type="hidden" name="custom_value" value="[% custom_values.$i | html %]" />
[% i = i + 1 %]
[% END %]
<input type="hidden" name="confirm_auto_submitted" value="1" />
</fieldset>
</form>
</div>
[% END # / IF op == 'cud-create' %]
</div>
<!-- / #illrequests -->
@ -346,6 +378,11 @@
[% ELSE %]
var services = [];
[% END %]
[% IF auto_backends_json.length > 0 %]
var auto_backends = [% auto_backends_json | $raw %];
[% ELSE %]
var auto_backends = [];
[% END %]
[% IF metadata.length > 0 %]
var metadata = "[% metadata | $raw %]";
[% END %]
@ -358,6 +395,9 @@
});
</script>
[% END %]
[% IF op == 'confirmautoill' %]
[% Asset.js("js/ill-autobackend.js") | $raw %]
[% END %]
<!-- prettier-ignore-start -->
[% TRY %]
[% PROCESS backend_jsinclude %]

View File

@ -0,0 +1,161 @@
$(document).ready(function () {
let auto_ill_el = "#confirmautoill-form #autoillbackends";
let auto_ill_message_el = "#confirmautoill-form #autoillbackend-message";
confirmAutoInit();
getBackendsAvailability(auto_backends, metadata);
/**
* Retrieves the backend availability for a given auto backend and metadata.
*
* @param {Object} auto_backend - The auto backend object.
* @param {string} metadata - The metadata string.
* @return {Promise} A Promise that resolves to the JSON response.
*/
async function getBackendAvailability(auto_backend, metadata) {
return $.ajax({
url: auto_backend.endpoint + metadata,
type: "GET",
dataType: "json",
beforeSend: function () {
_addBackendPlaceholderEl(auto_backend.name);
_addBackendOption(auto_backend.name);
_addVerifyingMessage(auto_backend.name);
auto_backend.available = 0;
},
success: function (data) {
_addSuccessMessage(auto_backend.name);
auto_backend.available = 1;
},
error: function (request, textstatus) {
if (textstatus === "timeout") {
_addErrorMessage(
auto_backend.name,
__("Verification timed out.")
);
} else {
let message = "Error";
if (request.hasOwnProperty("responseJSON")) {
if (request.responseJSON.error) {
message = request.responseJSON.error;
} else if (request.responseJSON.errors) {
message = request.responseJSON.errors
.map(error => error.message)
.join(", ");
}
}
_addErrorMessage(auto_backend.name, message);
}
},
timeout: 10000,
});
}
/**
* Asynchronously checks the availability of multiple auto backends.
*
* @param {Array} auto_backends - An array of auto backends to check availability for.
* @param {Object} metadata - Additional metadata for the availability check.
* @return {void}
*/
function getBackendsAvailability(auto_backends, metadata) {
let promises = [];
for (const auto_backend of auto_backends) {
try {
const prom = getBackendAvailability(auto_backend, metadata);
promises.push(prom);
} catch (e) {
console.log(e);
}
}
Promise.allSettled(promises).then(() => {
let auto_backend = auto_backends.find(backend => backend.available);
if (typeof auto_backend === "undefined") {
_setAutoBackend("Standard");
} else {
_setAutoBackend(auto_backend.name);
}
$('#confirmautoill-form .action input[type="submit"]').prop(
"disabled",
false
);
});
_addBackendPlaceholderEl("Standard");
_addBackendOption("Standard");
}
function _addSuccessMessage(auto_backend_name) {
_removeVerifyingMessage(auto_backend_name);
$(auto_ill_el + " > #backend-" + auto_backend_name).append(
'<span class="text-success"><i class="fa-solid fa-check"></i> ' +
__("Available.").format(auto_backend_name) +
"</span>"
);
}
function _addErrorMessage(auto_backend_name, message) {
_removeVerifyingMessage(auto_backend_name);
$(auto_ill_el + " > #backend-" + auto_backend_name).append(
'<span class="text-danger"> <i class="fa-solid fa-xmark"></i> ' +
__("Not readily available:").format(auto_backend_name) +
" " +
message +
"</span>"
);
}
function _addBackendOption(auto_backend_name) {
$(auto_ill_el + " > #backend-" + auto_backend_name).append(
' <input type="radio" id="' +
auto_backend_name +
'" name="backend" value="' +
auto_backend_name +
'">' +
' <label for="' +
auto_backend_name +
'" class="radio">' +
auto_backend_name +
"</label> "
);
}
function _addVerifyingMessage(auto_backend_name) {
$(auto_ill_el + " > #backend-" + auto_backend_name).append(
'<span id="verifying-availabilty" class="text-info"><i id="issues-table-load-delay-spinner" class="fa fa-spinner fa-pulse fa-fw"></i> ' +
__("Verifying availability...").format(auto_backend_name) +
"</span>"
);
}
function _removeVerifyingMessage(auto_backend_name) {
$(
auto_ill_el +
" #backend-" +
auto_backend_name +
" #verifying-availabilty"
).remove();
}
function _setAutoBackend(auto_backend_name) {
$(
'#confirmautoill-form #autoillbackends input[id="' +
auto_backend_name +
'"]'
).prop("checked", true);
$("#confirmautoill-form").submit();
}
function _addBackendPlaceholderEl(auto_backend_name) {
$(auto_ill_el)
.append('<div id="backend-' + auto_backend_name + '">')
.hide();
}
function confirmAutoInit() {
$('#confirmautoill-form .action input[name="backend"]').remove();
$(auto_ill_message_el).html(
'<span id="verifying-availabilty" class="text-info"><i id="issues-table-load-delay-spinner" class="fa fa-spinner fa-pulse fa-fw"></i> ' +
__("Placing your request...") +
"</span>"
);
}
});

View File

@ -33,6 +33,7 @@ use Koha::ILL::Request;
use Koha::Libraries;
use Koha::Patrons;
use Koha::ILL::Request::Workflow::Availability;
use Koha::ILL::Request::Workflow::ConfirmAuto;
use Koha::ILL::Request::Workflow::TypeDisclaimer;
my $query = CGI->new;
@ -117,6 +118,7 @@ if ( $op eq 'list' ) {
# Before request creation operations - Preparation
my $availability = Koha::ILL::Request::Workflow::Availability->new( $params, 'opac' );
my $type_disclaimer = Koha::ILL::Request::Workflow::TypeDisclaimer->new( $params, 'opac' );
my $confirm_auto = Koha::ILL::Request::Workflow::ConfirmAuto->new( $params, 'opac' );
# ILLCheckAvailability operation
if ( $availability->show_availability($request) ) {
@ -135,6 +137,15 @@ if ( $op eq 'list' ) {
$template->output, undef,
{ force_no_caching => 1 };
exit;
# ConfirmAuto operation
} elsif ( $confirm_auto->show_confirm_auto($request) ) {
$op = 'confirmautoill';
$template->param( $confirm_auto->confirm_auto_template_params($params) );
output_html_with_http_headers $query, $cookie,
$template->output, undef,
{ force_no_caching => 1 };
exit;
}
my $patron = Koha::Patrons->find( { borrowernumber => $loggedinuser } );