Bug 27854: Clean GET /patrons controller
[koha.git] / Koha / REST / V1 / Patrons.pm
1 package Koha::REST::V1::Patrons;
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 Koha::DateUtils;
23 use Koha::Patrons;
24
25 use Scalar::Util qw(blessed);
26 use Try::Tiny;
27
28 =head1 NAME
29
30 Koha::REST::V1::Patrons
31
32 =head1 API
33
34 =head2 Methods
35
36 =head3 list
37
38 Controller function that handles listing Koha::Patron objects
39
40 =cut
41
42 sub list {
43     my $c = shift->openapi->valid_input or return;
44
45     return try {
46
47         my $query = {};
48         my $restricted = delete $c->validation->output->{restricted};
49         $query->{debarred} = { '!=' => undef }
50             if $restricted;
51
52         my $patrons_rs = Koha::Patrons->search($query);
53         my $patrons    = $c->objects->search( $patrons_rs );
54
55         return $c->render(
56             status  => 200,
57             openapi => $patrons
58         );
59     }
60     catch {
61         $c->unhandled_exception($_);
62     };
63 }
64
65 =head3 get
66
67 Controller function that handles retrieving a single Koha::Patron object
68
69 =cut
70
71 sub get {
72     my $c = shift->openapi->valid_input or return;
73
74     return try {
75         my $patron_id = $c->validation->param('patron_id');
76         my $patron    = Koha::Patrons->find($patron_id);
77
78         unless ($patron) {
79             return $c->render( status => 404, openapi => { error => "Patron not found." } );
80         }
81
82         return $c->render( status => 200, openapi => $patron->to_api );
83     }
84     catch {
85         $c->unhandled_exception($_);
86     };
87 }
88
89 =head3 add
90
91 Controller function that handles adding a new Koha::Patron object
92
93 =cut
94
95 sub add {
96     my $c = shift->openapi->valid_input or return;
97
98     return try {
99
100         my $patron = Koha::Patron->new_from_api( $c->validation->param('body') )->store;
101
102         $c->res->headers->location( $c->req->url->to_string . '/' . $patron->borrowernumber );
103         return $c->render(
104             status  => 201,
105             openapi => $patron->to_api
106         );
107     }
108     catch {
109
110         my $to_api_mapping = Koha::Patron->new->to_api_mapping;
111
112         unless ( blessed $_ && $_->can('rethrow') ) {
113             return $c->render(
114                 status  => 500,
115                 openapi => { error => "Something went wrong, check Koha logs for details." }
116             );
117         }
118         if ( $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
119             return $c->render(
120                 status  => 409,
121                 openapi => { error => $_->error, conflict => $_->duplicate_id }
122             );
123         }
124         elsif ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) {
125             return $c->render(
126                 status  => 400,
127                 openapi => {
128                           error => "Given "
129                         . $to_api_mapping->{ $_->broken_fk }
130                         . " does not exist"
131                 }
132             );
133         }
134         elsif ( $_->isa('Koha::Exceptions::BadParameter') ) {
135             return $c->render(
136                 status  => 400,
137                 openapi => {
138                           error => "Given "
139                         . $to_api_mapping->{ $_->parameter }
140                         . " does not exist"
141                 }
142             );
143         }
144         else {
145             $c->unhandled_exception($_);
146         }
147     };
148 }
149
150
151 =head3 update
152
153 Controller function that handles updating a Koha::Patron object
154
155 =cut
156
157 sub update {
158     my $c = shift->openapi->valid_input or return;
159
160     my $patron_id = $c->validation->param('patron_id');
161     my $patron    = Koha::Patrons->find( $patron_id );
162
163     unless ($patron) {
164          return $c->render(
165              status  => 404,
166              openapi => { error => "Patron not found" }
167          );
168      }
169
170     return try {
171         my $body = $c->validation->param('body');
172         my $user = $c->stash('koha.user');
173
174         if (
175                 $patron->is_superlibrarian
176             and !$user->is_superlibrarian
177             and (  exists $body->{email}
178                 or exists $body->{secondary_email}
179                 or exists $body->{altaddress_email} )
180           )
181         {
182             foreach my $email_field ( qw(email secondary_email altaddress_email) ) {
183                 my $exists_email = exists $body->{$email_field};
184                 next unless $exists_email;
185
186                 # exists, verify if we are asked to change it
187                 my $put_email      = $body->{$email_field};
188                 # As of writing this patch, 'email' is the only unmapped field
189                 # (i.e. it preserves its name, hence this fallback)
190                 my $db_email_field = $patron->to_api_mapping->{$email_field} // 'email';
191                 my $db_email       = $patron->$db_email_field;
192
193                 return $c->render(
194                     status  => 403,
195                     openapi => { error => "Not enough privileges to change a superlibrarian's email" }
196                   )
197                   unless ( !defined $put_email and !defined $db_email )
198                   or (  defined $put_email
199                     and defined $db_email
200                     and $put_email eq $db_email );
201             }
202         }
203
204         $patron->set_from_api($c->validation->param('body'))->store;
205         $patron->discard_changes;
206         return $c->render( status => 200, openapi => $patron->to_api );
207     }
208     catch {
209         unless ( blessed $_ && $_->can('rethrow') ) {
210             return $c->render(
211                 status  => 500,
212                 openapi => {
213                     error => "Something went wrong, check Koha logs for details."
214                 }
215             );
216         }
217         if ( $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
218             return $c->render(
219                 status  => 409,
220                 openapi => { error => $_->error, conflict => $_->duplicate_id }
221             );
222         }
223         elsif ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) {
224             return $c->render(
225                 status  => 400,
226                 openapi => { error => "Given " .
227                             $patron->to_api_mapping->{$_->broken_fk}
228                             . " does not exist" }
229             );
230         }
231         elsif ( $_->isa('Koha::Exceptions::MissingParameter') ) {
232             return $c->render(
233                 status  => 400,
234                 openapi => {
235                     error      => "Missing mandatory parameter(s)",
236                     parameters => $_->parameter
237                 }
238             );
239         }
240         elsif ( $_->isa('Koha::Exceptions::BadParameter') ) {
241             return $c->render(
242                 status  => 400,
243                 openapi => {
244                     error      => "Invalid parameter(s)",
245                     parameters => $_->parameter
246                 }
247             );
248         }
249         elsif ( $_->isa('Koha::Exceptions::NoChanges') ) {
250             return $c->render(
251                 status  => 204,
252                 openapi => { error => "No changes have been made" }
253             );
254         }
255         else {
256             $c->unhandled_exception($_);
257         }
258     };
259 }
260
261 =head3 delete
262
263 Controller function that handles deleting a Koha::Patron object
264
265 =cut
266
267 sub delete {
268     my $c = shift->openapi->valid_input or return;
269
270     my $patron = Koha::Patrons->find( $c->validation->param('patron_id') );
271
272     unless ( $patron ) {
273         return $c->render(
274             status  => 404,
275             openapi => { error => "Patron not found" }
276         );
277     }
278
279     return try {
280
281         $patron->delete;
282         return $c->render(
283             status  => 204,
284             openapi => q{}
285         );
286     } catch {
287         if ( blessed $_ && $_->isa('Koha::Exceptions::Patron::FailedDeleteAnonymousPatron') ) {
288             return $c->render(
289                 status  => 403,
290                 openapi => { error => "Anonymous patron cannot be deleted" }
291             );
292         }
293
294         $c->unhandled_exception($_);
295     };
296 }
297
298 =head3 guarantors_can_see_charges
299
300 Method for setting whether guarantors can see the patron's charges.
301
302 =cut
303
304 sub guarantors_can_see_charges {
305     my $c = shift->openapi->valid_input or return;
306
307     return try {
308         if ( C4::Context->preference('AllowPatronToSetFinesVisibilityForGuarantor') ) {
309             my $patron = $c->stash( 'koha.user' );
310             my $privacy_setting = ($c->req->json->{allowed}) ? 1 : 0;
311
312             $patron->privacy_guarantor_fines( $privacy_setting )->store;
313
314             return $c->render(
315                 status  => 200,
316                 openapi => {}
317             );
318         }
319         else {
320             return $c->render(
321                 status  => 403,
322                 openapi => {
323                     error =>
324                       'The current configuration doesn\'t allow the requested action.'
325                 }
326             );
327         }
328     }
329     catch {
330         $c->unhandled_exception($_);
331     };
332 }
333
334 =head3 guarantors_can_see_checkouts
335
336 Method for setting whether guarantors can see the patron's checkouts.
337
338 =cut
339
340 sub guarantors_can_see_checkouts {
341     my $c = shift->openapi->valid_input or return;
342
343     return try {
344         if ( C4::Context->preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') ) {
345             my $patron = $c->stash( 'koha.user' );
346             my $privacy_setting = ( $c->req->json->{allowed} ) ? 1 : 0;
347
348             $patron->privacy_guarantor_checkouts( $privacy_setting )->store;
349
350             return $c->render(
351                 status  => 200,
352                 openapi => {}
353             );
354         }
355         else {
356             return $c->render(
357                 status  => 403,
358                 openapi => {
359                     error =>
360                       'The current configuration doesn\'t allow the requested action.'
361                 }
362             );
363         }
364     }
365     catch {
366         $c->unhandled_exception($_);
367     };
368 }
369
370 1;