Bugzilla – Attachment 173194 Details for
Bug 24401
REST API: Check-in
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 24401: Add API endpoint to checkin items
Bug-24401-Add-API-endpoint-to-checkin-items.patch (text/plain), 14.43 KB, created by
David Nind
on 2024-10-23 09:19:31 UTC
(
hide
)
Description:
Bug 24401: Add API endpoint to checkin items
Filename:
MIME Type:
Creator:
David Nind
Created:
2024-10-23 09:19:31 UTC
Size:
14.43 KB
patch
obsolete
>From ab85ae14d5ed757d4eeb8d4c2a32274f7e18ca66 Mon Sep 17 00:00:00 2001 >From: Jan Kissig <jkissig@th-wildau.de> >Date: Tue, 2 Jul 2024 10:27:44 +0200 >Subject: [PATCH] Bug 24401: Add API endpoint to checkin items > > This adds a post /checkin endpoint to return checked out items either by its item_id or barcode. > > To test you can use f.e. ThunderClient extension in codium/vscode. > > Test plan for ktd on main: > > a) enable system preference RESTBasicAuth > b) check out an item and remember its item id or barcode. You can use the checkouts endpoint for this: > > Auth: username: koha & password: koha > Body JSON: > { > "item_id" : 578, > "library_id": "CPL" > } > POST http://localhost:8081/api/v1/checkouts > > c) apply patch and check in item via new checkin endpoint > > Auth: username: koha & password: koha > Body JSON: > { > "item_id" : 578, > "library_id": "CPL" > } > > or > > { > "external_id" : "39999000011418", > "library_id": "CPL" > } > > POST http://localhost:8081/api/v1/checkin > > The endpoint accepts item_id and/or barcode as body params. > > d) check JSON response for return messages > > e) run unit tests: prove t/db_dependent/api/v1/checkin.t > > Signed-off-by: Aleisha Amohia <aleishaamohia@hotmail.com> > >Signed-off-by: David Nind <david@davidnind.com> >--- > Koha/REST/V1/Checkouts.pm | 94 ++++++++++++ > api/v1/swagger/definitions/checkin.yaml | 20 +++ > api/v1/swagger/paths/checkin.yaml | 63 ++++++++ > api/v1/swagger/swagger.yaml | 4 + > t/db_dependent/api/v1/checkin.t | 192 ++++++++++++++++++++++++ > 5 files changed, 373 insertions(+) > create mode 100644 api/v1/swagger/definitions/checkin.yaml > create mode 100644 api/v1/swagger/paths/checkin.yaml > create mode 100755 t/db_dependent/api/v1/checkin.t > >diff --git a/Koha/REST/V1/Checkouts.pm b/Koha/REST/V1/Checkouts.pm >index 301369a45c..d2a7945168 100644 >--- a/Koha/REST/V1/Checkouts.pm >+++ b/Koha/REST/V1/Checkouts.pm >@@ -281,6 +281,100 @@ sub add { > }; > } > >+=head3 checkin >+ >+Check in a checked out item via item_id or barcode >+ >+=cut >+ >+sub checkin { >+ >+ my $c = shift->openapi->valid_input or return; >+ my $body = $c->req->json; >+ my $item_id = $body->{item_id}; >+ my $barcode = $body->{external_id}; >+ my $library_id = $body->{library_id}; >+ >+ my $exemptfine = $body->{exemptfine}; >+ >+ return try { >+ >+ unless ( $item_id or $barcode ) { >+ return $c->render( >+ status => 400, >+ openapi => { >+ error => 'Missing or wrong parameters', >+ error_code => 'MISSING_OR_WRONG_PARAMETERS', >+ } >+ ); >+ } >+ >+ my $item; >+ if ($item_id) { >+ $item = Koha::Items->find($item_id); >+ } >+ else { >+ $item = Koha::Items->find( { barcode => $barcode } ); >+ } >+ >+ unless ($item) { >+ return $c->render( >+ status => 404, >+ openapi => { >+ error => 'Item not found', >+ error_code => 'ITEM_NOT_FOUND', >+ } >+ ); >+ } >+ >+ # check that item matches when barcode and item_id were given >+ if ( $item_id >+ and $barcode >+ and ( $item->barcode ne $barcode or $item->id != $item_id ) ) >+ { >+ return $c->render( >+ status => 409, >+ openapi => { >+ error => >+ 'Item id and external id belong to different items', >+ error_code => 'ITEM_ID_NOT_MATCHING_EXTERNAL_ID', >+ } >+ ); >+ } >+ >+ my ( $returned, $return_messages ) = >+ C4::Circulation::AddReturn( $item->barcode, $library_id, >+ $exemptfine ); >+ >+ my $response = { returned => $returned, messages => $return_messages }; >+ >+ if ($return_messages->{BadBarcode}){ >+ $response->{error} = 'Item not found.'; >+ return $c->render( >+ status => 409, >+ openapi => $response >+ ); >+ } >+ >+ unless ($returned) { >+ return $c->render( >+ status => 403, >+ openapi => $response >+ ); >+ } >+ >+ $c->res->headers->location( $c->req->url->to_string ); >+ return $c->render( >+ status => 201, >+ openapi => $response >+ ); >+ >+ } >+ catch { >+ $c->unhandled_exception($_); >+ }; >+} >+ > =head3 get_renewals > > List Koha::Checkout::Renewals >diff --git a/api/v1/swagger/definitions/checkin.yaml b/api/v1/swagger/definitions/checkin.yaml >new file mode 100644 >index 0000000000..29bae3b611 >--- /dev/null >+++ b/api/v1/swagger/definitions/checkin.yaml >@@ -0,0 +1,20 @@ >+--- >+type: object >+properties: >+ returned: >+ type: integer >+ description: True if return checkin succeeded >+ readOnly: true >+ messages: >+ description: Messages to the returned item >+ type: object >+ item_id: >+ type: integer >+ description: Internal item identifier >+ external_id: >+ type: string >+ description: Barcode of the item >+ exemptfine: >+ type: boolean >+ description: If true overdue charges for the item will be removed >+additionalProperties: true >\ No newline at end of file >diff --git a/api/v1/swagger/paths/checkin.yaml b/api/v1/swagger/paths/checkin.yaml >new file mode 100644 >index 0000000000..0820ca322e >--- /dev/null >+++ b/api/v1/swagger/paths/checkin.yaml >@@ -0,0 +1,63 @@ >+--- >+/checkin: >+ post: >+ x-mojo-to: Checkouts#checkin >+ operationId: addCheckin >+ tags: >+ - checkin >+ - patrons >+ summary: Add a checkin >+ parameters: >+ - name: body >+ in: body >+ description: A JSON object containing information about the checkin >+ required: true >+ schema: >+ $ref: "../swagger.yaml#/definitions/checkin" >+ consumes: >+ - application/json >+ produces: >+ - application/json >+ responses: >+ "201": >+ description: Created checkin >+ schema: >+ $ref: "../swagger.yaml#/definitions/checkin" >+ "400": >+ description: Missing or wrong parameters >+ schema: >+ $ref: "../swagger.yaml#/definitions/error" >+ "401": >+ description: Authentication required >+ schema: >+ $ref: "../swagger.yaml#/definitions/error" >+ "403": >+ description: Cannot create checkin >+ schema: >+ $ref: "../swagger.yaml#/definitions/checkin" >+ "404": >+ description: Item not found >+ schema: >+ $ref: "../swagger.yaml#/definitions/error" >+ "409": >+ description: Conflict in creating checkin >+ schema: >+ $ref: "../swagger.yaml#/definitions/error" >+ "412": >+ description: Precondition failed >+ 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 >diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml >index dd72361380..bcc80ea793 100644 >--- a/api/v1/swagger/swagger.yaml >+++ b/api/v1/swagger/swagger.yaml >@@ -26,6 +26,8 @@ definitions: > $ref: ./definitions/cash_register.yaml > cashup: > $ref: ./definitions/cashup.yaml >+ checkin: >+ $ref: ./definitions/checkin.yaml > checkout: > $ref: ./definitions/checkout.yaml > checkouts: >@@ -271,6 +273,8 @@ paths: > $ref: "./paths/cash_registers.yaml#/~1cash_registers~1{cash_register_id}~1cashups" > "/cashups/{cashup_id}": > $ref: "./paths/cash_registers.yaml#/~1cashups~1{cashup_id}" >+ /checkin: >+ $ref: ./paths/checkin.yaml#/~1checkin > /checkouts: > $ref: ./paths/checkouts.yaml#/~1checkouts > "/checkouts/{checkout_id}": >diff --git a/t/db_dependent/api/v1/checkin.t b/t/db_dependent/api/v1/checkin.t >new file mode 100755 >index 0000000000..5c00ebbec8 >--- /dev/null >+++ b/t/db_dependent/api/v1/checkin.t >@@ -0,0 +1,192 @@ >+#!/usr/bin/env perl >+ >+# 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 Test::More tests => 22; >+use Test::MockModule; >+use Test::Mojo; >+use t::lib::Mocks; >+use t::lib::TestBuilder; >+ >+use DateTime; >+ >+use C4::Context; >+use C4::Circulation qw( AddIssue AddReturn CanBookBeIssued ); >+ >+use Koha::Database; >+use Koha::DateUtils qw( dt_from_string output_pref ); >+use Koha::Token; >+ >+my $schema = Koha::Database->schema; >+my $builder = t::lib::TestBuilder->new; >+ >+t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); >+my $t = Test::Mojo->new('Koha::REST::V1'); >+ >+$schema->storage->txn_begin; >+ >+my $dbh = C4::Context->dbh; >+ >+my $branchcode = $builder->build( { source => 'Branch' } )->{branchcode}; >+my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2, branchcode => $branchcode } >+ } >+); >+my $password = 'thePassword123'; >+$librarian->set_password( { password => $password, skip_validation => 1 } ); >+my $userid = $librarian->userid; >+ >+my $other_branchcode = $builder->build( { source => 'Branch' } )->{branchcode}; >+my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0, branchcode => $other_branchcode }, >+ } >+); >+my $unauth_password = 'thePassword000'; >+$patron->set_password( >+ { password => $unauth_password, skip_validattion => 1 } ); >+my $unauth_userid = $patron->userid; >+my $patron_id = $patron->borrowernumber; >+ >+my $item1 = $builder->build_sample_item; >+my $item1_id = $item1->id; >+ >+my $itemtype = $builder->build( { source => 'Itemtype' } )->{itemtype}; >+my $biblio_2 = $builder->build_sample_biblio; >+my $item_2 = $builder->build_sample_item( >+ { biblionumber => $biblio_2->biblionumber, itype => $itemtype } ); >+my $item_2_barcode = $item_2->barcode; >+my $item_2_id = $item_2->id; >+ >+my $not_issued_item = $builder->build_sample_item; >+my $not_issued_item_id = $not_issued_item->id; >+my $not_issued_item_barcode = $not_issued_item->barcode; >+ >+my $barcode = '321321321321'; >+my $matching_items = Koha::Items->search( { barcode => $barcode } ); >+while ( my $item = $matching_items->next ) { >+ $item->delete; >+} >+ >+my $non_existent_item = $builder->build_sample_item; >+my $non_existent_item_id = $non_existent_item->id; >+my $non_existent_item_barcode = $non_existent_item->barcode; >+$non_existent_item->delete; >+ >+my $checkout = $builder->build_object( >+ { >+ class => 'Koha::Checkouts', >+ value => { >+ itemnumber => $item1_id, >+ branchcode => $branchcode, >+ borrowernumber => $patron_id, >+ note => undef, >+ notedate => undef, >+ noteseen => undef >+ } >+ } >+); >+my $checkout_id = $checkout->id; >+ >+my $reserved_checkout = $builder->build_object( >+ { >+ class => 'Koha::Checkouts', >+ value => { >+ itemnumber => $item_2_id, >+ branchcode => $branchcode, >+ borrowernumber => $patron_id, >+ note => undef, >+ notedate => undef, >+ noteseen => undef >+ } >+ } >+); >+my $reserved_checkout_id = $reserved_checkout->id; >+ >+$dbh->do('DELETE FROM reserves'); >+Koha::CirculationRules->search()->delete(); >+Koha::CirculationRules->set_rules( >+ { >+ categorycode => undef, >+ branchcode => undef, >+ itemtype => undef, >+ rules => { >+ reservesallowed => 1, >+ holds_per_record => 99 >+ } >+ } >+); >+ >+my $reserve_id = C4::Reserves::AddReserve( >+ { >+ branchcode => $branchcode, >+ borrowernumber => $patron->borrowernumber, >+ biblionumber => $biblio_2->biblionumber, >+ priority => 1, >+ itemnumber => $item_2->itemnumber, >+ } >+); >+ >+# empty checkin >+$t->post_ok( "//$userid:$password@/api/v1/checkin" => json => {} ) >+ ->status_is(400); >+ >+# checkin on unknow item id >+$t->post_ok( "//$userid:$password@/api/v1/checkin" => json => >+ { item_id => $non_existent_item_id } )->status_is(404) >+ ->json_is( { error => 'Item not found', error_code => 'ITEM_NOT_FOUND' } ); >+ >+# checkin with unknow barcode >+$t->post_ok( "//$userid:$password@/api/v1/checkin" => json => >+ { external_id => $non_existent_item_barcode } )->status_is(404) >+ ->json_is( { error => 'Item not found', error_code => 'ITEM_NOT_FOUND' } ); >+ >+# not isued >+$t->post_ok( "//$userid:$password@/api/v1/checkin" => json => >+ { item_id => $not_issued_item_id, library_id => $branchcode } ) >+ ->status_is(403)->json_is( '/returned' => 0 ) >+ ->json_has( '/messages' => { NotIssued => $not_issued_item_barcode } ); >+ >+# checkin okay >+$t->post_ok( "//$userid:$password@/api/v1/checkin" => json => >+ { item_id => $item1_id, library_id => $branchcode } )->status_is(201) >+ ->json_is( '/returned' => 1 ) >+ ->json_has( >+ '/messages' => { TransferTrigger => 'ReturnToHome', WasReturned => '1' } ); >+ >+#mismatch of item_id and barcode when both given >+$t->post_ok( >+ "//$userid:$password@/api/v1/checkin" => json => { >+ item_id => $not_issued_item_id, >+ external_id => $item_2_barcode, >+ library_id => $branchcode >+ } >+)->status_is(409); >+ >+# reserved item >+$t->post_ok( "//$userid:$password@/api/v1/checkin" => json => >+ { item_id => $item_2_id, library_id => $branchcode } )->status_is(201) >+ ->json_is( '/returned' => 1 )->json_has( >+ '/messages' => { WasReturned => '1' }, >+ '/ResFound' => { "ResFound" => "Reserved" } >+ ); >+ >+$schema->storage->txn_rollback; >-- >2.39.5
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 24401
:
151109
|
151181
|
151182
|
151183
|
151184
|
152717
|
154428
|
154429
|
154430
|
154431
|
156884
|
156885
|
168337
|
172764
|
173189
| 173194