From 2f74752631aefbcd275013209c2744e3e2c19843 Mon Sep 17 00:00:00 2001 From: Andrew Isherwood Date: Fri, 19 Jul 2019 11:57:38 +0100 Subject: [PATCH] Bug 23336: Add POST /checkouts issue endpoint This patch adds a POST /checkouts issue endpoint for creating checkouts Test plan: - Run unit tests in t/db_dependent/api/v1/checkouts.t - Make POST request to http://yourlibrary/api/v1/checkouts with request body containing at least 'patron_id' & 'barcode' properties, other possible properties are: "due_date": Checkout due date "inprocess": Boolean "ignore_reserves": Boolean "cancel_reserve": Either null, "revert" or "cancel" "issue_date": Checkout issue date "sipmode": Boolean "params": Object containing arbitrary properties that will be passed to CanBookBeRenewed and AddIssue - Observe returned data and HTTP code - 201 for checkout created - 403 for cannot be checked out (with reason in response body) - 404 for either invalid patron or barcode - 500 for something else went wrong --- Koha/REST/V1/Checkouts.pm | 91 ++++++++++++++++++++++++++++ api/v1/swagger/paths/checkouts.json | 117 ++++++++++++++++++++++++++++++++++++ t/db_dependent/api/v1/checkouts.t | 36 ++++++++++- 3 files changed, 243 insertions(+), 1 deletion(-) diff --git a/Koha/REST/V1/Checkouts.pm b/Koha/REST/V1/Checkouts.pm index bb349a78ec..cf03cfc084 100644 --- a/Koha/REST/V1/Checkouts.pm +++ b/Koha/REST/V1/Checkouts.pm @@ -25,6 +25,8 @@ use C4::Context; use C4::Circulation; use Koha::Checkouts; use Koha::IssuingRules; +use Koha::Patrons; +use JSON; use Try::Tiny; @@ -172,6 +174,95 @@ sub allows_renewal { ); } +=head3 add + +Create a checkout + +=cut + +sub add { + my $c = shift->openapi->valid_input or return; + + my $body = $c->validation->param('body'); + + my $patron_id = $body->{'patron_id'}; + my $barcode = $body->{'barcode'}; + my $duedate = $body->{'due_date'}; + my $inprocess = $body->{'inprocess'}; + my $ignore_reserves = $body->{'ignore_reserves'}; + my $cancel_reserve = $body->{'cancel_reserve'}; + my $issuedate = $body->{'issue_date'}; + my $sipmode = $body->{'sipmode'}; + my $params = $body->{'params'}; + + my $patron = Koha::Patrons->find({ borrowernumber => $patron_id }); + my $item = Koha::Items->find({ barcode => $barcode }); + + $c->res->headers->location( $c->req->url->to_string ); + unless ($patron) { + return $c->render( + status => 404, + openapi => { error => "Patron doesn't exist" } + ); + } + + unless ($item) { + return $c->render( + status => 404, + openapi => { error => "Item doesn't exist" } + ); + } + + my ( $error, $confirm, $alerts, $messages ) = + C4::Circulation::CanBookBeIssued( + $patron, + $barcode, + $duedate, + $inprocess, + $ignore_reserves, + $params + ); + + my $problems = {}; + $problems->{error} = $error if %{$error}; + $problems->{confirm} = $confirm if %{$confirm}; + $problems->{alerts} = $alerts if %{$alerts}; + $problems->{messages} = $messages if %{$messages}; + + if (%{$problems}) { + return $c->render( + status => 403, + openapi => { + error => encode_json $problems + } + ); + } + + my $issue = C4::Circulation::AddIssue( + $patron->unblessed, + $barcode, + $duedate, + $cancel_reserve, + $issuedate, + 0, + $params + ); + + if ($issue && %{$issue}) { + return $c->render( + status => 201, + openapi => _to_api( $issue->TO_JSON ) + ); + } else { + return $c->render( + status => 500, + openapi => { error => 'Unknown error during checkout' } + ); + } + + +} + =head3 _to_api Helper function that maps a hashref of Koha::Checkout attributes into REST api diff --git a/api/v1/swagger/paths/checkouts.json b/api/v1/swagger/paths/checkouts.json index 7d89e3c534..66d239784a 100644 --- a/api/v1/swagger/paths/checkouts.json +++ b/api/v1/swagger/paths/checkouts.json @@ -35,6 +35,123 @@ "circulate": "circulate_remaining_permissions" } } + }, + "post": { + "x-mojo-to": "Checkouts#add", + "operationId": "addCheckout", + "tags": ["patrons", "checkouts"], + "parameters": [{ + "name": "body", + "in": "body", + "description": "A JSON object containing information about the new checkout", + "required": true, + "schema": { + "type": "object", + "properties": { + "patron_id": { + "description": "Internal patron identifier", + "type": "string" + }, + "barcode": { + "description": "Internal item barcode", + "type": "string" + }, + "due_date": { + "description": "Checkout due date", + "type": ["string", "null"], + "format": "date" + }, + "inprocess": { + "description": "In process", + "type": ["boolean", "null"] + }, + "ignore_reserves": { + "description": "Should reserves on this item be ignored", + "type": [ "boolean", "null" ] + }, + "cancel_reserve": { + "description": "Should reserves on this item be cancelled", + "type": [ "string", "null" ], + "enum": [ + "revert", + "cancel" + ] + }, + "issue_date": { + "description": "Checkout issue date", + "type": ["string", "null"], + "format": "date" + }, + "sipmode": { + "description": "Flag indicating sipmode", + "type": ["boolean", "null"] + }, + "params": { + "description": "Object holding other arbitrary paramters", + "type": ["object", "null"] + } + }, + "required": [ "patron_id", "barcode" ] + } + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "201": { + "description": "Created checkout", + "schema": { + "$ref": "../definitions.json#/checkout" + } + }, + "400": { + "description": "Missing or wrong parameters", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "403": { + "description": "Checkout not allowed", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "404": { + "description": "Item not found", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "404": { + "description": "Borrower not found", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "503": { + "description": "Under maintenance", + "schema": { + "$ref": "../definitions.json#/error" + } + } + }, + "x-koha-authorization": { + "permissions": { + "circulate": "circulate_remaining_permissions" + } + } } }, "/checkouts/{checkout_id}": { diff --git a/t/db_dependent/api/v1/checkouts.t b/t/db_dependent/api/v1/checkouts.t index 89e16fdb50..1c0e3894e4 100644 --- a/t/db_dependent/api/v1/checkouts.t +++ b/t/db_dependent/api/v1/checkouts.t @@ -17,7 +17,7 @@ use Modern::Perl; -use Test::More tests => 79; +use Test::More tests => 95; use Test::MockModule; use Test::Mojo; use t::lib::Mocks; @@ -196,3 +196,37 @@ $t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id . "/all current_renewals => 1, error => 'too_many' }); + +# Test create checkout POST route +$dbh->do(q{ + UPDATE issuingrules SET issuelength=? +}, {}, 7); +my $itemtype = $builder->build({ + source => 'Itemtype', + value => { notforloan => 0 } +})->{itemtype}; +my $item4 = $builder->build_sample_item({ itype => $itemtype }); +my $expected_issue_datedue = DateTime->now + ->set_time_zone('local') + ->add(days => 7) + ->set(hour => 23, minute => 59, second => 0); + +$t->post_ok( "//$userid:$password@/api/v1/checkouts/" => json => { patron_id => $patron_id, barcode => 'not_here' }) + ->status_is(404) + ->json_is({ error => "Item doesn't exist" }) + ->header_is(Location => "/api/v1/checkouts/"); + +$t->post_ok( "//$userid:$password@/api/v1/checkouts/" => json => { patron_id => 'not_here', barcode => $item4->barcode }) + ->status_is(404) + ->json_is({ error => "Patron doesn't exist" }) + ->header_is(Location => "/api/v1/checkouts/"); + +$t->post_ok( "//$userid:$password@/api/v1/checkouts/" => json => { patron_id => $patron_id, barcode => $item4->barcode }) + ->status_is(201) + ->json_is('/due_date' => output_pref( { dateformat => "rfc3339", dt => $expected_issue_datedue }) ) + ->header_is(Location => "/api/v1/checkouts/"); + +$t->post_ok( "//$userid:$password@/api/v1/checkouts/" => json => { patron_id => $patron_id, barcode => $item4->barcode }) + ->status_is(403) + ->json_is({ error => '{"confirm":{"RENEW_ISSUE":1}}' }) + ->header_is(Location => "/api/v1/checkouts/"); -- 2.11.0