View | Details | Raw Unified | Return to bug 30708
Collapse All | Expand All

(-)a/Koha/Preservation/Train.pm (+5 lines)
Lines 71-80 sub add_item { Link Here
71
    Koha::Exceptions::Preservation::ItemNotFound->throw unless $item;
71
    Koha::Exceptions::Preservation::ItemNotFound->throw unless $item;
72
    Koha::Exceptions::Preservation::ItemNotInWaitingList->throw if $item->notforloan != $not_for_loan;
72
    Koha::Exceptions::Preservation::ItemNotInWaitingList->throw if $item->notforloan != $not_for_loan;
73
73
74
    # FIXME We need a LOCK here
75
    # Not important for now as we have add_items
76
    # Note that there are several other places in Koha with this max+1 problem
77
    my $max = $self->items->search->_resultset->get_column("user_train_item_id")->max || 0;
74
    my $train_item_rs = $self->_result->add_to_preservation_trains_items(
78
    my $train_item_rs = $self->_result->add_to_preservation_trains_items(
75
        {
79
        {
76
            item_id       => $item->itemnumber,
80
            item_id       => $item->itemnumber,
77
            processing_id => $train_item->{processing_id} || $self->default_processing_id,
81
            processing_id => $train_item->{processing_id} || $self->default_processing_id,
82
            user_train_item_id => $max + 1,
78
            added_on      => \'NOW()',
83
            added_on      => \'NOW()',
79
        }
84
        }
80
    );
85
    );
(-)a/Koha/REST/V1/Preservation/Trains.pm (+42 lines)
Lines 288-293 sub get_item { Link Here
288
    };
288
    };
289
}
289
}
290
290
291
=head3 add_items
292
293
Controller function that handles adding items in batch to a train
294
295
=cut
296
297
sub add_items {
298
    my $c = shift->openapi->valid_input or return;
299
300
    my $train_id = $c->validation->param('train_id');
301
    my $train = Koha::Preservation::Trains->find( $train_id );
302
303
    unless ($train) {
304
        return $c->render(
305
            status  => 404,
306
            openapi => { error => "Train not found" }
307
        );
308
    }
309
310
    my $body = $c->validation->every_param('body');
311
    return try {
312
        Koha::Database->new->schema->txn_do(
313
            sub {
314
                my $train_items = $train->add_items($body);
315
                return $c->render( status => 201, openapi => $train_items );
316
            }
317
        );
318
    }
319
    catch {
320
        if ( blessed $_ ) {
321
            if ( $_->isa('Koha::Exceptions::Preservation::MissingSettings') ) {
322
                return $c->render(
323
                    status  => 400,
324
                    openapi => { error => "MissingSettings", parameter => $_->parameter }
325
                );
326
            }
327
        }
328
329
        $c->unhandled_exception($_);
330
    };
331
}
332
291
=head3 add_item
333
=head3 add_item
292
334
293
Controller function that handles adding items in batch to a train
335
Controller function that handles adding items in batch to a train
(-)a/api/v1/swagger/paths/preservation_trains.yaml (+78 lines)
Lines 376-381 Link Here
376
    x-koha-authorization:
376
    x-koha-authorization:
377
      permissions:
377
      permissions:
378
        preservation: 1
378
        preservation: 1
379
"/preservation/trains/{train_id}/items/batch":
380
  post:
381
    x-mojo-to: Preservation::Trains#add_items
382
    operationId: addItemsToTrain
383
    tags:
384
      - preservation_train
385
    summary: Add items to train
386
    consumes:
387
      - application/json
388
    produces:
389
      - application/json
390
    parameters:
391
      - $ref: "../swagger.yaml#/parameters/preservation_train_id_pp"
392
      - description: A list of items
393
        in: body
394
        name: body
395
        required: true
396
        schema:
397
          type: array
398
          items:
399
            type: object
400
    responses:
401
      201:
402
        description: A successfully added list of items
403
        schema:
404
          type: array
405
          items:
406
            type: object
407
      400:
408
        description: Bad parameter
409
        schema:
410
          $ref: "../swagger.yaml#/definitions/error"
411
      401:
412
        description: Authentication required
413
        schema:
414
          $ref: "../swagger.yaml#/definitions/error"
415
      403:
416
        description: Access forbidden
417
        schema:
418
          $ref: "../swagger.yaml#/definitions/error"
419
      404:
420
        description: Ressource not found
421
        schema:
422
          $ref: "../swagger.yaml#/definitions/error"
423
      409:
424
        description: Conflict in creating resource
425
        schema:
426
          $ref: "../swagger.yaml#/definitions/error"
427
      413:
428
        description: Payload too large
429
        schema:
430
          $ref: "../swagger.yaml#/definitions/error"
431
      500:
432
        description: |-
433
          Internal server error. Possible `error_code` attribute values:
434
          * `internal_server_error`
435
        schema:
436
          $ref: "../swagger.yaml#/definitions/error"
437
      503:
438
        description: Under maintenance
439
        schema:
440
          $ref: "../swagger.yaml#/definitions/error"
441
    x-koha-authorization:
442
      permissions:
443
        preservation: 1
444
        description: |-
445
          Internal server error. Possible `error_code` attribute values:
446
          * `internal_server_error`
447
        schema:
448
          $ref: "../swagger.yaml#/definitions/error"
449
      503:
450
        description: Under maintenance
451
        schema:
452
          $ref: "../swagger.yaml#/definitions/error"
453
    x-koha-authorization:
454
      permissions:
455
        preservation: 1
456
379
"/preservation/trains/{train_id}/items/{train_item_id}":
457
"/preservation/trains/{train_id}/items/{train_item_id}":
380
  put:
458
  put:
381
    x-mojo-to: Preservation::Trains#update_item
459
    x-mojo-to: Preservation::Trains#update_item
(-)a/api/v1/swagger/swagger.yaml (+2 lines)
Lines 315-320 paths: Link Here
315
    $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}"
315
    $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}"
316
  "/preservation/trains/{train_id}/items":
316
  "/preservation/trains/{train_id}/items":
317
    $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}~1items"
317
    $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}~1items"
318
  "/preservation/trains/{train_id}/items/batch":
319
    $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}~1items~1batch"
318
  "/preservation/trains/{train_id}/items/{train_item_id}":
320
  "/preservation/trains/{train_id}/items/{train_item_id}":
319
    $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}~1items~1{train_item_id}"
321
    $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}~1items~1{train_item_id}"
320
  /preservation/processings:
322
  /preservation/processings:
(-)a/installer/data/mysql/kohastructure.sql (+1 lines)
Lines 4827-4832 CREATE TABLE `preservation_trains_items` ( Link Here
4827
  `train_id` int(11) NOT NULL COMMENT 'link with preservation_train',
4827
  `train_id` int(11) NOT NULL COMMENT 'link with preservation_train',
4828
  `item_id` int(11) NOT NULL COMMENT 'link with items',
4828
  `item_id` int(11) NOT NULL COMMENT 'link with items',
4829
  `processing_id` int(11) NULL COMMENT 'specific processing for this item',
4829
  `processing_id` int(11) NULL COMMENT 'specific processing for this item',
4830
  `user_train_item_id` int(11) NOT NULL COMMENT 'train item id for this train, starts from 1',
4830
  `added_on` datetime DEFAULT NULL COMMENT 'added date',
4831
  `added_on` datetime DEFAULT NULL COMMENT 'added date',
4831
  PRIMARY KEY (`train_item_id`),
4832
  PRIMARY KEY (`train_item_id`),
4832
  UNIQUE KEY (`train_id`,`item_id`),
4833
  UNIQUE KEY (`train_id`,`item_id`),
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/TrainsShow.vue (-10 / +15 lines)
Lines 253-266 export default { Link Here
253
                            let item_row = {}
253
                            let item_row = {}
254
                            this.train.default_processing.attributes.forEach(
254
                            this.train.default_processing.attributes.forEach(
255
                                attribute => {
255
                                attribute => {
256
                                    if (item.attributes.length <= 0) return ""
256
                                    let v = ""
257
                                    if (item.attributes.length >= 0) {
258
                                        let a = item.attributes.find(
259
                                            a =>
260
                                                a.processing_attribute_id ==
261
                                                attribute.processing_attribute_id
262
                                        )
263
                                        if (a) {
264
                                            v = a.value
265
                                        }
266
                                    }
267
257
                                    item_row[
268
                                    item_row[
258
                                        attribute.processing_attribute_id
269
                                        attribute.processing_attribute_id
259
                                    ] = item.attributes.find(
270
                                    ] = v
260
                                        a =>
261
                                            a.processing_attribute_id ==
262
                                            attribute.processing_attribute_id
263
                                    ).value
264
                                }
271
                                }
265
                            )
272
                            )
266
                            item_row.item = item
273
                            item_row.item = item
Lines 268-278 export default { Link Here
268
                        })
275
                        })
269
                        this.item_table.columns = []
276
                        this.item_table.columns = []
270
                        this.item_table.columns.push({
277
                        this.item_table.columns.push({
271
                            name: "id",
278
                            name: "",
272
                            title: this.$__("ID"),
279
                            title: this.$__("ID"),
273
                            render: (data, type, row) => {
280
                            data: "item.user_train_item_id",
274
                                return 42
275
                            },
276
                        })
281
                        })
277
                        train.default_processing.attributes.forEach(a =>
282
                        train.default_processing.attributes.forEach(a =>
278
                            this.item_table.columns.push({
283
                            this.item_table.columns.push({
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/WaitingList.vue (-23 / +29 lines)
Lines 192-221 export default { Link Here
192
            this.loading()
192
            this.loading()
193
            let item_ids = Object.values(this.last_items)
193
            let item_ids = Object.values(this.last_items)
194
            const client = APIClient.preservation
194
            const client = APIClient.preservation
195
            let promises = []
195
            client.train_items
196
            this.last_items.forEach(i =>
196
                .createAll(item_ids, this.train_id_selected_for_add)
197
                promises.push(
197
                .then(
198
                    client.train_items.create(
198
                    result => {
199
                        { item_id: i.item_id },
199
                        if (result.length) {
200
                        this.train_id_selected_for_add
200
                            this.setMessage(
201
                    )
201
                                this.$__(
202
                )
202
                                    "%s items have been added to train %s."
203
            )
203
                                ).format(
204
            Promise.all(promises)
204
                                    result.length,
205
                .then(() => {
205
                                    this.train_id_selected_for_add
206
                    this.setMessage(
206
                                ),
207
                        this.$__(
207
                                true
208
                            "The items have been added to train %s."
208
                            )
209
                        ).format(this.train_id_selected_for_add),
209
                        } else {
210
                        true
210
                            this.setMessage(
211
                    )
211
                                this.$__(
212
                                    "No items have been added to the train."
213
                                )
214
                            )
215
                        }
212
216
213
                    this.$refs.table.redraw(
217
                        this.$refs.table.redraw(
214
                        "/api/v1/preservation/waiting-list/items"
218
                            "/api/v1/preservation/waiting-list/items"
215
                    )
219
                        )
216
                    this.show_modal_add_to_train = false
220
                        this.show_modal_add_to_train = false
217
                    this.last_items = []
221
                        this.last_items = []
218
                })
222
                    },
223
                    error => {}
224
                )
219
                .then(() => this.loaded())
225
                .then(() => this.loaded())
220
        },
226
        },
221
        addItemsToWaitingList: function (e) {
227
        addItemsToWaitingList: function (e) {
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/preservation-api-client.js (+5 lines)
Lines 116-121 export class PreservationAPIClient extends HttpClient { Link Here
116
                    endpoint: "trains/" + train_id + "/items",
116
                    endpoint: "trains/" + train_id + "/items",
117
                    body: train_item,
117
                    body: train_item,
118
                }),
118
                }),
119
            createAll: (train_items, train_id) =>
120
                this.post({
121
                    endpoint: "trains/" + train_id + "/items/batch",
122
                    body: train_items,
123
                }),
119
            update: (train_item, train_id, id) =>
124
            update: (train_item, train_id, id) =>
120
                this.put({
125
                this.put({
121
                    endpoint: "trains/" + train_id + "/items/" + id,
126
                    endpoint: "trains/" + train_id + "/items/" + id,
(-)a/t/db_dependent/api/v1/preservation_Trains.t (-2 / +20 lines)
Lines 19-24 use Modern::Perl; Link Here
19
19
20
use Test::More tests => 6;
20
use Test::More tests => 6;
21
use Test::Mojo;
21
use Test::Mojo;
22
use Test::Warn;
22
23
23
use t::lib::TestBuilder;
24
use t::lib::TestBuilder;
24
use t::lib::Mocks;
25
use t::lib::Mocks;
Lines 372-378 subtest 'delete() tests' => sub { Link Here
372
373
373
subtest '*_item() tests' => sub {
374
subtest '*_item() tests' => sub {
374
375
375
    plan tests => 26;
376
    plan tests => 35;
376
377
377
    $schema->storage->txn_begin;
378
    $schema->storage->txn_begin;
378
379
Lines 485-490 subtest '*_item() tests' => sub { Link Here
485
          json => { item_id => $item_2->itemnumber } )->status_is(404)
486
          json => { item_id => $item_2->itemnumber } )->status_is(404)
486
      ->json_is( { error => 'Item not found' } );
487
      ->json_is( { error => 'Item not found' } );
487
488
489
    # batch add items
490
    # Nothing added (FIXME maybe not 201?)
491
    warning_is {
492
        $t->post_ok(
493
            "//$userid:$password@/api/v1/preservation/trains/$train_id/items/batch"
494
              => json => [ { item_id => $item_3->itemnumber } ] )
495
          ->status_is(201)->json_is( [] );
496
    }
497
    'Item not added to train: [Cannot add item to train, it is not in the waiting list]';
498
499
    $item_3->notforloan($not_for_loan_waiting_list_in)->store;
500
    $t->post_ok(
501
        "//$userid:$password@/api/v1/preservation/trains/$train_id/items/batch"
502
          => json => [ { item_id => $item_3->itemnumber } ] )->status_is(201)
503
      ->json_is( '/0/item_id'       => $item_3->itemnumber )
504
      ->json_is( '/0/processing_id' => $train->default_processing_id )
505
      ->json_has('/0/added_on');
506
488
    # Update item
507
    # Update item
489
    my $new_item_attributes = [
508
    my $new_item_attributes = [
490
        {
509
        {
491
- 

Return to bug 30708