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 327-332 paths: Link Here
327
    $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}"
327
    $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}"
328
  "/preservation/trains/{train_id}/items":
328
  "/preservation/trains/{train_id}/items":
329
    $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}~1items"
329
    $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}~1items"
330
  "/preservation/trains/{train_id}/items/batch":
331
    $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}~1items~1batch"
330
  "/preservation/trains/{train_id}/items/{train_item_id}":
332
  "/preservation/trains/{train_id}/items/{train_item_id}":
331
    $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}~1items~1{train_item_id}"
333
    $ref: "./paths/preservation_trains.yaml#/~1preservation~1trains~1{train_id}~1items~1{train_item_id}"
332
  /preservation/processings:
334
  /preservation/processings:
(-)a/installer/data/mysql/kohastructure.sql (+1 lines)
Lines 4846-4851 CREATE TABLE `preservation_trains_items` ( Link Here
4846
  `train_id` int(11) NOT NULL COMMENT 'link with preservation_train',
4846
  `train_id` int(11) NOT NULL COMMENT 'link with preservation_train',
4847
  `item_id` int(11) NOT NULL COMMENT 'link with items',
4847
  `item_id` int(11) NOT NULL COMMENT 'link with items',
4848
  `processing_id` int(11) NULL COMMENT 'specific processing for this item',
4848
  `processing_id` int(11) NULL COMMENT 'specific processing for this item',
4849
  `user_train_item_id` int(11) NOT NULL COMMENT 'train item id for this train, starts from 1',
4849
  `added_on` datetime DEFAULT NULL COMMENT 'added date',
4850
  `added_on` datetime DEFAULT NULL COMMENT 'added date',
4850
  PRIMARY KEY (`train_item_id`),
4851
  PRIMARY KEY (`train_item_id`),
4851
  UNIQUE KEY (`train_id`,`item_id`),
4852
  UNIQUE KEY (`train_id`,`item_id`),
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/TrainsShow.vue (-12 / +16 lines)
Lines 146-154 Link Here
146
                        v-for="(item, counter) in train.items"
146
                        v-for="(item, counter) in train.items"
147
                        v-bind:key="counter"
147
                        v-bind:key="counter"
148
                    >
148
                    >
149
                        <!-- FIXME Counter here may change, we should pass an order by clause when retrieving the items -->
150
                        <label
149
                        <label
151
                            >{{ counter + 1 }}
150
                            >{{ item.user_train_item_id }}
152
                            <span class="action_links">
151
                            <span class="action_links">
153
                                <router-link
152
                                <router-link
154
                                    :to="`/cgi-bin/koha/preservation/trains/${train.train_id}/items/edit/${item.train_item_id}`"
153
                                    :to="`/cgi-bin/koha/preservation/trains/${train.train_id}/items/edit/${item.train_item_id}`"
Lines 253-266 export default { Link Here
253
                            let item_row = {}
252
                            let item_row = {}
254
                            this.train.default_processing.attributes.forEach(
253
                            this.train.default_processing.attributes.forEach(
255
                                attribute => {
254
                                attribute => {
256
                                    if (item.attributes.length <= 0) return ""
255
                                    let v = ""
256
                                    if (item.attributes.length >= 0) {
257
                                        let a = item.attributes.find(
258
                                            a =>
259
                                                a.processing_attribute_id ==
260
                                                attribute.processing_attribute_id
261
                                        )
262
                                        if (a) {
263
                                            v = a.value
264
                                        }
265
                                    }
266
257
                                    item_row[
267
                                    item_row[
258
                                        attribute.processing_attribute_id
268
                                        attribute.processing_attribute_id
259
                                    ] = item.attributes.find(
269
                                    ] = v
260
                                        a =>
261
                                            a.processing_attribute_id ==
262
                                            attribute.processing_attribute_id
263
                                    ).value
264
                                }
270
                                }
265
                            )
271
                            )
266
                            item_row.item = item
272
                            item_row.item = item
Lines 268-278 export default { Link Here
268
                        })
274
                        })
269
                        this.item_table.columns = []
275
                        this.item_table.columns = []
270
                        this.item_table.columns.push({
276
                        this.item_table.columns.push({
271
                            name: "id",
277
                            name: "",
272
                            title: this.$__("ID"),
278
                            title: this.$__("ID"),
273
                            render: (data, type, row) => {
279
                            data: "item.user_train_item_id",
274
                                return 42
275
                            },
276
                        })
280
                        })
277
                        train.default_processing.attributes.forEach(a =>
281
                        train.default_processing.attributes.forEach(a =>
278
                            this.item_table.columns.push({
282
                            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