From 39ca5ef2c333b071852d8e89ecaef14eccc321a5 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 | 71 +++++++++++++++++++++++---------- api/v1/swagger/paths/items.yaml | 35 ++++++++++++++++ api/v1/swagger/swagger.yaml | 2 + t/db_dependent/api/v1/items.t | 69 ++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 20 deletions(-) diff --git a/Koha/REST/V1/Items.pm b/Koha/REST/V1/Items.pm index 938ffcd659..5237f80d10 100644 --- a/Koha/REST/V1/Items.pm +++ b/Koha/REST/V1/Items.pm @@ -27,19 +27,13 @@ use List::MoreUtils qw( any ); use Try::Tiny qw( catch try ); =head1 NAME - Koha::REST::V1::Items - Koha REST API for handling items (V1) - =head1 API - =head2 Methods - =cut =head3 list - Controller function that handles listing Koha::Item objects - =cut sub list { @@ -59,9 +53,7 @@ sub list { } =head3 list_public - Controller function that handles listing Koha::Item objects available to the opac - =cut sub list_public { @@ -85,9 +77,7 @@ sub list_public { } =head3 get - Controller function that handles retrieving a single Koha::Item - =cut sub get { @@ -110,9 +100,7 @@ sub get { } =head3 delete - Controller function that handles deleting a single Koha::Item - =cut sub delete { @@ -173,12 +161,59 @@ sub delete { $c->unhandled_exception($_); }; } +=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($_); + }; +} =head3 pickup_locations - Method that returns the possible pickup_locations for a given item used for building the dropdown selector - =cut sub pickup_locations { @@ -242,9 +277,7 @@ sub pickup_locations { } =head3 bundled_items - Controller function that handles bundled_items Koha::Item objects - =cut sub bundled_items { @@ -274,9 +307,7 @@ sub bundled_items { } =head3 add_to_bundle - Controller function that handles adding items to this bundle - =cut sub add_to_bundle { @@ -371,9 +402,7 @@ sub add_to_bundle { } =head3 remove_from_bundle - Controller function that handles removing items from this bundle - =cut sub remove_from_bundle { @@ -408,3 +437,5 @@ sub remove_from_bundle { } 1; + + diff --git a/api/v1/swagger/paths/items.yaml b/api/v1/swagger/paths/items.yaml index ac8c313a14..c8029f1dc6 100644 --- a/api/v1/swagger/paths/items.yaml +++ b/api/v1/swagger/paths/items.yaml @@ -396,6 +396,41 @@ 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 "/public/items": get: x-mojo-to: Items#list_public diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index 23c59b96a1..562a8f9d07 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -273,6 +273,8 @@ paths: $ref: ./paths/items.yaml#/~1items~1{item_id}~1bundled_items~1{bundled_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 a4a6300652..8ea62c37b9 100755 --- a/t/db_dependent/api/v1/items.t +++ b/t/db_dependent/api/v1/items.t @@ -572,3 +572,72 @@ 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; + my $non_existent_code = $items->itemnumber; + + $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