From c366b122f3032e49b8398f049e1fb5824ac694ab Mon Sep 17 00:00:00 2001 From: Tomas Cohen Arazi Date: Fri, 23 Apr 2021 12:04:12 -0300 Subject: [PATCH] Bug 17314: Add routes for purchase suggestions This patch adds routes for handling purchase suggestions. Signed-off-by: Tomas Cohen Arazi --- Koha/REST/V1/Suggestions.pm | 179 +++++++++++++ Koha/Suggestion.pm | 46 ++++ api/v1/swagger/definitions.json | 6 + api/v1/swagger/definitions/suggestion.json | 154 +++++++++++ api/v1/swagger/parameters.json | 3 + api/v1/swagger/parameters/suggestion.json | 9 + api/v1/swagger/paths.json | 6 + api/v1/swagger/paths/suggestions.json | 284 +++++++++++++++++++++ 8 files changed, 687 insertions(+) create mode 100644 Koha/REST/V1/Suggestions.pm create mode 100644 api/v1/swagger/definitions/suggestion.json create mode 100644 api/v1/swagger/parameters/suggestion.json create mode 100644 api/v1/swagger/paths/suggestions.json diff --git a/Koha/REST/V1/Suggestions.pm b/Koha/REST/V1/Suggestions.pm new file mode 100644 index 00000000000..94977fa0c2b --- /dev/null +++ b/Koha/REST/V1/Suggestions.pm @@ -0,0 +1,179 @@ +package Koha::REST::V1::Suggestions; + +# 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 . + +use Modern::Perl; + +use Mojo::Base 'Mojolicious::Controller'; + +use Koha::Suggestions; + +use Try::Tiny; + +=head1 NAME + +Koha::REST::V1::Suggestion + +=head1 API + +=head2 Methods + +=head3 list + +Controller method that handles listing Koha::Suggestion objects + +=cut + +sub list { + my $c = shift->openapi->valid_input or return; + + return try { + + my $suggestions = $c->objects->search( Koha::Suggestions->new ); + + return $c->render( + status => 200, + openapi => $suggestions + ); + } + catch { + $c->unhandled_exception($_); + }; +} + +=head3 get + +Controller method that handles retrieving a single Koha::Suggestion object + +=cut + +sub get { + my $c = shift->openapi->valid_input or return; + + return try { + my $suggestion_id = $c->validation->param('suggestion_id'); + my $suggestion = $c->objects->find( Koha::Suggestions->new, $suggestion_id ); + + unless ($suggestion) { + return $c->render( + status => 404, + openapi => { error => "Suggestion not found." } + ); + } + + return $c->render( + status => 200, + openapi => $suggestion + ); + } + catch { + $c->unhandled_exception($_); + }; +} + +=head3 add + +Controller method that handles adding a new Koha::Suggestion object + +=cut + +sub add { + my $c = shift->openapi->valid_input or return; + + my $body = $c->validation->param('body'); + + # FIXME: This should be handled in Koha::Suggestion->store + $body->{'status'} = 'ASKED' + unless defined $body->{'status'}; + + return try { + my $suggestion = Koha::Suggestion->new_from_api( $body )->store; + $c->res->headers->location( $c->req->url->to_string . '/' . $suggestion->suggestionid ); + + return $c->render( + status => 201, + openapi => $suggestion->to_api + ); + } + catch { + $c->unhandled_exception($_); + }; +} + +=head3 update + +Controller method that handles modifying Koha::Suggestion object + +=cut + +sub update { + my $c = shift->openapi->valid_input or return; + + my $suggestion_id = $c->validation->param('suggestion_id'); + my $suggestion = Koha::Suggestions->find( $suggestion_id ); + + return $c->render( + status => 404, + openapi => { error => 'Suggestion not found.' } + ) unless $suggestion; + + return try { + + my $body = $c->validation->param('body'); + + $suggestion->set_from_api( $body )->store; + $suggestion->discard_changes; + + return $c->render( + status => 200, + openapi => $suggestion->to_api + ); + } + catch { + $c->unhandled_exception($_); + }; + +} + +=head3 delete + +Controller method that handles removing a Koha::Suggestion object + +=cut + +sub delete { + my $c = shift->openapi->valid_input or return; + + my $suggestion_id = $c->validation->param('suggestion_id'); + my $suggestion = Koha::Suggestions->find( $suggestion_id ); + + return $c->render( + status => 404, + openapi => { error => 'Suggestion not found.' } + ) unless $suggestion; + + return try { + $suggestion->delete; + return $c->render( + status => 200, + openapi => q{} + ); + } + catch { + $c->unhandled_exception($_); + }; +} + +1; diff --git a/Koha/Suggestion.pm b/Koha/Suggestion.pm index f9f8c2a4908..9df5a359dd8 100644 --- a/Koha/Suggestion.pm +++ b/Koha/Suggestion.pm @@ -139,6 +139,52 @@ sub _type { return 'Suggestion'; } +=head3 to_api_mapping + +This method returns the mapping for representing a Koha::Patron object +on the API. + +=cut + +sub to_api_mapping { + return { + suggestionid => 'suggestion_id', + suggestedby => 'suggested_by', + suggesteddate => 'suggestion_date', + managedby => 'managed_by', + manageddate => 'managed_date', + acceptedby => 'accepted_by', + accepteddate => 'accepted_date', + rejectedby => 'rejected_by', + rejecteddate => 'rejected_date', + lastmodificationdate => 'last_modification_date', + lastmodificationby => 'last_modification_by', + STATUS => 'status', + note => 'note', + author => 'author', + title => 'title', + copyrightdate => 'copyright_date', + publishercode => 'publisher_code', + date => 'date_created', + volumedesc => 'volume_desc', + publicationyear => 'publication_year', + place => 'publication_place', + isbn => 'isbn', + biblionumber => 'biblio_id', + reason => 'reason', + patronreason => 'patron_reason', + budgetid => 'budget_id', + branchcode => 'library_id', + collectiontitle => 'collection_title', + itemtype => 'item_type', + quantity => 'quantity', + currency => 'currency', + price => 'item_price', + total => 'total_price', + archived => 'archived', + }; +} + =head1 AUTHOR Kyle M Hall diff --git a/api/v1/swagger/definitions.json b/api/v1/swagger/definitions.json index 02dbc95c019..6b47a274e69 100644 --- a/api/v1/swagger/definitions.json +++ b/api/v1/swagger/definitions.json @@ -88,5 +88,11 @@ }, "smtp_server": { "$ref": "definitions/smtp_server.json" + }, + "fund": { + "$ref": "definitions/fund.json" + }, + "suggestion": { + "$ref": "definitions/suggestion.json" } } diff --git a/api/v1/swagger/definitions/suggestion.json b/api/v1/swagger/definitions/suggestion.json new file mode 100644 index 00000000000..11e5095c1e6 --- /dev/null +++ b/api/v1/swagger/definitions/suggestion.json @@ -0,0 +1,154 @@ +{ + "type": "object", + "properties": { + "suggestion_id": { + "type": "string", + "description": "unique identifier assigned automatically by Koha" + }, + "suggested_by": { + "type": ["string", "null"], + "description": "patron_id for the person making the suggestion, foreign key linking to the borrowers table" + }, + "suggestion_date": { + "type": ["string", "null"], + "format": "date", + "description": "the suggestion was submitted" + }, + "managed_by": { + "type": ["string", "null"], + "description": "patron_id for the librarian managing the suggestion, foreign key linking to the borrowers table" + }, + "managed_date": { + "type": ["string", "null"], + "format": "date", + "description": "date the suggestion was updated" + }, + "accepted_by": { + "type": ["string", "null"], + "description": "patron_id for the librarian who accepted the suggestion, foreign key linking to the borrowers table" + }, + "accepted_date": { + "type": ["string", "null"], + "format": "date", + "description": "date the suggestion was marked as accepted" + }, + "rejected_by": { + "type": ["string", "null"], + "description": "patron_id for the librarian who rejected the suggestion, foreign key linking to the borrowers table" + }, + "rejected_date": { + "type": ["string", "null"], + "format": "date", + "description": "date the suggestion was marked as rejected" + }, + "last_modification_by": { + "type": ["string", "null"], + "description": "patron the suggestion was last modified by" + }, + "last_modification_date": { + "type": ["string", "null"], + "format": "date", + "description": "date the suggestion was last modified" + }, + "status": { + "type": "string", + "description": "Suggestion status", + "enum": [ + "ASKED", + "CHECKED", + "ACCEPTED", + "REJECTED" + ] + }, + "note": { + "type": ["string", "null"], + "description": "note entered on the suggestion" + }, + "author": { + "type": ["string", "null"], + "description": "author of the suggested item" + }, + "title": { + "type": ["string", "null"], + "description": "title of the suggested item" + }, + "copyright_date": { + "type": ["string", "null"], + "format": "date", + "description": "copyright date of the suggested item" + }, + "publisher_code": { + "type": ["string", "null"], + "description": "publisher of the suggested item" + }, + "date_created": { + "type": ["string", "null"], + "description": "timestamp of date created" + }, + "volume_desc": { + "type": ["string", "null"], + "description": "volume description" + }, + "publication_year": { + "type": ["string", "null"], + "description": "year of publication" + }, + "publication_place": { + "type": ["string", "null"], + "description": "publication place of the suggested item" + }, + "isbn": { + "type": ["string", "null"], + "description": "isbn of the suggested item" + }, + "biblio_id": { + "type": ["string", "null"], + "description": "foreign key linking the suggestion to the biblio table after the suggestion has been ordered" + }, + "reason": { + "type": ["string", "null"], + "description": "reason for accepting or rejecting the suggestion" + }, + "patron_reason": { + "type": ["string", "null"], + "description": "reason for making the suggestion" + }, + "budget_id": { + "type": ["string", "null"], + "description": "foreign key linking the suggested budget to the aqbudgets table" + }, + "library_id": { + "type": ["string", "null"], + "description": "foreign key linking the suggested branch to the branches table" + }, + "collection_title": { + "type": ["string", "null"], + "description": "collection name for the suggested item" + }, + "item_type": { + "type": ["string", "null"], + "description": "suggested item type" + }, + "quantity": { + "type": ["string", "null"], + "description": "suggested quantity to be purchased" + }, + "currency": { + "type": ["string", "null"], + "description": "suggested currency for the suggested price" + }, + "item_price": { + "type": ["number", "null"], + "description": "suggested price" + }, + "total_price": { + "type": ["string", "null"], + "description": "suggested total cost (price*quantity updated for currency)" + }, + "archived": { + "type": ["boolean", "null"], + "description": "archived (processed) suggestion" + } + }, + "additionalProperties": false +} diff --git a/api/v1/swagger/parameters.json b/api/v1/swagger/parameters.json index 95464e14612..cc8d6186057 100644 --- a/api/v1/swagger/parameters.json +++ b/api/v1/swagger/parameters.json @@ -119,5 +119,8 @@ }, "fundidPathParam": { "$ref": "parameters/fund.json#/fundidPathParam" + }, + "suggestion_id_pp": { + "$ref": "parameters/suggestion.json#/suggestion_id_pp" } } diff --git a/api/v1/swagger/parameters/suggestion.json b/api/v1/swagger/parameters/suggestion.json new file mode 100644 index 00000000000..0de69f476f5 --- /dev/null +++ b/api/v1/swagger/parameters/suggestion.json @@ -0,0 +1,9 @@ +{ + "suggestion_id_pp": { + "name": "suggestion_id", + "in": "path", + "description": "Internal suggestion identifier", + "required": true, + "type": "integer" + } +} diff --git a/api/v1/swagger/paths.json b/api/v1/swagger/paths.json index 917100b6708..f41f472a12f 100644 --- a/api/v1/swagger/paths.json +++ b/api/v1/swagger/paths.json @@ -181,5 +181,11 @@ }, "/return_claims/{claim_id}": { "$ref": "paths/return_claims.json#/~1return_claims~1{claim_id}" + }, + "/suggestions": { + "$ref": "paths/suggestions.json#/~1suggestions" + }, + "/suggestions/{suggestion_id}": { + "$ref": "paths/suggestions.json#/~1suggestions~1{suggestion_id}" } } diff --git a/api/v1/swagger/paths/suggestions.json b/api/v1/swagger/paths/suggestions.json new file mode 100644 index 00000000000..d89aaec3910 --- /dev/null +++ b/api/v1/swagger/paths/suggestions.json @@ -0,0 +1,284 @@ +{ + "/suggestions": { + "get": { + "x-mojo-to": "Suggestions#list", + "operationId" : "list", + "tags": ["patrons", "suggestions"], + "parameters": [ + { + "$ref": "../parameters.json#/match" + }, + { + "$ref": "../parameters.json#/order_by" + }, + { + "$ref": "../parameters.json#/page" + }, + { + "$ref": "../parameters.json#/per_page" + }, + { + "$ref": "../parameters.json#/q_param" + }, + { + "$ref": "../parameters.json#/q_body" + }, + { + "$ref": "../parameters.json#/q_header" + } + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A list of suggestions", + "schema": { + "type": "array", + "items": { + "$ref": "../definitions.json#/suggestion" + } + } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "500": { + "description": "Internal error", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "503": { + "description": "Under maintenance", + "schema": { + "$ref": "../definitions.json#/error" + } + } + }, + "x-koha-authorization": { + "permissions": { + "acquisition": "1" + } + } + }, + "post": { + "x-mojo-to": "Suggestions#add", + "operationId": "add", + "tags": ["patrons", "suggestions"], + "parameters": [{ + "name": "body", + "in": "body", + "description": "A JSON object containing informations about the new suggestion", + "required": true, + "schema": { + "$ref": "../definitions.json#/suggestion" + } + }], + "produces": [ + "application/json" + ], + "responses": { + "201": { + "description": "Suggestion added", + "schema": { + "$ref": "../definitions.json#/suggestion" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "500": { + "description": "Internal error", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "503": { + "description": "Under maintenance", + "schema": { + "$ref": "../definitions.json#/error" + } + } + }, + "x-koha-authorization": { + "permissions": { + "acquisition": "1" + } + } + } + }, + "/suggestions/{suggestion_id}": { + "get": { + "x-mojo-to": "Suggestions#get", + "operationId": "getSuggestions", + "tags": ["patrons", "suggestions"], + "parameters": [{ + "$ref": "../parameters.json#/suggestion_id_pp" + } + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A suggestion", + "schema": { + "$ref": "../definitions.json#/suggestion" + } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "404": { + "description": "Suggestion not found", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "500": { + "description": "Internal error", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "503": { + "description": "Under maintenance", + "schema": { + "$ref": "../definitions.json#/error" + } + } + }, + "x-koha-authorization": { + "permissions": { + "acquisition": "1" + } + } + }, + "put": { + "x-mojo-to": "Suggestions#update", + "operationId": "update", + "tags": ["patrons", "suggestions"], + "parameters": [{ + "$ref": "../parameters.json#/suggestion_id_pp" + }, { + "name": "body", + "in": "body", + "description": "A JSON object containing informations about the new hold", + "required": true, + "schema": { + "$ref": "../definitions.json#/suggestion" + } + }], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A suggestion", + "schema": { + "$ref": "../definitions.json#/suggestion" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "404": { + "description": "Suggestion not found", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "500": { + "description": "Internal error", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "503": { + "description": "Under maintenance", + "schema": { + "$ref": "../definitions.json#/error" + } + } + }, + "x-koha-authorization": { + "permissions": { + "acquisition": "1" + } + } + }, + "delete": { + "x-mojo-to": "Suggestions#delete", + "operationId": "delete", + "tags": ["patrons", "suggestions"], + "parameters": [{ + "$ref": "../parameters.json#/suggestion_id_pp" + }], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Suggestion deleted", + "schema": { + "type": "string" + } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "404": { + "description": "Suggestion not found", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "500": { + "description": "Internal error", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "503": { + "description": "Under maintenance", + "schema": { + "$ref": "../definitions.json#/error" + } + } + }, + "x-koha-authorization": { + "permissions": { + "acquisition": "1" + } + } + } + } +} -- 2.31.1