From a520028a3a62f0384c30deb13f7aed235b0c3f60 Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Thu, 6 Apr 2023 14:50:42 +0200 Subject: [PATCH] Bug 30708: Add user_train_item_id Sponsored-by: BULAC - http://www.bulac.fr/ Signed-off-by: BULAC - http://www.bulac.fr/ --- Koha/Preservation/Train.pm | 5 ++ Koha/REST/V1/Preservation/Trains.pm | 42 ++++++++++ api/v1/swagger/paths/preservation_trains.yaml | 78 +++++++++++++++++++ api/v1/swagger/swagger.yaml | 2 + installer/data/mysql/kohastructure.sql | 1 + .../components/Preservation/TrainsShow.vue | 25 +++--- .../components/Preservation/WaitingList.vue | 52 +++++++------ .../js/vue/fetch/preservation-api-client.js | 5 ++ t/db_dependent/api/v1/preservation_Trains.t | 21 ++++- 9 files changed, 197 insertions(+), 34 deletions(-) diff --git a/Koha/Preservation/Train.pm b/Koha/Preservation/Train.pm index f52cb5c15af..dadb1dd13d7 100644 --- a/Koha/Preservation/Train.pm +++ b/Koha/Preservation/Train.pm @@ -71,10 +71,15 @@ sub add_item { Koha::Exceptions::Preservation::ItemNotFound->throw unless $item; Koha::Exceptions::Preservation::ItemNotInWaitingList->throw if $item->notforloan != $not_for_loan; + # FIXME We need a LOCK here + # Not important for now as we have add_items + # Note that there are several other places in Koha with this max+1 problem + my $max = $self->items->search->_resultset->get_column("user_train_item_id")->max || 0; my $train_item_rs = $self->_result->add_to_preservation_trains_items( { item_id => $item->itemnumber, processing_id => $train_item->{processing_id} || $self->default_processing_id, + user_train_item_id => $max + 1, added_on => \'NOW()', } ); diff --git a/Koha/REST/V1/Preservation/Trains.pm b/Koha/REST/V1/Preservation/Trains.pm index 51dc2982460..644a7a97215 100644 --- a/Koha/REST/V1/Preservation/Trains.pm +++ b/Koha/REST/V1/Preservation/Trains.pm @@ -288,6 +288,48 @@ sub get_item { }; } +=head3 add_items + +Controller function that handles adding items in batch to a train + +=cut + +sub add_items { + my $c = shift->openapi->valid_input or return; + + my $train_id = $c->validation->param('train_id'); + my $train = Koha::Preservation::Trains->find( $train_id ); + + unless ($train) { + return $c->render( + status => 404, + openapi => { error => "Train not found" } + ); + } + + my $body = $c->validation->every_param('body'); + return try { + Koha::Database->new->schema->txn_do( + sub { + my $train_items = $train->add_items($body); + return $c->render( status => 201, openapi => $train_items ); + } + ); + } + catch { + if ( blessed $_ ) { + if ( $_->isa('Koha::Exceptions::Preservation::MissingSettings') ) { + return $c->render( + status => 400, + openapi => { error => "MissingSettings", parameter => $_->parameter } + ); + } + } + + $c->unhandled_exception($_); + }; +} + =head3 add_item Controller function that handles adding items in batch to a train diff --git a/api/v1/swagger/paths/preservation_trains.yaml b/api/v1/swagger/paths/preservation_trains.yaml index 6890adf8d36..e7fc5c90f83 100644 --- a/api/v1/swagger/paths/preservation_trains.yaml +++ b/api/v1/swagger/paths/preservation_trains.yaml @@ -376,6 +376,84 @@ x-koha-authorization: permissions: preservation: 1 +"/preservation/trains/{train_id}/items/batch": + post: + x-mojo-to: Preservation::Trains#add_items + operationId: addItemsToTrain + tags: + - preservation_train + summary: Add items to train + consumes: + - application/json + produces: + - application/json + parameters: + - $ref: "../swagger.yaml#/parameters/preservation_train_id_pp" + - description: A list of items + in: body + name: body + required: true + schema: + type: array + items: + type: object + responses: + 201: + description: A successfully added list of items + schema: + type: array + items: + type: object + 400: + description: Bad parameter + 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: Ressource not found + schema: + $ref: "../swagger.yaml#/definitions/error" + 409: + description: Conflict in creating resource + schema: + $ref: "../swagger.yaml#/definitions/error" + 413: + description: Payload too large + 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: + preservation: 1 + 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: + preservation: 1 + "/preservation/trains/{train_id}/items/{train_item_id}": put: x-mojo-to: Preservation::Trains#update_item diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index 7ff7aa82328..19075ea5fee 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -315,6 +315,8 @@ paths: $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}" "/preservation/trains/{train_id}/items": $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}~1items" + "/preservation/trains/{train_id}/items/batch": + $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}~1items~1batch" "/preservation/trains/{train_id}/items/{train_item_id}": $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}~1items~1{train_item_id}" /preservation/processings: diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index b8ef56f9444..d1e703bd0de 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -4827,6 +4827,7 @@ CREATE TABLE `preservation_trains_items` ( `train_id` int(11) NOT NULL COMMENT 'link with preservation_train', `item_id` int(11) NOT NULL COMMENT 'link with items', `processing_id` int(11) NULL COMMENT 'specific processing for this item', + `user_train_item_id` int(11) NOT NULL COMMENT 'train item id for this train, starts from 1', `added_on` datetime DEFAULT NULL COMMENT 'added date', PRIMARY KEY (`train_item_id`), UNIQUE KEY (`train_id`,`item_id`), diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/TrainsShow.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/TrainsShow.vue index 4038b43fc67..d453af96f54 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/TrainsShow.vue +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/TrainsShow.vue @@ -253,14 +253,21 @@ export default { let item_row = {} this.train.default_processing.attributes.forEach( attribute => { - if (item.attributes.length <= 0) return "" + let v = "" + if (item.attributes.length >= 0) { + let a = item.attributes.find( + a => + a.processing_attribute_id == + attribute.processing_attribute_id + ) + if (a) { + v = a.value + } + } + item_row[ attribute.processing_attribute_id - ] = item.attributes.find( - a => - a.processing_attribute_id == - attribute.processing_attribute_id - ).value + ] = v } ) item_row.item = item @@ -268,11 +275,9 @@ export default { }) this.item_table.columns = [] this.item_table.columns.push({ - name: "id", + name: "", title: this.$__("ID"), - render: (data, type, row) => { - return 42 - }, + data: "item.user_train_item_id", }) train.default_processing.attributes.forEach(a => this.item_table.columns.push({ diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/WaitingList.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/WaitingList.vue index ca87c139ba7..a1d627ecd52 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/WaitingList.vue +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/WaitingList.vue @@ -192,30 +192,36 @@ export default { this.loading() let item_ids = Object.values(this.last_items) const client = APIClient.preservation - let promises = [] - this.last_items.forEach(i => - promises.push( - client.train_items.create( - { item_id: i.item_id }, - this.train_id_selected_for_add - ) - ) - ) - Promise.all(promises) - .then(() => { - this.setMessage( - this.$__( - "The items have been added to train %s." - ).format(this.train_id_selected_for_add), - true - ) + client.train_items + .createAll(item_ids, this.train_id_selected_for_add) + .then( + result => { + if (result.length) { + this.setMessage( + this.$__( + "%s items have been added to train %s." + ).format( + result.length, + this.train_id_selected_for_add + ), + true + ) + } else { + this.setMessage( + this.$__( + "No items have been added to the train." + ) + ) + } - this.$refs.table.redraw( - "/api/v1/preservation/waiting-list/items" - ) - this.show_modal_add_to_train = false - this.last_items = [] - }) + this.$refs.table.redraw( + "/api/v1/preservation/waiting-list/items" + ) + this.show_modal_add_to_train = false + this.last_items = [] + }, + error => {} + ) .then(() => this.loaded()) }, addItemsToWaitingList: function (e) { diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/preservation-api-client.js b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/preservation-api-client.js index c9ea4bf9f45..455a80be5e7 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/preservation-api-client.js +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/preservation-api-client.js @@ -116,6 +116,11 @@ export class PreservationAPIClient extends HttpClient { endpoint: "trains/" + train_id + "/items", body: train_item, }), + createAll: (train_items, train_id) => + this.post({ + endpoint: "trains/" + train_id + "/items/batch", + body: train_items, + }), update: (train_item, train_id, id) => this.put({ endpoint: "trains/" + train_id + "/items/" + id, diff --git a/t/db_dependent/api/v1/preservation_Trains.t b/t/db_dependent/api/v1/preservation_Trains.t index 6f73bd64076..136c6d34339 100755 --- a/t/db_dependent/api/v1/preservation_Trains.t +++ b/t/db_dependent/api/v1/preservation_Trains.t @@ -19,6 +19,7 @@ use Modern::Perl; use Test::More tests => 6; use Test::Mojo; +use Test::Warn; use t::lib::TestBuilder; use t::lib::Mocks; @@ -372,7 +373,7 @@ subtest 'delete() tests' => sub { subtest '*_item() tests' => sub { - plan tests => 26; + plan tests => 35; $schema->storage->txn_begin; @@ -485,6 +486,24 @@ subtest '*_item() tests' => sub { json => { item_id => $item_2->itemnumber } )->status_is(404) ->json_is( { error => 'Item not found' } ); + # batch add items + # Nothing added (FIXME maybe not 201?) + warning_is { + $t->post_ok( + "//$userid:$password@/api/v1/preservation/trains/$train_id/items/batch" + => json => [ { item_id => $item_3->itemnumber } ] ) + ->status_is(201)->json_is( [] ); + } + 'Item not added to train: [Cannot add item to train, it is not in the waiting list]'; + + $item_3->notforloan($not_for_loan_waiting_list_in)->store; + $t->post_ok( + "//$userid:$password@/api/v1/preservation/trains/$train_id/items/batch" + => json => [ { item_id => $item_3->itemnumber } ] )->status_is(201) + ->json_is( '/0/item_id' => $item_3->itemnumber ) + ->json_is( '/0/processing_id' => $train->default_processing_id ) + ->json_has('/0/added_on'); + # Update item my $new_item_attributes = [ { -- 2.25.1