Bug 33573: Add public endpoint for cancelling holds
[koha.git] / Koha / REST / V1 / Items.pm
1 package Koha::REST::V1::Items;
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
22 use C4::Circulation qw( barcodedecode );
23
24 use Koha::Items;
25
26 use List::MoreUtils qw( any );
27 use Try::Tiny qw( catch try );
28
29 =head1 NAME
30
31 Koha::REST::V1::Items - Koha REST API for handling items (V1)
32
33 =head1 API
34
35 =head2 Methods
36
37 =cut
38
39 =head3 list
40
41 Controller function that handles listing Koha::Item objects
42
43 =cut
44
45 sub list {
46     my $c = shift->openapi->valid_input or return;
47
48     return try {
49         my $items_set = Koha::Items->new;
50         my $items     = $c->objects->search( $items_set );
51         return $c->render(
52             status  => 200,
53             openapi => $items
54         );
55     }
56     catch {
57         $c->unhandled_exception($_);
58     };
59 }
60
61
62 =head3 get
63
64 Controller function that handles retrieving a single Koha::Item
65
66 =cut
67
68 sub get {
69     my $c = shift->openapi->valid_input or return;
70
71     try {
72         my $items_rs = Koha::Items->new;
73         my $item = $c->objects->find($items_rs, $c->validation->param('item_id'));
74         unless ( $item ) {
75             return $c->render(
76                 status => 404,
77                 openapi => { error => 'Item not found'}
78             );
79         }
80         return $c->render( status => 200, openapi => $item );
81     }
82     catch {
83         $c->unhandled_exception($_);
84     };
85 }
86
87 =head3 pickup_locations
88
89 Method that returns the possible pickup_locations for a given item
90 used for building the dropdown selector
91
92 =cut
93
94 sub pickup_locations {
95     my $c = shift->openapi->valid_input or return;
96
97     my $item_id = $c->validation->param('item_id');
98     my $item = Koha::Items->find( $item_id );
99
100     unless ($item) {
101         return $c->render(
102             status  => 404,
103             openapi => { error => "Item not found" }
104         );
105     }
106
107     my $patron_id = delete $c->validation->output->{patron_id};
108     my $patron    = Koha::Patrons->find( $patron_id );
109
110     unless ($patron) {
111         return $c->render(
112             status  => 400,
113             openapi => { error => "Patron not found" }
114         );
115     }
116
117     return try {
118
119         my $pl_set = $item->pickup_locations( { patron => $patron } );
120
121         my @response = ();
122         if ( C4::Context->preference('AllowHoldPolicyOverride') ) {
123
124             my $libraries_rs = Koha::Libraries->search( { pickup_location => 1 } );
125             my $libraries    = $c->objects->search($libraries_rs);
126
127             @response = map {
128                 my $library = $_;
129                 $library->{needs_override} = (
130                     any { $_->branchcode eq $library->{library_id} }
131                     @{ $pl_set->as_list }
132                   )
133                   ? Mojo::JSON->false
134                   : Mojo::JSON->true;
135                 $library;
136             } @{$libraries};
137         }
138         else {
139
140             my $pickup_locations = $c->objects->search($pl_set);
141             @response = map { $_->{needs_override} = Mojo::JSON->false; $_; } @{$pickup_locations};
142         }
143
144         return $c->render(
145             status  => 200,
146             openapi => \@response
147         );
148     }
149     catch {
150         $c->unhandled_exception($_);
151     };
152 }
153
154 =head3 bundled_items
155
156 Controller function that handles bundled_items Koha::Item objects
157
158 =cut
159
160 sub bundled_items {
161     my $c = shift->openapi->valid_input or return;
162
163     my $item_id = $c->validation->param('item_id');
164     my $item = Koha::Items->find( $item_id );
165
166     unless ($item) {
167         return $c->render(
168             status  => 404,
169             openapi => { error => "Item not found" }
170         );
171     }
172
173     return try {
174         my $items_set = $item->bundle_items;
175         my $items     = $c->objects->search( $items_set );
176         return $c->render(
177             status  => 200,
178             openapi => $items
179         );
180     }
181     catch {
182         $c->unhandled_exception($_);
183     };
184 }
185
186 =head3 add_to_bundle
187
188 Controller function that handles adding items to this bundle
189
190 =cut
191
192 sub add_to_bundle {
193     my $c = shift->openapi->valid_input or return;
194
195     my $item_id = $c->validation->param('item_id');
196     my $item = Koha::Items->find( $item_id );
197
198     unless ($item) {
199         return $c->render(
200             status  => 404,
201             openapi => { error => "Item not found" }
202         );
203     }
204
205     my $bundle_item_id = $c->validation->param('body')->{'external_id'};
206     $bundle_item_id = barcodedecode($bundle_item_id);
207     my $bundle_item = Koha::Items->find( { barcode => $bundle_item_id } );
208
209     unless ($bundle_item) {
210         return $c->render(
211             status  => 404,
212             openapi => { error => "Bundle item not found" }
213         );
214     }
215
216     return try {
217         my $force_checkin = $c->validation->param('body')->{'force_checkin'};
218         my $link = $item->add_to_bundle($bundle_item, { force_checkin => $force_checkin });
219         return $c->render(
220             status  => 201,
221             openapi => $bundle_item
222         );
223     }
224     catch {
225         if ( ref($_) eq 'Koha::Exceptions::Object::DuplicateID' ) {
226             return $c->render(
227                 status  => 409,
228                 openapi => {
229                     error      => 'Item is already bundled',
230                     error_code => 'already_bundled',
231                     key        => $_->duplicate_id
232                 }
233             );
234         }
235         elsif ( ref($_) eq 'Koha::Exceptions::Item::Bundle::ItemIsCheckedOut' )
236         {
237             return $c->render(
238                 status  => 409,
239                 openapi => {
240                     error      => 'Item is checked out',
241                     error_code => 'checked_out'
242                 }
243             );
244         }
245         elsif ( ref($_) eq 'Koha::Exceptions::Checkin::FailedCheckin' ) {
246             return $c->render(
247                 status  => 409,
248                 openapi => {
249                     error      => 'Item cannot be checked in',
250                     error_code => 'failed_checkin'
251                 }
252             );
253         }
254         elsif ( ref($_) eq 'Koha::Exceptions::Item::Bundle::IsBundle' ) {
255             return $c->render(
256                 status  => 400,
257                 openapi => {
258                     error      => 'Bundles cannot be nested',
259                     error_code => 'failed_nesting'
260                 }
261             );
262         }
263         else {
264             $c->unhandled_exception($_);
265         }
266     };
267 }
268
269 =head3 remove_from_bundle
270
271 Controller function that handles removing items from this bundle
272
273 =cut
274
275 sub remove_from_bundle {
276     my $c = shift->openapi->valid_input or return;
277
278     my $item_id = $c->validation->param('item_id');
279     my $item = Koha::Items->find( $item_id );
280
281     unless ($item) {
282         return $c->render(
283             status  => 404,
284             openapi => { error => "Item not found" }
285         );
286     }
287
288     my $bundle_item_id = $c->validation->param('bundled_item_id');
289     $bundle_item_id = barcodedecode($bundle_item_id);
290     my $bundle_item = Koha::Items->find( { itemnumber => $bundle_item_id } );
291
292     unless ($bundle_item) {
293         return $c->render(
294             status  => 404,
295             openapi => { error => "Bundle item not found" }
296         );
297     }
298
299     $bundle_item->remove_from_bundle;
300     return $c->render(
301         status  => 204,
302         openapi => q{}
303     );
304 }
305
306 1;