From 7c98b4571fabdb4f1aa8e8858933254a509200e6 Mon Sep 17 00:00:00 2001 From: Andrew Isherwood Date: Wed, 11 May 2022 13:34:58 +0100 Subject: [PATCH] Bug 30719: DB, API & tests This commit adds the following: - A database update that adds an "illbatches" table, a "illrequest.batch_id" foreign key and an "illbatches.borrowernumber" foreign key - Illbatch and Illbatches Koha objects - Adds 'batch' accessor to Illrequest object - CRUD API endpoints for working with ILL batches - Unit tests of the Koha objects and API endpoints - Adds 'batch_id' filter to /illrequests endpoint Signed-off-by: Pedro Amorim --- Koha/Illbatch.pm | 95 ++++ Koha/Illbatches.pm | 61 +++ Koha/Illrequest.pm | 18 + Koha/REST/V1/Illbatches.pm | 219 +++++++++ Koha/REST/V1/Illrequests.pm | 5 +- api/v1/swagger/definitions/illbatch.yaml | 39 ++ api/v1/swagger/definitions/illbatches.yaml | 5 + api/v1/swagger/paths/illbatches.yaml | 232 ++++++++++ api/v1/swagger/paths/illrequests.yaml | 5 + api/v1/swagger/swagger.yaml | 17 + .../atomicupdate/bug_30719_add_ill_batches.pl | 38 ++ installer/data/mysql/kohastructure.sql | 20 +- t/db_dependent/Illbatches.t | 96 ++++ t/db_dependent/api/v1/illbatches.t | 417 ++++++++++++++++++ 14 files changed, 1265 insertions(+), 2 deletions(-) create mode 100644 Koha/Illbatch.pm create mode 100644 Koha/Illbatches.pm create mode 100644 Koha/REST/V1/Illbatches.pm create mode 100644 api/v1/swagger/definitions/illbatch.yaml create mode 100644 api/v1/swagger/definitions/illbatches.yaml create mode 100644 api/v1/swagger/paths/illbatches.yaml create mode 100755 installer/data/mysql/atomicupdate/bug_30719_add_ill_batches.pl create mode 100755 t/db_dependent/Illbatches.t create mode 100755 t/db_dependent/api/v1/illbatches.t diff --git a/Koha/Illbatch.pm b/Koha/Illbatch.pm new file mode 100644 index 0000000000..edb2feb029 --- /dev/null +++ b/Koha/Illbatch.pm @@ -0,0 +1,95 @@ +package Koha::Illbatch; + +# Copyright PTFS Europe 2022 +# +# 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 base qw(Koha::Object); + +=head1 NAME + +Koha::Illbatch - Koha Illbatch Object class + +=head2 Class methods + +=head3 patron + + my $patron = Koha::Illbatch->patron; + +Return the patron object associated with this batch + +=cut + +sub patron { + my ( $self ) = @_; + return Koha::Patron->_new_from_dbic( + scalar $self->_result->borrowernumber + ); +} + +=head3 branch + + my $branch = Koha::Illbatch->branch; + +Return the branch object associated with this batch + +=cut + +sub branch { + my ( $self ) = @_; + return Koha::Library->_new_from_dbic( + scalar $self->_result->branchcode + ); +} + +=head3 requests_count + + my $requests_count = Koha::Illbatch->requests_count; + +Return the number of requests associated with this batch + +=cut + +sub requests_count { + my ( $self ) = @_; + return Koha::Illrequests->search({ + batch_id => $self->id + })->count; +} + +=head2 Internal methods + +=head3 _type + + my $type = Koha::Illbatch->_type; + +Return this object's type + +=cut + +sub _type { + return 'Illbatch'; +} + +=head1 AUTHOR + +Andrew Isherwood + +=cut + +1; diff --git a/Koha/Illbatches.pm b/Koha/Illbatches.pm new file mode 100644 index 0000000000..ee65a262d6 --- /dev/null +++ b/Koha/Illbatches.pm @@ -0,0 +1,61 @@ +package Koha::Illbatches; + +# Copyright PTFS Europe 2022 +# +# 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::Illbatch; +use base qw(Koha::Objects); + +=head1 NAME + +Koha::Illbatches - Koha Illbatches Object class + +=head2 Internal methods + +=head3 _type + + my $type = Koha::Illbatches->_type; + +Return this object's type + +=cut + +sub _type { + return 'Illbatch'; +} + +=head3 object_class + + my $class = Koha::Illbatches->object_class; + +Return this object's class name + +=cut + +sub object_class { + return 'Koha::Illbatch'; +} + +=head1 AUTHOR + +Andrew Isherwood + +=cut + +1; diff --git a/Koha/Illrequest.pm b/Koha/Illrequest.pm index 723b11843e..063a3dd2da 100644 --- a/Koha/Illrequest.pm +++ b/Koha/Illrequest.pm @@ -32,6 +32,7 @@ use Koha::Illrequestattributes; use Koha::AuthorisedValue; use Koha::Illrequest::Logger; use Koha::Patron; +use Koha::Illbatches; use Koha::AuthorisedValues; use Koha::Biblios; use Koha::Items; @@ -143,6 +144,23 @@ sub push_processor { push @{$self->{processors}}, $processor; } +=head3 batch + + my $batch = $request->batch; + +Returns the batch associated with a request + +=cut + +sub batch { + my ( $self ) = @_; + + return Koha::Illbatches->find($self->_result->batch_id); +# return Koha::Illbatch->_new_from_dbic( +# scalar $self->_result->batch_id +# ); +} + =head3 statusalias my $statusalias = $request->statusalias; diff --git a/Koha/REST/V1/Illbatches.pm b/Koha/REST/V1/Illbatches.pm new file mode 100644 index 0000000000..af21e762ae --- /dev/null +++ b/Koha/REST/V1/Illbatches.pm @@ -0,0 +1,219 @@ +package Koha::REST::V1::Illbatches; + +# 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::Illbatches; +use Koha::Illrequests; + +=head1 NAME + +Koha::REST::V1::Illbatches + +=head2 Operations + +=head3 list + +Return a list of available ILL batches + +=cut + +sub list { + my $c = shift->openapi->valid_input; + + my @batches = Koha::Illbatches->search()->as_list; + + # Get all patrons associated with all our batches + # in one go + my $patrons = {}; + foreach my $batch(@batches) { + my $patron_id = $batch->borrowernumber; + $patrons->{$patron_id} = 1 + }; + my @patron_ids = keys %{$patrons}; + my $patron_results = Koha::Patrons->search({ + borrowernumber => { -in => \@patron_ids } + }); + + # Get all branches associated with all our batches + # in one go + my $branches = {}; + foreach my $batch(@batches) { + my $branch_id = $batch->branchcode; + $branches->{$branch_id} = 1 + }; + my @branchcodes = keys %{$branches}; + my $branch_results = Koha::Libraries->search({ + branchcode => { -in => \@branchcodes } + }); + + # Populate the response + my @to_return = (); + foreach my $it_batch(@batches) { + my $patron = $patron_results->find({ borrowernumber => $it_batch->borrowernumber}); + my $branch = $branch_results->find({ branchcode => $it_batch->branchcode }); + push @to_return, { + %{$it_batch->unblessed}, + patron => $patron, + branch => $branch, + requests_count => $it_batch->requests_count + }; + } + + return $c->render( status => 200, openapi => \@to_return ); +} + +=head3 get + +Get one batch + +=cut + +sub get { + my $c = shift->openapi->valid_input; + + my $batchid = $c->validation->param('illbatch_id'); + + my $batch = Koha::Illbatches->find($batchid); + + if (not defined $batch) { + return $c->render( + status => 404, + openapi => { error => "ILL batch not found" } + ); + } + + return $c->render( + status => 200, + openapi => { + %{$batch->unblessed}, + patron => $batch->patron->unblessed, + branch => $batch->branch->unblessed, + requests_count => $batch->requests_count + } + ); +} + +=head3 add + +Add a new batch + +=cut + +sub add { + my $c = shift->openapi->valid_input or return; + + my $body = $c->validation->param('body'); + + # We receive cardnumber, so we need to look up the corresponding + # borrowernumber + my $patron = Koha::Patrons->find({ cardnumber => $body->{cardnumber} }); + delete $body->{cardnumber}; + $body->{borrowernumber} = $patron->borrowernumber; + + return try { + my $batch = Koha::Illbatch->new( $body ); + $batch->store; + $c->res->headers->location( $c->req->url->to_string . '/' . $batch->id ); + + my $ret = { + %{$batch->unblessed}, + patron => $batch->patron->unblessed, + branch => $batch->branch->unblessed, + requests_count => 0 + }; + + return $c->render( + status => 201, + openapi => $ret + ); + } + catch { + $c->unhandled_exception($_); + }; +} + +=head3 update + +Update a batch + +=cut + +sub update { + my $c = shift->openapi->valid_input or return; + + my $batch = Koha::Illbatches->find( $c->validation->param('illbatch_id') ); + + if ( not defined $batch ) { + return $c->render( + status => 404, + openapi => { error => "ILL batch not found" } + ); + } + + my $params = $c->req->json; + delete $params->{cardnumber}; + + return try { + $batch->set( $params ); + $batch->store(); + + my $ret = { + %{$batch->unblessed}, + patron => $batch->patron->unblessed, + branch => $batch->branch->unblessed, + requests_count => $batch->requests_count + }; + + return $c->render( + status => 200, + openapi => $ret + ); + } + catch { + $c->unhandled_exception($_); + }; +} + +=head3 delete + +Delete a batch + +=cut + +sub delete { + + my $c = shift->openapi->valid_input or return; + + my $batch = Koha::Illbatches->find( $c->validation->param( 'illbatch_id' ) ); + + if ( not defined $batch ) { + return $c->render( status => 404, openapi => { error => "ILL batch not found" } ); + } + + return try { + $batch->delete; + return $c->render( status => 204, openapi => ''); + } + catch { + $c->unhandled_exception($_); + }; +} + +1; diff --git a/Koha/REST/V1/Illrequests.pm b/Koha/REST/V1/Illrequests.pm index 82c5b5dd08..e3fa0a2363 100644 --- a/Koha/REST/V1/Illrequests.pm +++ b/Koha/REST/V1/Illrequests.pm @@ -61,13 +61,16 @@ sub list { my $hidden_statuses = [ split /\|/, $hidden_statuses_string ]; # Get all requests - # If necessary, only get those from a specified patron + # If necessary, restrict the resultset my @requests = Koha::Illrequests->search({ $hidden_statuses ? ( status => { 'not in' => $hidden_statuses } ) : (), $args->{borrowernumber} ? ( borrowernumber => $args->{borrowernumber} ) + : (), + $args->{batch_id} + ? ( batch_id => $args->{batch_id} ) : () })->as_list; diff --git a/api/v1/swagger/definitions/illbatch.yaml b/api/v1/swagger/definitions/illbatch.yaml new file mode 100644 index 0000000000..2248beae54 --- /dev/null +++ b/api/v1/swagger/definitions/illbatch.yaml @@ -0,0 +1,39 @@ +--- +type: object +properties: + id: + type: string + description: Internal ILL batch identifier + name: + type: string + description: Name of the ILL batch + backend: + type: string + description: Backend name + cardnumber: + type: string + description: Card number of the patron of the ILL batch + borrowernumber: + type: string + description: Borrower number of the patron of the ILL batch + branchcode: + type: string + description: Branch code of the branch of the ILL batch + patron: + type: + - object + - "null" + description: The patron associated with the batch + branch: + type: + - object + - "null" + description: The branch associated with the batch + requests_count: + type: string + description: The number of requests in this batch +additionalProperties: false +required: + - name + - backend + - branchcode \ No newline at end of file diff --git a/api/v1/swagger/definitions/illbatches.yaml b/api/v1/swagger/definitions/illbatches.yaml new file mode 100644 index 0000000000..4db20f480d --- /dev/null +++ b/api/v1/swagger/definitions/illbatches.yaml @@ -0,0 +1,5 @@ +--- +type: array +items: + $ref: "illbatch.yaml" +additionalProperties: false diff --git a/api/v1/swagger/paths/illbatches.yaml b/api/v1/swagger/paths/illbatches.yaml new file mode 100644 index 0000000000..d19df76f75 --- /dev/null +++ b/api/v1/swagger/paths/illbatches.yaml @@ -0,0 +1,232 @@ +--- +/illbatches: + get: + x-mojo-to: Illbatches#list + operationId: listIllbatches + tags: + - illbatches + summary: List ILL batches + parameters: [] + produces: + - application/json + responses: + "200": + description: A list of ILL batches + schema: + $ref: "../swagger.yaml#/definitions/illbatches" + "401": + description: Authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + "403": + description: Access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + "404": + description: ILL batches not found + 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: + ill: "1" + post: + x-mojo-to: Illbatches#add + operationId: addIllbatch + tags: + - illbatches + summary: Add ILL batch + parameters: + - name: body + in: body + description: A JSON object containing informations about the new batch + required: true + schema: + $ref: "../swagger.yaml#/definitions/illbatch" + produces: + - application/json + responses: + "201": + description: Batch added + schema: + $ref: "../swagger.yaml#/definitions/illbatch" + "400": + description: Bad request + schema: + $ref: "../swagger.yaml#/definitions/error" + "401": + description: Authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + "403": + description: Access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + "409": + description: Conflict in creating resource + 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: + ill: "1" +"/illbatches/{illbatch_id}": + get: + x-mojo-to: Illbatches#get + operationId: getIllbatches + tags: + - illbatches + summary: Get ILL batch + parameters: + - name: illbatch_id + in: path + description: ILL batch id/name/contents + required: true + type: string + produces: + - application/json + responses: + "200": + description: An ILL batch + schema: + $ref: "../swagger.yaml#/definitions/illbatch" + "401": + description: Authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + "403": + description: Access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + "404": + description: ILL batch not found + 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: + ill: "1" + put: + x-mojo-to: Illbatches#update + operationId: updateIllBatch + tags: + - illbatches + summary: Update batch + parameters: + - $ref: "../swagger.yaml#/parameters/illbatch_id_pp" + - name: body + in: body + description: A JSON object containing information on the batch + required: true + schema: + $ref: "../swagger.yaml#/definitions/illbatch" + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: An ILL batch + schema: + $ref: "../swagger.yaml#/definitions/illbatch" + "400": + description: Bad request + schema: + $ref: "../swagger.yaml#/definitions/error" + "401": + description: Authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + "403": + description: Access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + "404": + description: ILL batch not found + 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: + ill: "1" + delete: + x-mojo-to: Illbatches#delete + operationId: deleteBatch + tags: + - illbatches + summary: Delete ILL batch + parameters: + - $ref: "../swagger.yaml#/parameters/illbatch_id_pp" + produces: + - application/json + responses: + "204": + description: ILL batch deleted + schema: + type: string + "401": + description: Authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + "403": + description: Access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + "404": + description: ILL batch not found + 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: + ill: "1" diff --git a/api/v1/swagger/paths/illrequests.yaml b/api/v1/swagger/paths/illrequests.yaml index 1e3224bda0..92725f08b1 100644 --- a/api/v1/swagger/paths/illrequests.yaml +++ b/api/v1/swagger/paths/illrequests.yaml @@ -38,6 +38,11 @@ description: Internal biblio identifier required: false type: integer + - name: batch_id + in: query + description: Internal identifier of the batch the request belongs to + required: false + type: integer - name: borrowernumber in: query description: Internal patron identifier diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index 696a73be8b..873463a001 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -48,6 +48,10 @@ definitions: $ref: ./definitions/ill_backend.yaml ill_backends: $ref: ./definitions/ill_backends.yaml + illbatch: + $ref: ./definitions/illbatch.yaml + illbatches: + $ref: ./definitions/illbatches.yaml import_batch_profile: $ref: ./definitions/import_batch_profile.yaml import_batch_profiles: @@ -225,6 +229,10 @@ paths: $ref: ./paths/ill_backends.yaml#/~1ill_backends "/ill_backends/{ill_backend_id}": $ref: "./paths/ill_backends.yaml#/~1ill_backends~1{ill_backend_id}" + /illbatches: + $ref: ./paths/illbatches.yaml#/~1illbatches + "/illbatches/{illbatch_id}": + $ref: "./paths/illbatches.yaml#/~1illbatches~1{illbatch_id}" /illrequests: $ref: ./paths/illrequests.yaml#/~1illrequests "/import_batches/{import_batch_id}/records/{import_record_id}/matches/chosen": @@ -420,6 +428,12 @@ parameters: name: hold_id required: true type: integer + illbatch_id_pp: + description: Internal ILL batch identifier + in: path + name: illbatch_id + required: true + type: integer import_batch_profile_id_pp: description: Internal profile identifier in: path @@ -713,6 +727,9 @@ tags: - description: "Manage ILL module backends\n" name: illbackends x-displayName: ILL backends + - description: "Manage ILL module batches\n" + name: illbatches + x-displayName: ILL batches - description: "Manage ILL requests\n" name: illrequests x-displayName: ILL requests diff --git a/installer/data/mysql/atomicupdate/bug_30719_add_ill_batches.pl b/installer/data/mysql/atomicupdate/bug_30719_add_ill_batches.pl new file mode 100755 index 0000000000..0d8846c694 --- /dev/null +++ b/installer/data/mysql/atomicupdate/bug_30719_add_ill_batches.pl @@ -0,0 +1,38 @@ +use Modern::Perl; + +return { + bug_number => "30719", + description => "Add ILL batches", + up => sub { + my ($args) = @_; + my ($dbh, $out) = @$args{qw(dbh out)}; + $dbh->do(q{ + CREATE TABLE `illbatches` ( + `id` int(11) NOT NULL auto_increment, -- Batch ID + `name` varchar(100) NOT NULL, -- Unique name of batch + `backend` varchar(20) NOT NULL, -- Name of batch backend + `borrowernumber` int(11), -- Patron associated with batch + `branchcode` varchar(50), -- Branch associated with batch + PRIMARY KEY (`id`), + UNIQUE KEY `u_illbatches__name` (`name`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + }); + $dbh->do(q{ + ALTER TABLE `illrequests` + ADD COLUMN `batch_id` int(11) AFTER backend -- Optional ID of batch that this request belongs to + }); + $dbh->do(q{ + ALTER TABLE `illrequests` + ADD CONSTRAINT `illrequests_ibfk` FOREIGN KEY (`batch_id`) REFERENCES `illbatches` (`id`) ON DELETE SET NULL ON UPDATE CASCADE + }); + $dbh->do(q{ + ALTER TABLE `illbatches` + ADD CONSTRAINT `illbatches_bnfk` FOREIGN KEY (`borrowernumber`) REFERENCES `borrowers` (`borrowernumber`) ON DELETE SET NULL ON UPDATE CASCADE + }); + $dbh->do(q{ + ALTER TABLE `illbatches` + ADD CONSTRAINT `illbatches_bcfk` FOREIGN KEY (`branchcode`) REFERENCES `branches` (`branchcode`) ON DELETE SET NULL ON UPDATE CASCADE + }); + say $out "Bug 30719: Add ILL batches completed" + }, +}; diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index d831006cd5..ad05b24458 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -3227,6 +3227,22 @@ CREATE TABLE `illrequestattributes` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `illbatches` +-- +DROP TABLE IF EXISTS `illbatches`; +CREATE TABLE `illbatches` ( + `id` int(11) NOT NULL auto_increment, -- Batch ID + `name` varchar(100) NOT NULL, -- Unique name of batch + `backend` varchar(20) NOT NULL, -- Name of batch backend + `borrowernumber` int(11), -- Patron associated with batch + `branchcode` varchar(50), -- Branch associated with batch + PRIMARY KEY (`id`), + UNIQUE KEY `u_illbatches__name` (`name`), + CONSTRAINT `illbatches_bnfk` FOREIGN KEY (`borrowernumber`) REFERENCES `borrowers` (`borrowernumber`) ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT `illbatches_bcfk` FOREIGN KEY (`branchcode`) REFERENCES `branches` (`branchcode`) ON DELETE SET NULL ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + -- -- Table structure for table `illrequests` -- @@ -3254,13 +3270,15 @@ CREATE TABLE `illrequests` ( `notesstaff` mediumtext DEFAULT NULL COMMENT 'Staff notes attached to request', `orderid` varchar(50) DEFAULT NULL COMMENT 'Backend id attached to request', `backend` varchar(20) DEFAULT NULL COMMENT 'The backend used to create request', + `batch_id` int(11) COMMENT 'Optional ID of batch that this request belongs to', PRIMARY KEY (`illrequest_id`), KEY `illrequests_bnfk` (`borrowernumber`), KEY `illrequests_bcfk_2` (`branchcode`), KEY `illrequests_safk` (`status_alias`), CONSTRAINT `illrequests_bcfk_2` FOREIGN KEY (`branchcode`) REFERENCES `branches` (`branchcode`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `illrequests_bnfk` FOREIGN KEY (`borrowernumber`) REFERENCES `borrowers` (`borrowernumber`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `illrequests_safk` FOREIGN KEY (`status_alias`) REFERENCES `authorised_values` (`authorised_value`) ON DELETE SET NULL ON UPDATE CASCADE + CONSTRAINT `illrequests_safk` FOREIGN KEY (`status_alias`) REFERENCES `authorised_values` (`authorised_value`) ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT `illrequests_ibfk` FOREIGN KEY (`batch_id`) REFERENCES `illbatches` (`id`) ON DELETE SET NULL ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; diff --git a/t/db_dependent/Illbatches.t b/t/db_dependent/Illbatches.t new file mode 100755 index 0000000000..0d28a8f44b --- /dev/null +++ b/t/db_dependent/Illbatches.t @@ -0,0 +1,96 @@ +#s!/usr/bin/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 . + +use Modern::Perl; + +use File::Basename qw/basename/; +use Koha::Database; +use Koha::Illbatch; +use Koha::Illbatches; +use Koha::Illrequests; +use Koha::Patrons; +use t::lib::Mocks; +use t::lib::TestBuilder; +use Test::MockObject; +use Test::MockModule; + +use Test::More tests => 8; + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new; +use_ok('Koha::Illbatch'); +use_ok('Koha::Illbatches'); + +$schema->storage->txn_begin; + +Koha::Illrequests->search->delete; + +# Create a patron +my $patron = $builder->build({ source => 'Borrower' }); + +# Create a librarian +my $librarian = $builder->build({ + source => 'Borrower', + value => { + firstname => "Grogu" + } +}); + +# Create a branch +my $branch = $builder->build({ + source => 'Branch' +}); + +# Create a batch +my $illbatch = $builder->build({ + source => 'Illbatch', + value => { + name => "My test batch", + backend => "Mock", + borrowernumber => $librarian->{borrowernumber}, + branchcode => $branch->{branchcode} + } +}); +my $batch_obj = Koha::Illbatches->find($illbatch->{id}); +isa_ok( $batch_obj, 'Koha::Illbatch' ); + +# Create an ILL request in the batch +my $illrq = $builder->build({ + source => 'Illrequest', + value => { + borrowernumber => $patron->{borrowernumber}, + batch_id => $illbatch->{id} + } +}); +my $illrq_obj = Koha::Illrequests->find($illrq->{illrequest_id}); + +# Check requests_count +my $requests_count = $batch_obj->requests_count; +is( $requests_count, 1, 'requests_count returns correctly' ); + +# Check patron +my $batch_patron = $batch_obj->patron; +isa_ok( $batch_patron, 'Koha::Patron' ); +is( $batch_patron->firstname, "Grogu", "patron returns correctly" ); + +# Check branch +my $batch_branch = $batch_obj->branch; +isa_ok( $batch_branch, 'Koha::Library' ); +is( $batch_branch->branchcode, $branch->{branchcode}, "branch returns correctly" ); + +$illrq_obj->delete; +$schema->storage->txn_rollback; diff --git a/t/db_dependent/api/v1/illbatches.t b/t/db_dependent/api/v1/illbatches.t new file mode 100755 index 0000000000..4e7512fadd --- /dev/null +++ b/t/db_dependent/api/v1/illbatches.t @@ -0,0 +1,417 @@ +#!/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 . + +use Modern::Perl; + +use Test::More tests => 5; +use Test::Mojo; + +use t::lib::TestBuilder; +use t::lib::Mocks; + +use Koha::Illbatch; +use Koha::Illbatches; +use Koha::Illrequests; +use Koha::Database; + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new; + +my $t = Test::Mojo->new('Koha::REST::V1'); +t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); + +subtest 'list() tests' => sub { + + plan tests => 19; + + $schema->storage->txn_begin; + + Koha::Illbatches->search->delete; + + my $librarian = $builder->build_object( + { + class => 'Koha::Patrons', + value => { + flags => 2 ** 22 # 22 => ill + } + } + ); + + my $branch = $builder->build_object( + { + class => 'Koha::Libraries' + } + ); + + my $password = 'sheev_is_da_boss!'; + $librarian->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $librarian->userid; + + ## Authorized user tests + # No batches, so empty array should be returned + $t->get_ok("//$userid:$password@/api/v1/illbatches") + ->status_is(200) + ->json_is( [] ); + + my $batch = $builder->build_object({ + class => 'Koha::Illbatches', + value => { + name => "PapaPalpatine", + backend => "Mock", + borrowernumber => $librarian->borrowernumber, + branchcode => $branch->branchcode + } + }); + + my $illrq = $builder->build({ + source => 'Illrequest', + value => { + borrowernumber => $librarian->borrowernumber, + batch_id => $batch->id + } + }); + + # One batch created, should get returned + $t->get_ok("//$userid:$password@/api/v1/illbatches") + ->status_is(200) + ->json_has( '/0/id', 'Batch ID' ) + ->json_has( '/0/name', 'Batch name' ) + ->json_has( '/0/backend', 'Backend name' ) + ->json_has( '/0/borrowernumber', 'Borrowernumber' ) + ->json_has( '/0/branchcode', 'Branchcode' ) + ->json_has( '/0/patron', 'patron embedded' ) + ->json_has( '/0/branch', 'branch embedded' ) + ->json_has( '/0/requests_count', 'request count' ); + + # Try to create a second batch with the same name, this should fail + my $another_batch = $builder->build_object({ class => 'Koha::Illbatches', value => { + name => $batch->name + } }); + # Create a second batch with a different name + my $batch_with_another_name = $builder->build_object({ class => 'Koha::Illbatches' }); + + # Two batches created, they should both be returned + $t->get_ok("//$userid:$password@/api/v1/illbatches") + ->status_is(200) + ->json_has('/0', 'has first batch') + ->json_has('/1', 'has second batch'); + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { + cardnumber => 999, + flags => 0 + } + } + ); + + $patron->set_password( { password => $password, skip_validation => 1 } ); + my $unauth_userid = $patron->userid; + + # Unauthorized access + $t->get_ok("//$unauth_userid:$password@/api/v1/illbatches") + ->status_is(403); + + $schema->storage->txn_rollback; +}; + +subtest 'get() tests' => sub { + + plan tests => 15; + + $schema->storage->txn_begin; + + my $librarian = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 2**22 } # 22 => ill + } + ); + my $password = 'Rebelz4DaWin'; + $librarian->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $librarian->userid; + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 0 } + } + ); + + my $branch = $builder->build_object( + { + class => 'Koha::Libraries' + } + ); + + my $batch = $builder->build_object({ + class => 'Koha::Illbatches', + value => { + name => "LeiaOrgana", + backend => "Mock", + borrowernumber => $librarian->borrowernumber, + branchcode => $branch->branchcode + } + }); + + + $patron->set_password( { password => $password, skip_validation => 1 } ); + my $unauth_userid = $patron->userid; + + $t->get_ok( "//$userid:$password@/api/v1/illbatches/" . $batch->id ) + ->status_is(200) + ->json_has( '/id', 'Batch ID' ) + ->json_has( '/name', 'Batch name' ) + ->json_has( '/backend', 'Backend name' ) + ->json_has( '/borrowernumber', 'Borrowernumber' ) + ->json_has( '/branchcode', 'Branchcode' ) + ->json_has( '/patron', 'patron embedded' ) + ->json_has( '/branch', 'branch embedded' ) + ->json_has( '/requests_count', 'request count' ); + + $t->get_ok( "//$unauth_userid:$password@/api/v1/illbatches/" . $batch->id ) + ->status_is(403); + + my $batch_to_delete = $builder->build_object({ class => 'Koha::Illbatches' }); + my $non_existent_id = $batch_to_delete->id; + $batch_to_delete->delete; + + $t->get_ok( "//$userid:$password@/api/v1/illbatches/$non_existent_id" ) + ->status_is(404) + ->json_is( '/error' => 'ILL batch not found' ); + + $schema->storage->txn_rollback; +}; + +subtest 'add() tests' => sub { + + plan tests =>16; + + $schema->storage->txn_begin; + + my $librarian = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 2**22 } # 22 => ill + } + ); + my $password = 'v4d3rRox'; + $librarian->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $librarian->userid; + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 0 } + } + ); + + $patron->set_password( { password => $password, skip_validation => 1 } ); + my $unauth_userid = $patron->userid; + + my $branch = $builder->build_object( + { + class => 'Koha::Libraries' + } + ); + + my $batch_metadata = { + name => "Anakin's requests", + backend => "Mock", + borrowernumber => $librarian->borrowernumber, + branchcode => $branch->branchcode + }; + + # Unauthorized attempt to write + $t->post_ok("//$unauth_userid:$password@/api/v1/illbatches" => json => $batch_metadata) + ->status_is(403); + + # Authorized attempt to write invalid data + my $batch_with_invalid_field = { + %{$batch_metadata}, + doh => 1 + }; + + $t->post_ok( "//$userid:$password@/api/v1/illbatches" => json => $batch_with_invalid_field ) + ->status_is(400) + ->json_is( + "/errors" => [ + { + message => "Properties not allowed: doh.", + path => "/body" + } + ] + ); + + # Authorized attempt to write + my $batch_id = + $t->post_ok( "//$userid:$password@/api/v1/illbatches" => json => $batch_metadata ) + ->status_is( 201 ) + ->json_is( '/name' => $batch_metadata->{name} ) + ->json_is( '/backend' => $batch_metadata->{backend} ) + ->json_is( '/borrowernumber' => $batch_metadata->{borrowernumber} ) + ->json_is( '/branchcode' => $batch_metadata->{branchcode} ) + ->json_has( '/patron' ) + ->json_has( '/branch' ); + + # Authorized attempt to create with null id + $batch_metadata->{id} = undef; + $t->post_ok( "//$userid:$password@/api/v1/illbatches" => json => $batch_metadata ) + ->status_is(400) + ->json_has('/errors'); + + $schema->storage->txn_rollback; +}; + +subtest 'update() tests' => sub { + + plan tests => 15; + + $schema->storage->txn_begin; + + my $librarian = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 2**22 } # 22 => ill + } + ); + my $password = 'aw3s0m3y0d41z'; + $librarian->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $librarian->userid; + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 0 } + } + ); + + $patron->set_password( { password => $password, skip_validation => 1 } ); + my $unauth_userid = $patron->userid; + + my $branch = $builder->build_object( + { + class => 'Koha::Libraries' + } + ); + + my $batch_id = $builder->build_object({ class => 'Koha::Illbatches' } )->id; + + # Unauthorized attempt to update + $t->put_ok( "//$unauth_userid:$password@/api/v1/illbatches/$batch_id" => json => { name => 'These are not the droids you are looking for' } ) + ->status_is(403); + + # Attempt partial update on a PUT + my $batch_with_missing_field = { + backend => "Mock", + borrowernumber => $librarian->borrowernumber, + branchcode => $branch->branchcode + }; + + $t->put_ok( "//$userid:$password@/api/v1/illbatches/$batch_id" => json => $batch_with_missing_field ) + ->status_is(400) + ->json_is( "/errors" => + [ { message => "Missing property.", path => "/body/name" } ] + ); + + # Full object update on PUT + my $batch_with_updated_field = { + name => "Master Ploo Koon", + backend => "Mock", + borrowernumber => $librarian->borrowernumber, + branchcode => $branch->branchcode + }; + + $t->put_ok( "//$userid:$password@/api/v1/illbatches/$batch_id" => json => $batch_with_updated_field ) + ->status_is(200) + ->json_is( '/name' => 'Master Ploo Koon' ); + + # Authorized attempt to write invalid data + my $batch_with_invalid_field = { + doh => 1, + name => "Master Mace Windu", + backend => "Mock" + }; + + $t->put_ok( "//$userid:$password@/api/v1/illbatches/$batch_id" => json => $batch_with_invalid_field ) + ->status_is(400) + ->json_is( + "/errors" => [ + { + message => "Properties not allowed: doh.", + path => "/body" + } + ] + ); + + my $batch_to_delete = $builder->build_object({ class => 'Koha::Cities' }); + my $non_existent_id = $batch_to_delete->id; + $batch_to_delete->delete; + + $t->put_ok( "//$userid:$password@/api/v1/illbatches/$non_existent_id" => json => $batch_with_updated_field ) + ->status_is(404); + + # Wrong method (POST) + $batch_with_updated_field->{id} = 2; + + $t->post_ok( "//$userid:$password@/api/v1/illbatches/$batch_id" => json => $batch_with_updated_field ) + ->status_is(404); + + $schema->storage->txn_rollback; +}; + +subtest 'delete() tests' => sub { + + plan tests => 6; + + $schema->storage->txn_begin; + + my $librarian = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 2**22 } # 22 => ill + } + ); + my $password = 's1th43v3r!'; + $librarian->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $librarian->userid; + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 0 } + } + ); + + $patron->set_password( { password => $password, skip_validation => 1 } ); + my $unauth_userid = $patron->userid; + + my $batch_id = $builder->build_object({ class => 'Koha::Illbatches' })->id; + + # Unauthorized attempt to delete + $t->delete_ok( "//$unauth_userid:$password@/api/v1/illbatches/$batch_id" ) + ->status_is(403); + + $t->delete_ok("//$userid:$password@/api/v1/illbatches/$batch_id") + ->status_is(204); + + $t->delete_ok("//$userid:$password@/api/v1/illbatches/$batch_id") + ->status_is(404); + + $schema->storage->txn_rollback; +}; -- 2.25.1