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

(-)a/Koha/Item/Transfer.pm (+23 lines)
Lines 216-221 sub strings_map { Link Here
216
    };
216
    };
217
}
217
}
218
218
219
=head3 to_api_mapping
220
221
This method returns the mapping for representing a Koha::Item::Transfer object
222
on the API.
223
224
=cut
225
226
sub to_api_mapping {
227
    return {
228
        branchtransfer_id   => 'library_transfer_id',
229
        itemnumber          => 'item_id',
230
        frombranch          => 'from_library_id',
231
        tobranch            => 'to_library_id',
232
        daterequested       => 'date_requested',
233
        datesent            => 'date_sent',
234
        datecancelled       => 'date_cancelled',
235
        datearrived         => 'date_arrived',
236
        reason              => 'reason',
237
        cancellation_reason => 'cancellation_reason',
238
        comments            => 'comments',
239
    };
240
}
241
219
=head3 type
242
=head3 type
220
243
221
=cut
244
=cut
(-)a/Koha/REST/V1/Items/Transfers.pm (+185 lines)
Line 0 Link Here
1
package Koha::REST::V1::Items::Transfers;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Mojo::Base 'Mojolicious::Controller';
21
use Koha::Item::Transfers;
22
use Koha::Libraries;
23
use Koha::Items;
24
use Koha::Patrons;
25
use Koha::Recalls;
26
use C4::Context;
27
28
use Koha::Exceptions::Item::Transfer;
29
30
use Scalar::Util qw( blessed );
31
32
use Try::Tiny qw( catch try );
33
34
=head1 NAME
35
36
Koha::REST::V1::Items::Transfers - Koha REST API for handling item transfers (V1)
37
38
=head1 API
39
40
=head2 Methods
41
42
=cut
43
44
=head3 list
45
46
Controller function that handles listing Koha::Item::Transfers objects
47
48
=cut
49
50
sub list {
51
    my $c = shift->openapi->valid_input or return;
52
53
    return try {
54
        my $transfers = $c->objects->search( Koha::Item::Transfers->new );
55
        return $c->render( status => 200, openapi => $transfers );
56
    } catch {
57
        $c->unhandled_exception($_);
58
    };
59
}
60
61
=head3 get
62
63
Controller function that handles retrieving a single Koha::Item::Transfer
64
65
=cut
66
67
sub get {
68
    my $c = shift->openapi->valid_input or return;
69
70
    my $item_id = $c->param('item_id');
71
    my $item    = Koha::Items->find($item_id);
72
73
    return $c->render_resource_not_found("Item")
74
        unless $item;
75
    try {
76
        my $transfer = $item->transfer;
77
78
        return $c->render(
79
            status  => 404,
80
            openapi => { error => "Item is not in transfer" },
81
        ) unless $transfer;
82
83
        return $c->render(
84
            status  => 200,
85
            openapi => $c->objects->to_api($transfer),
86
        );
87
    } catch {
88
        $c->unhandled_exception($_);
89
    };
90
}
91
92
=head3 add
93
94
Controller function that handles adding a new transfer
95
96
=cut
97
98
sub add {
99
    my $c = shift->openapi->valid_input or return;
100
101
    my $item_id       = $c->param('item_id');
102
    my $item          = Koha::Items->find($item_id);
103
    my $ignore_limits = $c->param('ignore_limits');
104
    my $enqueue       = $c->param('enqueue');
105
    my $replace       = $c->param('replace');
106
107
    return $c->render_resource_not_found("Item")
108
        unless $item;
109
110
    my $body = $c->req->json;
111
112
    my $to_library = Koha::Libraries->find( $body->{to_library_id} );
113
114
    return $c->render_resource_not_found("Library")
115
        unless $to_library;
116
    return try {
117
        my $params = {
118
            to      => $to_library,
119
            reason  => $body->{reason},
120
            comment => $body->{comments},
121
        };
122
        $params->{ignore_limits} = $ignore_limits if defined $ignore_limits;
123
        $params->{enqueue}       = $enqueue       if defined $enqueue;
124
        $params->{replace}       = $replace       if defined $replace;
125
126
        my $transfer = $item->request_transfer($params);
127
        $transfer->transit;
128
        return $c->render(
129
            status  => 201,
130
            openapi => $c->objects->to_api($transfer),
131
        );
132
    } catch {
133
        if ( blessed $_ && $_->isa('Koha::Exceptions::Item::Transfer::InQueue') ) {
134
            return $c->render(
135
                status  => 409,
136
                openapi => { error => "Item is already in transfer queue" },
137
            );
138
        }
139
        $c->unhandled_exception($_);
140
    };
141
}
142
143
=head3 cancel
144
145
Controller function that handles deleting a transfer
146
147
=cut
148
149
sub cancel {
150
    my $c = shift->openapi->valid_input or return;
151
152
    my $item_id = $c->param('item_id');
153
    my $item    = Koha::Items->find($item_id);
154
155
    return $c->render_resource_not_found("Item")
156
        unless $item;
157
158
    my $reason = $c->param('reason') || "Manual";
159
160
    return try {
161
        my $transfer = $item->transfer;
162
        return $c->render(
163
            status  => 404,
164
            openapi => { error => "Item is not in transfer" },
165
        ) unless $transfer;
166
167
        $transfer->cancel(
168
            {
169
                reason => $reason,
170
                force  => 1,
171
            }
172
        );
173
        if ( C4::Context->preference('UseRecalls') ) {
174
            my $recall_transfer_deleted = Koha::Recalls->find( { item_id => $item_id, status => 'in_transit' } );
175
            if ( defined $recall_transfer_deleted ) {
176
                $recall_transfer_deleted->revert_transfer;
177
            }
178
        }
179
        return $c->render_resource_deleted;
180
    } catch {
181
        $c->unhandled_exception($_);
182
    };
183
}
184
185
1;
(-)a/api/v1/swagger/definitions/item_transfer.yaml (+70 lines)
Line 0 Link Here
1
---
2
type: object
3
properties:
4
  library_transfer_id:
5
    type: integer
6
    description: Internal transfer limit identifier
7
  item_id:
8
    type: integer
9
    description: Internal item identifier
10
  date_requested:
11
    type: 
12
      - string
13
      - 'null'
14
    format: date-time
15
    description: Date when the transfer was requested
16
  date_sent:
17
    type: 
18
      - string
19
      - 'null'
20
    format: date-time
21
    description: Date when the transfer was sent
22
  to_library_id:
23
    type: string
24
    description: Internal library id for which library the item is going to
25
  date_arrived:
26
    type: 
27
      - string
28
      - 'null'
29
    format: date-time
30
    description: Date when the transfer arrived at the destination library
31
  date_cancelled:
32
    type: 
33
      - string
34
      - 'null'
35
    format: date-time
36
    description: Date when the transfer was cancelled
37
  from_library_id:
38
    type: string
39
    description: Internal library id for which library the item is coming from
40
  comments:
41
    type: 
42
      - string
43
      - 'null'
44
    description: Comments associated with the transfer
45
  reason:
46
    type:
47
      - string
48
    description: Reason for the transfer
49
    enum:
50
      - 'Manual'
51
      - 'StockrotationAdvance'
52
      - 'StockrotationRepatriation'
53
      - 'ReturnToHome'
54
      - 'ReturnToHolding'
55
      - 'RotatingCollection'
56
      - 'Reserve'
57
      - 'LostReserve'
58
      - 'CancelReserve'
59
      - 'TransferCancellation'
60
      - 'Recall'
61
      - 'RecallCancellation'
62
  cancellation_reason:
63
    type: 
64
      - string
65
      - 'null'
66
    description: Reason for cancelling the transfer
67
additionalProperties: false
68
required:
69
  - to_library_id
70
  - reason
(-)a/api/v1/swagger/paths/items.yaml (+259 lines)
Lines 473-478 Link Here
473
    x-koha-authorization:
473
    x-koha-authorization:
474
      permissions:
474
      permissions:
475
        reserveforothers: place_holds
475
        reserveforothers: place_holds
476
/items/transfers:
477
  get:
478
    x-mojo-to: Items::Transfers#list
479
    operationId: listItemsTransfers
480
    description: This resource returns a list of existing transfers.
481
    summary: List transfers
482
    tags:
483
      - transfers
484
    parameters:
485
      - $ref: "../swagger.yaml#/parameters/match"
486
      - $ref: "../swagger.yaml#/parameters/order_by"
487
      - $ref: "../swagger.yaml#/parameters/page"
488
      - $ref: "../swagger.yaml#/parameters/per_page"
489
      - $ref: "../swagger.yaml#/parameters/q_param"
490
      - $ref: "../swagger.yaml#/parameters/q_body"
491
      - $ref: "../swagger.yaml#/parameters/request_id_header"
492
    produces:
493
      - application/json
494
    responses:
495
      "200":
496
        description: A list of transfers
497
        schema:
498
          type: array
499
          items:
500
            $ref: "../swagger.yaml#/definitions/item_transfer"
501
      "400":
502
        description: |
503
          Bad request. Possible `error_code` attribute values:
504
505
            * `invalid_query`
506
        schema:
507
          $ref: "../swagger.yaml#/definitions/error"
508
      "401":
509
        description: Authentication required
510
        schema:
511
          $ref: "../swagger.yaml#/definitions/error"
512
      "403":
513
        description: Access forbidden
514
        schema:
515
          $ref: "../swagger.yaml#/definitions/error"
516
      "404":
517
        description: Resource not found
518
        schema:
519
          $ref: "../swagger.yaml#/definitions/error"
520
      "500":
521
        description: |
522
          Internal server error. Possible `error_code` attribute values:
523
524
          * `internal_server_error`
525
        schema:
526
          $ref: "../swagger.yaml#/definitions/error"
527
      "503":
528
        description: Under maintenance
529
        schema:
530
          $ref: "../swagger.yaml#/definitions/error"
531
    x-koha-authorization:
532
      permissions:
533
        parameters: manage_transfers
534
/items/{item_id}/transfers:
535
  get: 
536
    x-mojo-to: Items::Transfers#get
537
    operationId: getItemTransfer
538
    description: This resource returns the active transfer for an item.
539
    summary: Get active item transfer
540
    tags:
541
      - transfers
542
    parameters:
543
      - $ref: "../swagger.yaml#/parameters/item_id_pp"
544
      - $ref: "../swagger.yaml#/parameters/match"
545
      - $ref: "../swagger.yaml#/parameters/order_by"
546
      - $ref: "../swagger.yaml#/parameters/page"
547
      - $ref: "../swagger.yaml#/parameters/per_page"
548
      - $ref: "../swagger.yaml#/parameters/q_param"
549
      - $ref: "../swagger.yaml#/parameters/q_body"
550
      - $ref: "../swagger.yaml#/parameters/request_id_header"
551
    produces:
552
      - application/json
553
    responses:
554
      "200":
555
        description: An active transfer for the item
556
        schema:
557
          type: object
558
          items:
559
            $ref: "../swagger.yaml#/definitions/item_transfer"
560
      "400":
561
        description: |
562
          Bad request. Possible `error_code` attribute values:
563
564
            * `invalid_query`
565
        schema:
566
          $ref: "../swagger.yaml#/definitions/error"
567
      "401":
568
        description: Authentication required
569
        schema:
570
          $ref: "../swagger.yaml#/definitions/error"
571
      "403":
572
        description: Access forbidden
573
        schema:
574
          $ref: "../swagger.yaml#/definitions/error"
575
      "404":
576
        description: Resource not found
577
        schema:
578
          $ref: "../swagger.yaml#/definitions/error"
579
      "500":
580
        description: |
581
          Internal server error. Possible `error_code` attribute values:
582
583
          * `internal_server_error`
584
        schema:
585
          $ref: "../swagger.yaml#/definitions/error"
586
      "503":
587
        description: Under maintenance
588
        schema:
589
          $ref: "../swagger.yaml#/definitions/error"
590
    x-koha-authorization:
591
      permissions:
592
        parameters: manage_transfers
593
  post:
594
    x-mojo-to: Items::Transfers#add
595
    operationId: addItemTransfer
596
    description: This resource creates a new transfer for the specified item.
597
    tags:
598
      - transfers
599
    summary: Add an item transfer
600
    parameters:
601
      - $ref: "../swagger.yaml#/parameters/item_id_pp"
602
      - name: ignore_limits
603
        in: query
604
        description: |
605
          If set to true, the transfer will be created even if it exceeds the transfer limits.
606
        required: false
607
        type: boolean
608
      - name: enqueue
609
        in: query
610
        description: |
611
          Used to queue up the transfer when the existing transfer is found to be in transit.
612
        required: false
613
        type: boolean
614
      - name: replace
615
        in: query
616
        description: |
617
          Used to replace the existing transfer request with your own. Reason for the cancellation of the existing transfer must be provided here.
618
        required: false
619
        type: string
620
        enum:
621
          - Manual
622
          - StockrotationAdvance
623
          - StockrotationRepatriation
624
          - ReturnToHome
625
          - ReturnToHolding
626
          - RotatingCollection
627
          - Reserve
628
          - LostReserve
629
          - CancelReserve
630
          - ItemLost
631
          - WrongTransfer
632
          - RecallCancellation    
633
      - name: body
634
        in: body
635
        description: A JSON object containing information about a new transfer
636
        required: true
637
        schema:
638
          $ref: "../swagger.yaml#/definitions/item_transfer"
639
    produces:
640
      - application/json
641
    responses:
642
      "201":
643
        description: Transfer added
644
        schema:
645
          $ref: "../swagger.yaml#/definitions/item_transfer"
646
      "400":
647
        description: Bad request
648
        schema:
649
          $ref: "../swagger.yaml#/definitions/error"
650
      "401":
651
        description: Authentication required
652
        schema:
653
          $ref: "../swagger.yaml#/definitions/error"
654
      "403":
655
        description: Access forbidden
656
        schema:
657
          $ref: "../swagger.yaml#/definitions/error"
658
      "404":
659
        description: Resource not found
660
        schema:
661
          $ref: "../swagger.yaml#/definitions/error"
662
      "409":
663
        description: Conflict in creating resource
664
        schema:
665
          $ref: "../swagger.yaml#/definitions/error"
666
      "500":
667
        description: |
668
          Internal server error. Possible `error_code` attribute values:
669
670
          * `internal_server_error`
671
        schema:
672
          $ref: "../swagger.yaml#/definitions/error"
673
      "503":
674
        description: Under maintenance
675
        schema:
676
          $ref: "../swagger.yaml#/definitions/error"
677
    x-koha-authorization:
678
      permissions:
679
        parameters: manage_transfers
680
  delete:
681
    x-mojo-to: Items::Transfers#cancel
682
    operationId: cancelItemTransfer
683
    description: This resource cancels the active transfer for the specified item.
684
    tags:
685
      - transfers
686
    summary: Cancel an item transfer
687
    parameters:
688
      - $ref: "../swagger.yaml#/parameters/item_id_pp"
689
      - name: reason
690
        in: query
691
        description: |
692
          Reason for cancelling the transfer. If not provided, the transfer will be cancelled with the default reason.
693
        required: false
694
        type: string
695
        default: "Manual"
696
    produces:
697
      - application/json
698
    responses:
699
      "204":
700
        description: Transfer cancelled
701
      "400":
702
        description: Bad request
703
        schema:
704
          $ref: "../swagger.yaml#/definitions/error"
705
      "401":
706
        description: Authentication required
707
        schema:
708
          $ref: "../swagger.yaml#/definitions/error"
709
      "403":
710
        description: Access forbidden
711
        schema:
712
          $ref: "../swagger.yaml#/definitions/error"
713
      "404":
714
        description: Resource not found
715
        schema:
716
          $ref: "../swagger.yaml#/definitions/error"
717
      "409":
718
        description: Conflict in creating resource
719
        schema:
720
          $ref: "../swagger.yaml#/definitions/error"
721
      "500":
722
        description: |
723
          Internal server error. Possible `error_code` attribute values:
724
725
          * `internal_server_error`
726
        schema:
727
          $ref: "../swagger.yaml#/definitions/error"
728
      "503":
729
        description: Under maintenance
730
        schema:
731
          $ref: "../swagger.yaml#/definitions/error"
732
    x-koha-authorization:
733
      permissions:
734
        parameters: manage_transfers
476
"/public/items":
735
"/public/items":
477
  get:
736
  get:
478
    x-mojo-to: Items#list_public
737
    x-mojo-to: Items#list_public
(-)a/api/v1/swagger/swagger.yaml (+6 lines)
Lines 126-131 definitions: Link Here
126
    $ref: ./definitions/invoice.yaml
126
    $ref: ./definitions/invoice.yaml
127
  item:
127
  item:
128
    $ref: ./definitions/item.yaml
128
    $ref: ./definitions/item.yaml
129
  item_transfer:
130
    $ref: ./definitions/item_transfer.yaml
129
  item_group:
131
  item_group:
130
    $ref: ./definitions/item_group.yaml
132
    $ref: ./definitions/item_group.yaml
131
  item_type:
133
  item_type:
Lines 445-450 paths: Link Here
445
    $ref: ./paths/items.yaml#/~1items~1{item_id}~1bundled_items~1{bundled_item_id}
447
    $ref: ./paths/items.yaml#/~1items~1{item_id}~1bundled_items~1{bundled_item_id}
446
  "/items/{item_id}/pickup_locations":
448
  "/items/{item_id}/pickup_locations":
447
    $ref: "./paths/items.yaml#/~1items~1{item_id}~1pickup_locations"
449
    $ref: "./paths/items.yaml#/~1items~1{item_id}~1pickup_locations"
450
  "/items/transfers":
451
    $ref: "./paths/items.yaml#/~1items~1transfers"
452
  "/items/{item_id}/transfers":
453
    $ref: "./paths/items.yaml#/~1items~1{item_id}~1transfers"
448
  /jobs:
454
  /jobs:
449
    $ref: ./paths/jobs.yaml#/~1jobs
455
    $ref: ./paths/jobs.yaml#/~1jobs
450
  "/jobs/{job_id}":
456
  "/jobs/{job_id}":
(-)a/t/db_dependent/api/v1/items/item_transfers.t (-1 / +288 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/env perl
2
3
use Modern::Perl;
4
5
use Test::NoWarnings;
6
use Test::More tests => 5;
7
use Test::MockModule;
8
use Test::Mojo;
9
10
use t::lib::TestBuilder;
11
use t::lib::Mocks;
12
13
use Koha::Database;
14
15
my $schema  = Koha::Database->new->schema;
16
my $builder = t::lib::TestBuilder->new;
17
18
t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
19
20
my $t = Test::Mojo->new('Koha::REST::V1');
21
22
subtest 'list()' => sub {
23
    plan tests => 9;
24
25
    $schema->storage->txn_begin;
26
27
    my $library1 = $builder->build_object( { class => 'Koha::Libraries' } );
28
    my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
29
    my $item     = $builder->build_sample_item(
30
        {
31
            homebranch    => $library1->branchcode,
32
            holdingbranch => $library1->branchcode,
33
        }
34
    );
35
36
    my $transfer = $item->request_transfer( { to => $library2, reason => 'Manual' } );
37
38
    my $patron = $builder->build_object(
39
        {
40
            class => 'Koha::Patrons',
41
            value => { flags => 1 }
42
        }
43
    );
44
45
    my $nonprivilegedpatron = $builder->build_object(
46
        {
47
            class => 'Koha::Patrons',
48
            value => { flags => 0 }
49
        }
50
    );
51
52
    my $password = 'thePassword123';
53
54
    $nonprivilegedpatron->set_password( { password => $password, skip_validation => 1 } );
55
    my $userid = $nonprivilegedpatron->userid;
56
57
    $t->get_ok("//$userid:$password@/api/v1/items/transfers")->status_is(403)
58
        ->json_is( '/error' => 'Authorization failure. Missing required permission(s).' );
59
60
    $patron->set_password( { password => $password, skip_validation => 1 } );
61
    $userid = $patron->userid;
62
63
    $t->get_ok("//$userid:$password@/api/v1/items/transfers")->status_is( 200, 'Transfer list retrieved successfully' );
64
65
    my $res  = $t->tx->res->json;
66
    my $json = pop @$res;
67
    is( $json->{item_id},         $item->itemnumber,     'Item number matches' );
68
    is( $json->{from_library_id}, $library1->branchcode, 'From library matches' );
69
    is( $json->{to_library_id},   $library2->branchcode, 'To library matches' );
70
    is( $json->{reason},          'Manual',              'Reason matches' );
71
72
    $schema->storage->txn_rollback;
73
};
74
75
subtest 'get()' => sub {
76
    plan tests => 9;
77
78
    $schema->storage->txn_begin;
79
80
    my $library1 = $builder->build_object( { class => 'Koha::Libraries' } );
81
    my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
82
    my $item     = $builder->build_sample_item(
83
        {
84
            homebranch    => $library1->branchcode,
85
            holdingbranch => $library1->branchcode,
86
        }
87
    );
88
89
    my $transfer = $item->request_transfer( { to => $library2, reason => 'Manual' } );
90
91
    my $patron = $builder->build_object(
92
        {
93
            class => 'Koha::Patrons',
94
            value => { flags => 1 }
95
        }
96
    );
97
98
    my $nonprivilegedpatron = $builder->build_object(
99
        {
100
            class => 'Koha::Patrons',
101
            value => { flags => 0 }
102
        }
103
    );
104
105
    my $password = 'thePassword123';
106
107
    $nonprivilegedpatron->set_password( { password => $password, skip_validation => 1 } );
108
    my $userid = $nonprivilegedpatron->userid;
109
110
    $t->get_ok("//$userid:$password@/api/v1/items/transfers")->status_is(403)
111
        ->json_is( '/error' => 'Authorization failure. Missing required permission(s).' );
112
113
    $patron->set_password( { password => $password, skip_validation => 1 } );
114
    $userid = $patron->userid;
115
    my $itemnumber = $item->itemnumber;
116
    $t->get_ok("//$userid:$password@/api/v1/items/$itemnumber/transfers")
117
        ->status_is( 200, 'Transfer retrieved successfully' );
118
119
    my $res = $t->tx->res->json;
120
    is( $res->{item_id},         $item->itemnumber,     'Item number matches' );
121
    is( $res->{from_library_id}, $library1->branchcode, 'From library matches' );
122
    is( $res->{to_library_id},   $library2->branchcode, 'To library matches' );
123
    is( $res->{reason},          'Manual',              'Reason matches' );
124
125
    $schema->storage->txn_rollback;
126
};
127
128
subtest 'add()' => sub {
129
    plan tests => 33;
130
131
    $schema->storage->txn_begin;
132
133
    my $library1 = $builder->build_object( { class => 'Koha::Libraries' } );
134
    my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
135
    my $library3 = $builder->build_object( { class => 'Koha::Libraries' } );
136
    my $item     = $builder->build_sample_item(
137
        {
138
            homebranch    => $library1->branchcode,
139
            holdingbranch => $library1->branchcode,
140
        }
141
    );
142
143
    my $patron = $builder->build_object(
144
        {
145
            class => 'Koha::Patrons',
146
            value => { flags => 1 }
147
        }
148
    );
149
150
    my $nonprivilegedpatron = $builder->build_object(
151
        {
152
            class => 'Koha::Patrons',
153
            value => { flags => 0 }
154
        }
155
    );
156
157
    my $password = 'thePassword123';
158
159
    $nonprivilegedpatron->set_password( { password => $password, skip_validation => 1 } );
160
    my $userid     = $nonprivilegedpatron->userid;
161
    my $itemnumber = $item->itemnumber;
162
163
    my $params = {
164
        to_library_id => $library2->branchcode,
165
        reason        => 'Manual',
166
    };
167
168
    $t->post_ok( "//$userid:$password@/api/v1/items/$itemnumber/transfers" => json => $params )->status_is(403)
169
        ->json_is( '/error' => 'Authorization failure. Missing required permission(s).' );
170
171
    $patron->set_password( { password => $password, skip_validation => 1 } );
172
    $userid = $patron->userid;
173
174
    $t->post_ok( "//$userid:$password@/api/v1/items/$itemnumber/transfers" => json => $params )
175
        ->status_is( 201, 'Transfer is created successfully' );
176
177
    my $res = $t->tx->res->json;
178
    is( $res->{item_id},         $item->itemnumber,     'Item number matches' );
179
    is( $res->{from_library_id}, $library1->branchcode, 'From library matches' );
180
    is( $res->{to_library_id},   $library2->branchcode, 'To library matches' );
181
    is( $res->{reason},          'Manual',              'Reason matches' );
182
183
    # Check transfer with limits
184
    my $limit = $builder->build_object( { class => 'Koha::Item::Transfer::Limits' } );
185
    $limit->set( { toBranch => $library3->branchcode, fromBranch => $library1->branchcode } )->store;
186
187
    $params->{to_library_id} = $library3->branchcode;
188
    $t->post_ok( "//$userid:$password@/api/v1/items/$itemnumber/transfers" => json => $params )
189
        ->status_is( 409, 'Transfer is not created due to transfer limit' );
190
191
    $t->post_ok(
192
        "//$userid:$password@/api/v1/items/$itemnumber/transfers?ignore_limits=1&enqueue=1" => json => $params )
193
        ->status_is( 201, 'Transfer is created successfully ignoring transfer limit' );
194
195
    # Duplicate transfer
196
    $params->{to_library_id} = $library1->branchcode;
197
    $params->{comments}      = 'Test comment';
198
199
    $t->post_ok( "//$userid:$password@/api/v1/items/$itemnumber/transfers" => json => $params )->status_is(409)
200
        ->json_is( '/error' => 'Item is already in transfer queue' );
201
202
    # Add to enqueue even if already in transfer queue
203
    $t->post_ok( "//$userid:$password@/api/v1/items/$itemnumber/transfers?enqueue=1" => json => $params )
204
        ->status_is( 201, 'Transfer is created successfully to enqueue up transfer' );
205
    $res = $t->tx->res->json;
206
207
    is( $res->{to_library_id}, $library1->branchcode, 'To library matches' );
208
    is( $res->{reason},        'Manual',              'Reason matches' );
209
    is( $res->{comments},      'Test comment',        'Comments matches' );
210
211
    my $library_transfer_id = $res->{library_transfer_id};
212
213
    # Add to enqueue with replace
214
    $params->{to_library_id} = $library2->branchcode;
215
    $params->{reason}        = 'Reserve';
216
    $t->post_ok( "//$userid:$password@/api/v1/items/$itemnumber/transfers?replace=WrongTransfer" => json => $params )
217
        ->status_is( 201, 'Transfer is created successfully to replace existing transfer' );
218
    $res = $t->tx->res->json;
219
    is( $res->{to_library_id}, $library2->branchcode, 'To library matches after replace' );
220
    is( $res->{reason},        'Reserve',             'Reason matches after replace' );
221
222
    # Invalid item number
223
    my $invalid_itemnumber = $itemnumber + 1;
224
    $t->post_ok( "//$userid:$password@/api/v1/items/$invalid_itemnumber/transfers" => json => $params )->status_is(404)
225
        ->json_is( '/error' => 'Item not found' );
226
227
    # Invalid library
228
    $params->{to_library_id} = 'InvalidLibrary';
229
    $t->post_ok( "//$userid:$password@/api/v1/items/$itemnumber/transfers" => json => $params )->status_is(404)
230
        ->json_is( '/error' => 'Library not found' );
231
232
    # Invalid reason
233
    $params->{reason} = 'InvalidReason';
234
    $t->post_ok( "//$userid:$password@/api/v1/items/$itemnumber/transfers" => json => $params )->status_is(400);
235
236
    $schema->storage->txn_rollback;
237
};
238
239
subtest 'delete()' => sub {
240
    plan tests => 7;
241
242
    $schema->storage->txn_begin;
243
244
    my $library1 = $builder->build_object( { class => 'Koha::Libraries' } );
245
    my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
246
    my $item     = $builder->build_sample_item(
247
        {
248
            homebranch    => $library1->branchcode,
249
            holdingbranch => $library1->branchcode,
250
        }
251
    );
252
253
    my $transfer = $item->request_transfer( { to => $library2, reason => 'Manual' } );
254
255
    my $patron = $builder->build_object(
256
        {
257
            class => 'Koha::Patrons',
258
            value => { flags => 1 }
259
        }
260
    );
261
262
    my $nonprivilegedpatron = $builder->build_object(
263
        {
264
            class => 'Koha::Patrons',
265
            value => { flags => 0 }
266
        }
267
    );
268
269
    my $password = 'thePassword123';
270
271
    $nonprivilegedpatron->set_password( { password => $password, skip_validation => 1 } );
272
    my $userid     = $nonprivilegedpatron->userid;
273
    my $itemnumber = $item->itemnumber;
274
    $t->delete_ok("//$userid:$password@/api/v1/items/$itemnumber/transfers")->status_is(403)
275
        ->json_is( '/error' => 'Authorization failure. Missing required permission(s).' );
276
277
    is( $item->transfer->branchtransfer_id, $transfer->branchtransfer_id, 'Item transfer exists' );
278
279
    $patron->set_password( { password => $password, skip_validation => 1 } );
280
    $userid = $patron->userid;
281
282
    $t->delete_ok("//$userid:$password@/api/v1/items/$itemnumber/transfers")
283
        ->status_is( 204, 'Transfer deleted successfully' );
284
285
    is( $item->transfer, undef, 'Item transfer is removed' );
286
287
    $schema->storage->txn_rollback;
288
};

Return to bug 35722