From baa6a048ce78bb972fa087868c68348150ba6091 Mon Sep 17 00:00:00 2001 From: danyonsewell Date: Mon, 1 May 2023 04:54:35 +0000 Subject: [PATCH] Bug 24400: Rest API for checkout WIP --- Koha/REST/V1/Items.pm | 60 ++++++++++++++++++++++++++++ api/v1/swagger/paths/items.yaml | 35 +++++++++++++++++ api/v1/swagger/swagger.yaml | 2 + t/db_dependent/api/v1/items.t | 70 ++++++++++++++++++++++++++++++++- 4 files changed, 166 insertions(+), 1 deletion(-) diff --git a/Koha/REST/V1/Items.pm b/Koha/REST/V1/Items.pm index d3c5d7e177..6d319853a4 100644 --- a/Koha/REST/V1/Items.pm +++ b/Koha/REST/V1/Items.pm @@ -148,4 +148,64 @@ sub pickup_locations { }; } +=head3 checkout + +Check out an item + +=cut + +sub checkout { + my $c = shift->openapi->valid_input or return; + + my $item_id = $c->validation->param('item_id'); + my $item = Koha::Items->find( $item_id ); + + unless ( $item ) { + return $c->render( + status => 404, + openapi => { error => "Item not found" } + ); + } + + my $patron_id = $c->validation->param('patron_id'); + my $patron = Koha::Patrons->find( $patron_id ); + + unless ( $patron ) { + return $c->render( + status => 400, + openapi => { error => "Patron not found" } + ); + } + + try { + my $barcode = $item->barcode; + my ( $issuing_impossible, $needs_confirmation ) = C4::Circulation::CanBookBeIssued( + $patron, $barcode ); + + if ( keys %$issuing_impossible or keys %$needs_confirmation ) { + return $c->render( + status => 403, + openapi => { error => "Checkout not allowed" } + ); + } + + my $borrower = $patron->unblessed; + + my $checkout = C4::Circulation::AddIssue( + $borrower, + $barcode + ); + + $c->res->headers->location( $c->req->url->to_string ); + return $c->render( + status => 201, + openapi => $checkout->to_api + ); + } + catch { + $c->unhandled_exception($_); + }; +} + + 1; diff --git a/api/v1/swagger/paths/items.yaml b/api/v1/swagger/paths/items.yaml index 66fc1ee6a8..420c2fdfde 100644 --- a/api/v1/swagger/paths/items.yaml +++ b/api/v1/swagger/paths/items.yaml @@ -156,3 +156,38 @@ x-koha-authorization: permissions: reserveforothers: place_holds +"/items/{item_id}/checkout/{patron_id}": + post: + x-mojo-to: Items#checkout + operationId: addCheckout + tags: + - items + - checkouts + summary: Add a checkout + parameters: + - $ref: "../swagger.yaml#/parameters/item_id_pp" + - $ref: "../swagger.yaml#/parameters/patron_id_pp" + consumes: + - application/json + produces: + - application/json + responses: + "201": + description: Added checkout + schema: + $ref: "../swagger.yaml#/definitions/checkout" + "400": + description: Patron not found + schema: + $ref: "../swagger.yaml#/definitions/error" + "403": + description: Cannot add checkout + schema: + $ref: "../swagger.yaml#/definitions/error" + "404": + description: Item not found + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + circulate: circulate_remaining_permissions diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index c9291f820e..7d1ce6b292 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -157,6 +157,8 @@ paths: $ref: "./paths/items.yaml#/~1items~1{item_id}" "/items/{item_id}/pickup_locations": $ref: "./paths/items.yaml#/~1items~1{item_id}~1pickup_locations" + "/items/{item_id}/checkout/{patron_id}": + $ref: "./paths/items.yaml#/~1items~1{item_id}~1checkout~1{patron_id}" /jobs: $ref: ./paths/jobs.yaml#/~1jobs "/jobs/{job_id}": diff --git a/t/db_dependent/api/v1/items.t b/t/db_dependent/api/v1/items.t index 41f574ccf7..5058a29f16 100755 --- a/t/db_dependent/api/v1/items.t +++ b/t/db_dependent/api/v1/items.t @@ -19,7 +19,7 @@ use Modern::Perl; -use Test::More tests => 3; +use Test::More tests => 4; use Test::Mojo; use Test::Warn; @@ -321,3 +321,71 @@ subtest 'pickup_locations() tests' => sub { $schema->storage->txn_rollback; }; + +subtest 'checkout() tests' => sub { + + plan tests => 12; + + $schema->storage->txn_begin; + + my $item = $builder->build_sample_item; + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 4 } + } + ); + + # Make sure we have at least 10 items + for ( 1..10 ) { + $builder->build_sample_item; + } + + my $nonprivilegedpatron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 0 } + } + ); + + my $password = 'thePassword123'; + + $nonprivilegedpatron->set_password( + { password => $password, skip_validation => 1 } ); + + my $userid = $nonprivilegedpatron->userid; + my $itemid = $item->id; + my $patronid = $patron->id; + + $t->get_ok( "//$userid:$password@/api/v1/items/$itemid/checkout/$patronid" ) + ->status_is(403) + ->json_is( + '/error' => 'Authorization failure. Missing required permission(s).' ); + + $t->get_ok( "//$userid:$password@/api/v1/items/" . $non_existent_code ) + ->status_is(404) + ->json_is( + '/error' => 'Item not found' ); + + $userid = $patron->userid; + + $t->get_ok( "//$userid:$password@/api/v1/items?_per_page=10" ) + ->status_is( 200, 'SWAGGER3.2.2' ); + + my $response_count = scalar @{ $t->tx->res->json }; + + is( $response_count, 10, 'The API returns 10 items' ); + + $t->get_ok( "//$userid:$password@/api/v1/items?external_id=" . $item->barcode ) + ->status_is(200) + ->json_is( '' => [ $item->to_api ], 'SWAGGER3.3.2'); + + my $barcode = $item->barcode; + $item->delete; + + $t->get_ok( "//$userid:$password@/api/v1/items?external_id=" . $item->barcode ) + ->status_is(200) + ->json_is( '' => [] ); + + $schema->storage->txn_rollback; +}; -- 2.30.2