Bug 42443: Add ability to cancel a transfer via the REST API
This patch adds a DELETE /transfers/{transfer_id} route that cancels the
given transfer by id and reverts an in transit recall for the item the same
way as returns.pl does. The cancellation reason is passed in the request body
so this api endpoint could be used in other places we can cancel transfers.
Signed-off-by: Alex Carver [Acerock7] <alex@rcls.org>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
parent
560d7b7ff7
commit
c813fb2574
|
|
@ -19,7 +19,9 @@ use Modern::Perl;
|
|||
|
||||
use Mojo::Base 'Mojolicious::Controller';
|
||||
|
||||
use C4::Context;
|
||||
use Koha::Item::Transfers;
|
||||
use Koha::Recalls;
|
||||
|
||||
use Try::Tiny qw( catch try );
|
||||
|
||||
|
|
@ -44,4 +46,35 @@ sub list {
|
|||
};
|
||||
}
|
||||
|
||||
=head3 delete
|
||||
|
||||
Controller function that handles cancelling an item transfer
|
||||
|
||||
=cut
|
||||
|
||||
sub delete {
|
||||
my $c = shift->openapi->valid_input or return;
|
||||
|
||||
my $transfer = Koha::Item::Transfers->find( $c->param('transfer_id') );
|
||||
|
||||
return $c->render_resource_not_found("Transfer")
|
||||
unless $transfer;
|
||||
|
||||
return try {
|
||||
my $body = $c->req->json;
|
||||
|
||||
$transfer->cancel( { reason => $body->{cancellation_reason}, force => 1 } );
|
||||
|
||||
# Revert an in transit recall tied to this item, as returns.pl does
|
||||
if ( C4::Context->preference('UseRecalls') ) {
|
||||
my $recall = Koha::Recalls->find( { item_id => $transfer->itemnumber, status => 'in_transit' } );
|
||||
$recall->revert_transfer if $recall;
|
||||
}
|
||||
|
||||
return $c->render_resource_deleted;
|
||||
} catch {
|
||||
$c->unhandled_exception($_);
|
||||
};
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -85,3 +85,60 @@
|
|||
x-koha-authorization:
|
||||
permissions:
|
||||
circulate: circulate_remaining_permissions
|
||||
"/transfers/{transfer_id}":
|
||||
delete:
|
||||
x-mojo-to: Transfers#delete
|
||||
operationId: deleteTransfer
|
||||
tags:
|
||||
- transfers
|
||||
summary: Cancel transfer
|
||||
parameters:
|
||||
- $ref: "../swagger.yaml#/parameters/transfer_id_pp"
|
||||
- name: body
|
||||
in: body
|
||||
description: A JSON object describing how to cancel the transfer
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
cancellation_reason:
|
||||
type: string
|
||||
description: The reason the transfer is being cancelled
|
||||
required:
|
||||
- cancellation_reason
|
||||
additionalProperties: false
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"204":
|
||||
description: Transfer cancelled
|
||||
"400":
|
||||
description: Bad request
|
||||
schema:
|
||||
$ref: "../swagger.yaml#/definitions/error"
|
||||
"401":
|
||||
description: Authentication required
|
||||
schema:
|
||||
$ref: "../swagger.yaml#/definitions/error"
|
||||
"403":
|
||||
description: Access forbidden
|
||||
schema:
|
||||
$ref: "../swagger.yaml#/definitions/error"
|
||||
"404":
|
||||
description: Transfer not found
|
||||
schema:
|
||||
$ref: "../swagger.yaml#/definitions/error"
|
||||
"500":
|
||||
description: |
|
||||
Internal server error. Possible `error_code` attribute values:
|
||||
|
||||
* `internal_server_error`
|
||||
schema:
|
||||
$ref: "../swagger.yaml#/definitions/error"
|
||||
"503":
|
||||
description: Under maintenance
|
||||
schema:
|
||||
$ref: "../swagger.yaml#/definitions/error"
|
||||
x-koha-authorization:
|
||||
permissions:
|
||||
circulate: circulate_remaining_permissions
|
||||
|
|
|
|||
|
|
@ -699,6 +699,8 @@ paths:
|
|||
$ref: "./paths/transfer_limits.yaml#/~1transfer_limits~1{limit_id}"
|
||||
/transfers:
|
||||
$ref: ./paths/transfers.yaml#/~1transfers
|
||||
"/transfers/{transfer_id}":
|
||||
$ref: "./paths/transfers.yaml#/~1transfers~1{transfer_id}"
|
||||
/status/version:
|
||||
$ref: ./paths/status.yaml#/~1status~1version
|
||||
parameters:
|
||||
|
|
@ -1129,6 +1131,12 @@ parameters:
|
|||
name: ticket_id
|
||||
required: true
|
||||
type: integer
|
||||
transfer_id_pp:
|
||||
description: Internal transfer identifier
|
||||
in: path
|
||||
name: transfer_id
|
||||
required: true
|
||||
type: integer
|
||||
transfer_limit_id_pp:
|
||||
description: Internal transfer limit identifier
|
||||
in: path
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@
|
|||
cancellation_date: { "=": null },
|
||||
};
|
||||
|
||||
$("#transferstoreceive").kohaTable(
|
||||
var transfers_table = $("#transferstoreceive").kohaTable(
|
||||
{
|
||||
ajax: {
|
||||
url: "/api/v1/transfers",
|
||||
|
|
@ -190,8 +190,8 @@
|
|||
searchable: false,
|
||||
orderable: false,
|
||||
render: function (data, type, row, meta) {
|
||||
var itemnumber = encodeURIComponent(row.item_id);
|
||||
return `<a href="/cgi-bin/koha/circ/returns.pl?itemnumber=${itemnumber}&canceltransfer=1&dest=ttr" class="btn btn-default btn-xs"><i class="fa fa-times"></i> ${_("Cancel transfer")}</a>`;
|
||||
var transfer_id = encodeURIComponent(row.transfer_id);
|
||||
return `<a href="#" class="btn btn-default btn-xs cancel-transfer" data-transfer-id="${transfer_id}"><i class="fa fa-times"></i> ${_("Cancel transfer")}</a>`;
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
@ -200,6 +200,29 @@
|
|||
1,
|
||||
additional_filters
|
||||
);
|
||||
|
||||
$("#transferstoreceive").on("click", ".cancel-transfer", function (e) {
|
||||
e.preventDefault();
|
||||
var transfer_id = $(this).data("transfer-id");
|
||||
$.ajax({
|
||||
method: "DELETE",
|
||||
url: `/api/v1/transfers/${transfer_id}`,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ cancellation_reason: "Manual" }),
|
||||
})
|
||||
.done(function () {
|
||||
transfers_table.DataTable().ajax.reload(null, false);
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
if (xhr.status === 404) {
|
||||
// The transfer was already received or cancelled elsewhere; refresh so the row drops off
|
||||
alert(_("This transfer no longer exists. It may have already been received or cancelled."));
|
||||
transfers_table.DataTable().ajax.reload(null, false);
|
||||
} else {
|
||||
alert(_("Something went wrong, the transfer could not be cancelled"));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
[% END %]
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
use Modern::Perl;
|
||||
|
||||
use Test::NoWarnings;
|
||||
use Test::More tests => 2;
|
||||
use Test::More tests => 3;
|
||||
use Test::Mojo;
|
||||
|
||||
use t::lib::TestBuilder;
|
||||
|
|
@ -114,3 +114,65 @@ subtest 'list() tests' => sub {
|
|||
|
||||
$schema->storage->txn_rollback;
|
||||
};
|
||||
|
||||
subtest 'delete() tests' => sub {
|
||||
|
||||
plan tests => 10;
|
||||
|
||||
$schema->storage->txn_begin;
|
||||
|
||||
my $librarian = $builder->build_object(
|
||||
{
|
||||
class => 'Koha::Patrons',
|
||||
value => { flags => 2**1 } # circulate flag = 1
|
||||
}
|
||||
);
|
||||
my $password = 'thePassword123';
|
||||
$librarian->set_password( { password => $password, skip_validation => 1 } );
|
||||
my $userid = $librarian->userid;
|
||||
|
||||
my $patron = $builder->build_object(
|
||||
{
|
||||
class => 'Koha::Patrons',
|
||||
value => { flags => 0 }
|
||||
}
|
||||
);
|
||||
$patron->set_password( { password => $password, skip_validation => 1 } );
|
||||
my $unauth_userid = $patron->userid;
|
||||
|
||||
my $item = $builder->build_sample_item;
|
||||
my $transfer = $builder->build_object(
|
||||
{
|
||||
class => 'Koha::Item::Transfers',
|
||||
value => {
|
||||
itemnumber => $item->itemnumber,
|
||||
datesent => \'NOW()', # in transit
|
||||
datearrived => undef,
|
||||
datecancelled => undef,
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
# Unauthorized access
|
||||
$t->delete_ok(
|
||||
"//$unauth_userid:$password@/api/v1/transfers/" . $transfer->id => json => { cancellation_reason => 'Manual' } )
|
||||
->status_is(403);
|
||||
|
||||
# A cancellation reason is mandatory
|
||||
$t->delete_ok( "//$userid:$password@/api/v1/transfers/" . $transfer->id )->status_is(400);
|
||||
|
||||
# Not found
|
||||
$t->delete_ok( "//$userid:$password@/api/v1/transfers/"
|
||||
. ( $transfer->id + 1000 ) => json => { cancellation_reason => 'Manual' } )->status_is(404);
|
||||
|
||||
# Cancel an in-transit transfer with the supplied reason
|
||||
$t->delete_ok(
|
||||
"//$userid:$password@/api/v1/transfers/" . $transfer->id => json => { cancellation_reason => 'WrongTransfer' } )
|
||||
->status_is(204);
|
||||
|
||||
$transfer->discard_changes;
|
||||
ok( defined $transfer->datecancelled, 'The transfer has been cancelled' );
|
||||
is( $transfer->cancellation_reason, 'WrongTransfer', 'The supplied cancellation reason is recorded' );
|
||||
|
||||
$schema->storage->txn_rollback;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue