From ca3cc01cefc11dcdd0882f2c79526cb304959056 Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Thu, 16 Sep 2021 09:17:30 +0100 Subject: [PATCH] Bug 29002: Add Booking objects and API classes --- Koha/Biblio.pm | 107 +++++++ Koha/Booking.pm | 220 +++++++++++++++ Koha/Bookings.pm | 59 ++++ Koha/Exceptions/Booking.pm | 15 + Koha/Item.pm | 105 +++++++ Koha/REST/V1/Biblios.pm | 34 +++ Koha/REST/V1/Bookings.pm | 165 +++++++++++ Koha/REST/V1/Items.pm | 34 +++ api/v1/swagger/definitions.json | 3 + api/v1/swagger/definitions/booking.json | 33 +++ api/v1/swagger/parameters.json | 3 + api/v1/swagger/parameters/booking.json | 9 + api/v1/swagger/paths.json | 12 + api/v1/swagger/paths/biblios.json | 98 +++++++ api/v1/swagger/paths/bookings.json | 352 ++++++++++++++++++++++++ api/v1/swagger/paths/items.json | 92 +++++++ 16 files changed, 1341 insertions(+) create mode 100644 Koha/Booking.pm create mode 100644 Koha/Bookings.pm create mode 100644 Koha/Exceptions/Booking.pm create mode 100644 Koha/REST/V1/Bookings.pm create mode 100644 api/v1/swagger/definitions/booking.json create mode 100644 api/v1/swagger/parameters/booking.json create mode 100644 api/v1/swagger/paths/bookings.json diff --git a/Koha/Biblio.pm b/Koha/Biblio.pm index 26ee7abd96..eda94849c4 100644 --- a/Koha/Biblio.pm +++ b/Koha/Biblio.pm @@ -35,6 +35,7 @@ use Koha::ArticleRequest::Status; use Koha::ArticleRequests; use Koha::Biblio::Metadatas; use Koha::Biblioitems; +use Koha::Bookings; use Koha::CirculationRules; use Koha::Item::Transfer::Limits; use Koha::Items; @@ -132,6 +133,98 @@ sub can_article_request { return q{}; } +=head3 can_by_booked + + my $bookable = + $biblio->can_be_booked( { start_date => $datetime, end_date => $datetime, [ booking_id => $booking_id ] } ); + +Returns a boolean denoting whether the passed booking can be made without clashing. + +Optionally, you may pass a booking id to exclude from the checks; This is helpful when you are updating an existing booking. + +=cut + +sub can_be_booked { + my ( $self, $params ) = @_; + + my $start_date = dt_from_string( $params->{start_date} ); + my $end_date = dt_from_string( $params->{end_date} ); + my $booking_id = $params->{booking_id}; + + my $bookable_items = $self->items; + my $total_bookable = $bookable_items->count; + + my $dtf = Koha::Database->new->schema->storage->datetime_parser; + my $existing_bookings = $self->bookings( + [ + start_date => { + '-between' => [ + $dtf->format_datetime($start_date), + $dtf->format_datetime($end_date) + ] + }, + end_date => { + '-between' => [ + $dtf->format_datetime($start_date), + $dtf->format_datetime($end_date) + ] + }, + { + start_date => { '<' => $dtf->format_datetime($start_date) }, + end_date => { '>' => $dtf->format_datetime($end_date) } + } + ] + ); + + my $booked_count = + defined($booking_id) + ? $existing_bookings->search( { booking_id => { '!=' => $booking_id } } ) + ->count + : $existing_bookings->count; + return ( ( $total_bookable - $booked_count ) > 0 ) ? 1 : 0; +} + +=head3 place_booking + + my $booking = $biblio->place_booking( + { + patron => $patron, + start_date => $datetime, + end_date => $datetime + } + ); + +Add a booking for this item for the dates passed. + +Returns the Koha::Booking object or throws an exception if the item cannot be booked for the given dates. + +=cut + +sub place_booking { + my ( $self, $params ) = @_; + + # check for mandatory params + my @mandatory = ( 'start_date', 'end_date', 'patron' ); + for my $param (@mandatory) { + unless ( defined( $params->{$param} ) ) { + Koha::Exceptions::MissingParameter->throw( + error => "The $param parameter is mandatory" ); + } + } + my $patron = $params->{patron}; + + # New booking object + my $booking = Koha::Booking->new( + { + start_date => $params->{start_date}, + end_date => $params->{end_date}, + borrowernumber => $patron->borrowernumber, + biblionumber => $self->biblionumber + } + )->store(); + return $booking; +} + =head3 can_be_transferred $biblio->can_be_transferred({ to => $to_library, from => $from_library }) @@ -480,6 +573,20 @@ sub biblioitem { return $self->{_biblioitem}; } +=head3 bookings + + my $bookings = $item->bookings(); + +Returns the bookings attached to this biblio. + +=cut + +sub bookings { + my ( $self, $params ) = @_; + my $bookings_rs = $self->_result->bookings->search($params); + return Koha::Bookings->_new_from_dbic( $bookings_rs ); +} + =head3 suggestions my $suggestions = $self->suggestions diff --git a/Koha/Booking.pm b/Koha/Booking.pm new file mode 100644 index 0000000000..308e3899cd --- /dev/null +++ b/Koha/Booking.pm @@ -0,0 +1,220 @@ +package Koha::Booking; + +# Copyright PTFS Europe 2021 +# +# 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 Koha::Exceptions::Booking; +use Koha::DateUtils qw( dt_from_string ); + +use base qw(Koha::Object); + +=head1 NAME + +Koha::Booking - Koha Booking object class + +=head1 API + +=head2 Class methods + +=head3 biblio + +Returns the related Koha::Biblio object for this booking + +=cut + +sub biblio { + my ($self) = @_; + + $self->{_biblio} ||= Koha::Biblios->find( $self->biblionumber() ); + + return $self->{_biblio}; +} + +=head3 patron + +Returns the related Koha::Patron object for this booking + +=cut + +sub patron { + my ($self) = @_; + + my $patron_rs = $self->_result->patron; + return Koha::Patron->_new_from_dbic($patron_rs); +} + +=head3 item + +Returns the related Koha::Item object for this Booking + +=cut + +sub item { + my ($self) = @_; + + $self->{_item} ||= Koha::Items->find( $self->itemnumber() ); + + return $self->{_item}; +} + +=head3 store + +Booking specific store method to catch booking clashes + +=cut + +sub store { + my ($self) = @_; + + $self->_result->result_source->schema->txn_do( + sub { + if ( $self->itemnumber ) { + Koha::Exceptions::Object::FKConstraint->throw( + broken_fk => 'itemnumber', + value => $self->itemnumber, + ) unless ( $self->item ); + + $self->biblionumber( $self->item->biblionumber ) + unless $self->biblionumber; + + Koha::Exceptions::Object::FKConstraint->throw() + unless ( $self->biblionumber == $self->item->biblionumber ); + } + + Koha::Exceptions::Object::FKConstraint->throw( + broken_fk => 'biblionumber', + value => $self->biblionumber, + ) unless ( $self->biblio ); + + # Throw exception for item level booking clash + Koha::Exceptions::Booking::Clash->throw() + if $self->itemnumber && !$self->item->can_be_booked( + { + start_date => $self->start_date, + end_date => $self->end_date, + booking_id => $self->in_storage ? $self->booking_id : undef + } + ); + + # Throw exception for biblio level booking clash + Koha::Exceptions::Booking::Clash->throw() + if !$self->biblio->can_be_booked( + { + start_date => $self->start_date, + end_date => $self->end_date, + booking_id => $self->in_storage ? $self->booking_id : undef + } + ); + + $self = $self->SUPER::store; + } + ); + + return $self; +} + +=head3 intersects + + my $intersects = $booking1->intersects($booking2); + +Returns a boolean denoting whether booking1 interfers/overlaps/clashes with booking2. + +=cut + +sub intersects { + my ( $self, $comp ) = @_; + + # Start date of comparison booking is after end date of this booking. + return 0 + if ( + DateTime->compare( + dt_from_string( $comp->start_date ), + dt_from_string( $self->end_date ) + ) >= 0 + ); + + # End date of comparison booking is before start date of this booking. + return 0 + if ( + DateTime->compare( + dt_from_string( $comp->end_date ), + dt_from_string( $self->start_date ) + ) <= 0 + ); + + # Bookings must overlap + return 1; +} + +=head3 get_items_that_can_fill + + my $items = $bookings->get_items_that_can_fill(); + +Return the list of items that can fulfill this booking. + +Items that are not: + + in transit + lost + withdrawn + not for loan + not already booked + +=cut + +sub get_items_that_can_fill { + my ($self) = @_; + return; +} + +=head3 to_api_mapping + +This method returns the mapping for representing a Koha::Booking object +on the API. + +=cut + +sub to_api_mapping { + return { + booking_id => 'booking_id', + borrowernumber => 'patron_id', + biblionumber => 'biblio_id', + itemnumber => 'item_id', + start_date => 'start_date', + end_date => 'end_date' + }; +} + +=head2 Internal methods + +=head3 _type + +=cut + +sub _type { + return 'Booking'; +} + +=head1 AUTHORS + +Martin Renvoize + +=cut + +1; diff --git a/Koha/Bookings.pm b/Koha/Bookings.pm new file mode 100644 index 0000000000..44bc032b02 --- /dev/null +++ b/Koha/Bookings.pm @@ -0,0 +1,59 @@ +package Koha::Bookings; + +# Copyright PTFS Europe 2021 +# +# 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 Koha::Database; +use Koha::Booking; + +use base qw(Koha::Objects); + +=head1 NAME + +Koha::Bookings - Koha Booking object set class + +=head1 API + +=head2 Class Methods + +=cut + +=head3 type + +=cut + +sub _type { + return 'Booking'; +} + +=head3 object_class + +=cut + +sub object_class { + return 'Koha::Booking'; +} + +=head1 AUTHOR + +Martin Renvoize + +=cut + +1; diff --git a/Koha/Exceptions/Booking.pm b/Koha/Exceptions/Booking.pm new file mode 100644 index 0000000000..9ebae153cb --- /dev/null +++ b/Koha/Exceptions/Booking.pm @@ -0,0 +1,15 @@ +package Koha::Exceptions::Booking; + +use Modern::Perl; + +use Exception::Class ( + 'Koha::Exceptions::Booking' => { + description => "Something went wrong!" + }, + 'Koha::Exceptions::Booking::Clash' => { + isa => 'Koha::Exceptions::Booking', + description => "Adding or updateing the booking would result in a clash" + }, +); + +1; diff --git a/Koha/Item.pm b/Koha/Item.pm index 60d80a5c3e..d8d4c4a05a 100644 --- a/Koha/Item.pm +++ b/Koha/Item.pm @@ -410,6 +410,111 @@ sub holds { return Koha::Holds->_new_from_dbic( $holds_rs ); } +=head3 bookings + + my $bookings = $item->bookings(); + +Returns the bookings attached to this item. + +=cut + +sub bookings { + my ( $self, $params ) = @_; + my $bookings_rs = $self->_result->bookings->search($params); + return Koha::Bookings->_new_from_dbic( $bookings_rs ); +} + +=head3 can_be_booked + + my $bookable = + $item->can_be_booked( { start_date => $datetime, end_date => $datetime, [ booking_id => $booking_id ] } ); + +Returns a boolean denoting whether the passed booking can be made without clashing. + +Optionally, you may pass a booking id to exclude from the checks; This is helpful when you are updating an existing booking. + +=cut + +sub can_be_booked { + my ($self, $params) = @_; + + my $start_date = dt_from_string( $params->{start_date} ); + my $end_date = dt_from_string( $params->{end_date} ); + my $booking_id = $params->{booking_id}; + + my $dtf = Koha::Database->new->schema->storage->datetime_parser; + my $existing_bookings = $self->bookings( + [ + start_date => { + '-between' => [ + $dtf->format_datetime($start_date), + $dtf->format_datetime($end_date) + ] + }, + end_date => { + '-between' => [ + $dtf->format_datetime($start_date), + $dtf->format_datetime($end_date) + ] + }, + { + start_date => { '<' => $dtf->format_datetime($start_date) }, + end_date => { '>' => $dtf->format_datetime($end_date) } + } + ] + ); + + my $bookings_count = + defined($booking_id) + ? $existing_bookings->search( { booking_id => { '!=' => $booking_id } } ) + ->count + : $existing_bookings->count; + + return $bookings_count ? 0 : 1; +} + +=head3 place_booking + + my $booking = $item->place_booking( + { + patron => $patron, + start_date => $datetime, + end_date => $datetime + } + ); + +Add a booking for this item for the dates passed. + +Returns the Koha::Booking object or throws an exception if the item cannot be booked for the given dates. + +=cut + +sub place_booking { + my ( $self, $params ) = @_; + + # check for mandatory params + my @mandatory = ( 'start_date', 'end_date', 'patron' ); + for my $param (@mandatory) { + unless ( defined( $params->{$param} ) ) { + Koha::Exceptions::MissingParameter->throw( + error => "The $param parameter is mandatory" ); + } + } + my $patron = $params->{patron}; + + # New booking object + my $booking = Koha::Booking->new( + { + start_date => $params->{start_date}, + end_date => $params->{end_date}, + borrowernumber => $patron->borrowernumber, + biblionumber => $self->biblionumber, + itemnumber => $self->itemnumber, + } + )->store(); + return $booking; +} + =head3 request_transfer my $transfer = $item->request_transfer( diff --git a/Koha/REST/V1/Biblios.pm b/Koha/REST/V1/Biblios.pm index 3ddb896e73..dd95d93a37 100644 --- a/Koha/REST/V1/Biblios.pm +++ b/Koha/REST/V1/Biblios.pm @@ -235,6 +235,40 @@ sub get_public { }; } +=head3 get_bookings + +Controller function that handles retrieving biblio's bookings + +=cut + +sub get_bookings { + my $c = shift->openapi->valid_input or return; + + my $biblio = Koha::Biblios->find( { biblionumber => $c->validation->param('biblio_id') }, { prefetch => ['bookings'] } ); + + unless ( $biblio ) { + return $c->render( + status => 404, + openapi => { + error => "Object not found." + } + ); + } + + return try { + + my $bookings_rs = $biblio->bookings; + my $bookings = $c->objects->search( $bookings_rs ); + return $c->render( + status => 200, + openapi => $bookings + ); + } + catch { + $c->unhandled_exception($_); + }; +} + =head3 get_items Controller function that handles retrieving biblio's items diff --git a/Koha/REST/V1/Bookings.pm b/Koha/REST/V1/Bookings.pm new file mode 100644 index 0000000000..cc1ad4a97c --- /dev/null +++ b/Koha/REST/V1/Bookings.pm @@ -0,0 +1,165 @@ +package Koha::REST::V1::Bookings; + +# 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::Bookings; + +use Try::Tiny qw( catch try ); + +=head1 API + +=head2 Methods + +=head3 list + +Controller function that handles retrieving a list of bookings + +=cut + +sub list { + my $c = shift->openapi->valid_input or return; + + return try { + my $bookings_set = Koha::Bookings->new; + my $bookings = $c->objects->search($bookings_set); + return $c->render( status => 200, openapi => $bookings ); + } + catch { + $c->unhandled_exception($_); + }; + +} + +=head3 get + +Controller function that handles retrieving a single booking + +=cut + +sub get { + my $c = shift->openapi->valid_input or return; + + return try { + my $booking = + Koha::Bookings->find( $c->validation->param('booking_id') ); + unless ($booking) { + return $c->render( + status => 404, + openapi => { error => "Booking not found" } + ); + } + + return $c->render( status => 200, openapi => $booking->to_api ); + } + catch { + $c->unhandled_exception($_); + } +} + +=head3 add + +Controller function that handles adding a new booking + +=cut + +sub add { + my $c = shift->openapi->valid_input or return; + + return try { + my $booking = + Koha::Booking->new_from_api( $c->validation->param('body') ); + $booking->store; + $c->res->headers->location( + $c->req->url->to_string . '/' . $booking->booking_id ); + return $c->render( + status => 201, + openapi => $booking->to_api + ); + } + catch { + if ( blessed $_ and $_->isa('Koha::Exceptions::Booking::Clash') ) { + return $c->render( + status => 400, + openapi => { error => "Booking would conflict" } + ); + } + + return $c->unhandled_exception($_); + }; +} + +=head3 update + +Controller function that handles updating an existing booking + +=cut + +sub update { + my $c = shift->openapi->valid_input or return; + + my $booking = Koha::Bookings->find( $c->validation->param('booking_id') ); + + if ( not defined $booking ) { + return $c->render( + status => 404, + openapi => { error => "Object not found" } + ); + } + + return try { + $booking->set_from_api( $c->validation->param('body') ); + $booking->store(); + return $c->render( status => 200, openapi => $booking->to_api ); + } + catch { + $c->unhandled_exception($_); + }; +} + +=head3 delete + +Controller function that handles removing an existing booking + +=cut + +sub delete { + my $c = shift->openapi->valid_input or return; + + my $booking = Koha::Bookings->find( $c->validation->param('booking_id') ); + if ( not defined $booking ) { + return $c->render( + status => 404, + openapi => { error => "Object not found" } + ); + } + + return try { + $booking->delete; + return $c->render( + status => 204, + openapi => q{} + ); + } + catch { + $c->unhandled_exception($_); + }; +} + +1; diff --git a/Koha/REST/V1/Items.pm b/Koha/REST/V1/Items.pm index 1887bce514..fcb06dc04c 100644 --- a/Koha/REST/V1/Items.pm +++ b/Koha/REST/V1/Items.pm @@ -81,6 +81,40 @@ sub get { }; } +=head3 get_bookings + +Controller function that handles retrieving item's bookings + +=cut + +sub get_bookings { + my $c = shift->openapi->valid_input or return; + + my $item = Koha::Items->find( { itemnumber => $c->validation->param('item_id') }, { prefetch => ['bookings'] } ); + + unless ( $item ) { + return $c->render( + status => 404, + openapi => { + error => "Object not found." + } + ); + } + + return try { + + my $bookings_rs = $item->bookings; + my $bookings = $c->objects->search( $bookings_rs ); + return $c->render( + status => 200, + openapi => $bookings + ); + } + catch { + $c->unhandled_exception($_); + }; +} + =head3 pickup_locations Method that returns the possible pickup_locations for a given item diff --git a/api/v1/swagger/definitions.json b/api/v1/swagger/definitions.json index 402eed6b25..09d380fa61 100644 --- a/api/v1/swagger/definitions.json +++ b/api/v1/swagger/definitions.json @@ -5,6 +5,9 @@ "basket": { "$ref": "definitions/basket.json" }, + "booking": { + "$ref": "definitions/booking.json" + }, "cashup": { "$ref": "definitions/cashup.json" }, diff --git a/api/v1/swagger/definitions/booking.json b/api/v1/swagger/definitions/booking.json new file mode 100644 index 0000000000..f2cc461e1a --- /dev/null +++ b/api/v1/swagger/definitions/booking.json @@ -0,0 +1,33 @@ +{ + "type": "object", + "properties": { + "booking_id": { + "type": "integer", + "description": "Internal booking identifier" + }, + "biblio_id": { + "type": "integer", + "description": "Internal identifier for the parent bibliographic record" + }, + "item_id": { + "type": [ "integer", "null" ], + "description": "Internal item identifier" + }, + "patron_id": { + "type": "integer", + "description": "Internal patron identifier" + }, + "start_date": { + "type": "string", + "format": "date-time", + "description": "Start date and time of this booking" + }, + "end_date": { + "type": "string", + "format": "date-time", + "description": "Start date and time of this booking" + } + }, + "additionalProperties": false, + "required": ["biblio_id", "item_id", "patron_id", "start_date", "end_date"] +} diff --git a/api/v1/swagger/parameters.json b/api/v1/swagger/parameters.json index 95464e1461..3d5cb73328 100644 --- a/api/v1/swagger/parameters.json +++ b/api/v1/swagger/parameters.json @@ -2,6 +2,9 @@ "biblio_id_pp": { "$ref": "parameters/biblio.json#/biblio_id_pp" }, + "booking_id_pp": { + "$ref": "parameters/booking.json#/booking_id_pp" + }, "advancededitormacro_id_pp": { "$ref": "parameters/advancededitormacro.json#/advancededitormacro_id_pp" }, diff --git a/api/v1/swagger/parameters/booking.json b/api/v1/swagger/parameters/booking.json new file mode 100644 index 0000000000..d1433066fc --- /dev/null +++ b/api/v1/swagger/parameters/booking.json @@ -0,0 +1,9 @@ +{ + "booking_id_pp": { + "name": "booking_id", + "in": "path", + "description": "Booking internal identifier", + "required": true, + "type": "integer" + } +} diff --git a/api/v1/swagger/paths.json b/api/v1/swagger/paths.json index 23f420cc05..25698b461e 100644 --- a/api/v1/swagger/paths.json +++ b/api/v1/swagger/paths.json @@ -20,12 +20,21 @@ "/biblios/{biblio_id}": { "$ref": "paths/biblios.json#/~1biblios~1{biblio_id}" }, + "/biblios/{biblio_id}/bookings": { + "$ref": "paths/biblios.json#/~1biblios~1{biblio_id}~1bookings" + }, "/biblios/{biblio_id}/items": { "$ref": "paths/biblios.json#/~1biblios~1{biblio_id}~1items" }, "/biblios/{biblio_id}/pickup_locations": { "$ref": "paths/biblios.json#/~1biblios~1{biblio_id}~1pickup_locations" }, + "/bookings": { + "$ref": "paths/bookings.json#/~1bookings" + }, + "/bookings/{booking_id}": { + "$ref": "paths/bookings.json#/~1bookings~1{booking_id}" + }, "/cash_registers/{cash_register_id}/cashups": { "$ref": "paths/cash_registers.json#/~1cash_registers~1{cash_register_id}~1cashups" }, @@ -83,6 +92,9 @@ "/items/{item_id}": { "$ref": "paths/items.json#/~1items~1{item_id}" }, + "/items/{item_id}/bookings": { + "$ref": "paths/items.json#/~1items~1{item_id}~1bookings" + }, "/items/{item_id}/pickup_locations": { "$ref": "paths/items.json#/~1items~1{item_id}~1pickup_locations" }, diff --git a/api/v1/swagger/paths/biblios.json b/api/v1/swagger/paths/biblios.json index f1b494ddb1..beeed7c035 100644 --- a/api/v1/swagger/paths/biblios.json +++ b/api/v1/swagger/paths/biblios.json @@ -132,6 +132,104 @@ } } }, + "/biblios/{biblio_id}/bookings": { + "get": { + "x-mojo-to": "Biblios#get_bookings", + "operationId": "getBiblioBookings", + "tags": [ + "bookings" + ], + "summary": "Get bookings for a biblio", + "parameters": [ + { + "$ref": "../parameters.json#/biblio_id_pp" + }, + { + "$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" + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A list of the bookings attached to the record", + "schema": { + "type": "array", + "items": { + "$ref": "../definitions.json#/booking" + } + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "404": { + "description": "Biblio not found", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "406": { + "description": "Not acceptable", + "schema": { + "type": "array", + "description": "Accepted content-types", + "items": { + "type": "string" + } + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "503": { + "description": "Under maintenance", + "schema": { + "$ref": "../definitions.json#/error" + } + } + }, + "x-koha-authorization": { + "permissions": { + "circulation": "1" + } + } + } + }, "/biblios/{biblio_id}/items": { "get": { "x-mojo-to": "Biblios#get_items", diff --git a/api/v1/swagger/paths/bookings.json b/api/v1/swagger/paths/bookings.json new file mode 100644 index 0000000000..1d92c67e8f --- /dev/null +++ b/api/v1/swagger/paths/bookings.json @@ -0,0 +1,352 @@ +{ + "/bookings": { + "get": { + "x-mojo-to": "Bookings#list", + "operationId": "listBookings", + "tags": [ + "bookings" + ], + "summary": "List bookings", + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "biblio_id", + "in": "query", + "description": "Case insensative search on booking biblio_id", + "required": false, + "type": "string" + }, + { + "name": "item_id", + "in": "query", + "description": "Case insensative search on booking item_id", + "required": false, + "type": "string" + }, + { + "name": "patron_id", + "in": "query", + "description": "Case insensative search on booking patron_id", + "required": false, + "type": "string" + }, + { + "name": "start_date", + "in": "query", + "description": "Case Insensative search on booking start_date", + "required": false, + "type": "string" + }, + { + "name": "end_date", + "in": "query", + "description": "Case Insensative search on booking end_date", + "required": false, + "type": "string" + }, + + { + "$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" + } + ], + "responses": { + "200": { + "description": "A list of bookings", + "schema": { + "type": "array", + "items": { + "$ref": "../definitions.json#/booking" + } + } + }, + "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": { + "catalogue": "1" + } + } + }, + "post": { + "x-mojo-to": "Bookings#add", + "operationId": "addBooking", + "tags": [ + "bookings" + ], + "summary": "Add booking", + "parameters": [ + { + "name": "body", + "in": "body", + "description": "A JSON object containing informations about the new booking", + "required": true, + "schema": { + "$ref": "../definitions.json#/booking" + } + } + ], + "produces": [ + "application/json" + ], + "responses": { + "201": { + "description": "Booking added", + "schema": { + "$ref": "../definitions.json#/booking" + } + }, + "400": { + "description": "Client error", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "401": { + "description": "Authentication required", + "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": { + "parameters": "manage_bookings" + } + } + } + }, + "/bookings/{booking_id}": { + "get": { + "x-mojo-to": "Bookings#get", + "operationId": "getBooking", + "tags": [ + "bookings" + ], + "summary": "Get booking", + "parameters": [ + { + "$ref": "../parameters.json#/booking_id_pp" + } + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A booking", + "schema": { + "$ref": "../definitions.json#/booking" + } + }, + "404": { + "description": "Booking 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": { + "catalogue": "1" + } + } + }, + "put": { + "x-mojo-to": "Bookings#update", + "operationId": "updateBooking", + "tags": [ + "bookings" + ], + "summary": "Update booking", + "parameters": [ + { + "$ref": "../parameters.json#/booking_id_pp" + }, + { + "name": "body", + "in": "body", + "description": "A booking object", + "required": true, + "schema": { + "$ref": "../definitions.json#/booking" + } + } + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A booking", + "schema": { + "$ref": "../definitions.json#/booking" + } + }, + "400": { + "description": "Client error", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "404": { + "description": "Booking 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": { + "parameters": "manage_bookings" + } + } + }, + "delete": { + "x-mojo-to": "Bookings#delete", + "operationId": "deleteBooking", + "tags": [ + "bookings" + ], + "summary": "Delete booking", + "parameters": [ + { + "$ref": "../parameters.json#/booking_id_pp" + } + ], + "produces": [ + "application/json" + ], + "responses": { + "204": { + "description": "Booking deleted" + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "404": { + "description": "Booking 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": { + "parameters": "manage_bookings" + } + } + } + } +} diff --git a/api/v1/swagger/paths/items.json b/api/v1/swagger/paths/items.json index 7c18f625ee..7cef274139 100644 --- a/api/v1/swagger/paths/items.json +++ b/api/v1/swagger/paths/items.json @@ -127,6 +127,98 @@ } } }, + "/items/{item_id}/bookings": { + "get": { + "x-mojo-to": "Items#bookings", + "operationId": "getItemBookings", + "summary": "Get existing bookings for an item", + "tags": ["items"], + "parameters": [ + { + "$ref": "../parameters.json#/item_id_pp" + }, + { + "$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" + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Item bookings", + "schema": { + "type": "array", + "items": { + "$ref": "../definitions.json#/booking" + } + } + }, + "400": { + "description": "Missing or wrong parameters", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "404": { + "description": "Item 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": { + "circulation": 1 + } + } + } + }, "/items/{item_id}/pickup_locations": { "get": { "x-mojo-to": "Items#pickup_locations", -- 2.20.1