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