Bug 23634: (QA follow-up) Catch all email cases in API
[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 $patrons_rs = Koha::Patrons->new;
48         my $args = $c->validation->output;
49         my $attributes = {};
50
51         # Extract reserved params
52         my ( $filtered_params, $reserved_params ) = $c->extract_reserved_params($args);
53
54         my $restricted = delete $filtered_params->{restricted};
55
56         # Merge sorting into query attributes
57         $c->dbic_merge_sorting(
58             {
59                 attributes => $attributes,
60                 params     => $reserved_params,
61                 result_set => $patrons_rs
62             }
63         );
64
65         # Merge pagination into query attributes
66         $c->dbic_merge_pagination(
67             {
68                 filter => $attributes,
69                 params => $reserved_params
70             }
71         );
72
73         if ( defined $filtered_params ) {
74
75             # Apply the mapping function to the passed params
76             $filtered_params = $patrons_rs->attributes_from_api($filtered_params);
77             $filtered_params = $c->build_query_params( $filtered_params, $reserved_params );
78         }
79
80         # translate 'restricted' => 'debarred'
81         $filtered_params->{debarred} = { '!=' => undef }
82           if $restricted;
83
84         my $patrons = $patrons_rs->search( $filtered_params, $attributes );
85         if ( $patrons_rs->is_paged ) {
86             $c->add_pagination_headers(
87                 {
88                     total  => $patrons->pager->total_entries,
89                     params => $args,
90                 }
91             );
92         }
93
94         return $c->render( status => 200, openapi => $patrons->to_api );
95     }
96     catch {
97         $c->unhandled_exception($_);
98     };
99 }
100
101
102 =head3 get
103
104 Controller function that handles retrieving a single Koha::Patron object
105
106 =cut
107
108 sub get {
109     my $c = shift->openapi->valid_input or return;
110
111     return try {
112         my $patron_id = $c->validation->param('patron_id');
113         my $patron    = Koha::Patrons->find($patron_id);
114
115         unless ($patron) {
116             return $c->render( status => 404, openapi => { error => "Patron not found." } );
117         }
118
119         return $c->render( status => 200, openapi => $patron->to_api );
120     }
121     catch {
122         $c->unhandled_exception($_);
123     };
124 }
125
126 =head3 add
127
128 Controller function that handles adding a new Koha::Patron object
129
130 =cut
131
132 sub add {
133     my $c = shift->openapi->valid_input or return;
134
135     return try {
136
137         my $patron = Koha::Patron->new_from_api( $c->validation->param('body') )->store;
138
139         $c->res->headers->location( $c->req->url->to_string . '/' . $patron->borrowernumber );
140         return $c->render(
141             status  => 201,
142             openapi => $patron->to_api
143         );
144     }
145     catch {
146
147         my $to_api_mapping = Koha::Patron->new->to_api_mapping;
148
149         unless ( blessed $_ && $_->can('rethrow') ) {
150             return $c->render(
151                 status  => 500,
152                 openapi => { error => "Something went wrong, check Koha logs for details." }
153             );
154         }
155         if ( $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
156             return $c->render(
157                 status  => 409,
158                 openapi => { error => $_->error, conflict => $_->duplicate_id }
159             );
160         }
161         elsif ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) {
162             return $c->render(
163                 status  => 400,
164                 openapi => {
165                           error => "Given "
166                         . $to_api_mapping->{ $_->broken_fk }
167                         . " does not exist"
168                 }
169             );
170         }
171         elsif ( $_->isa('Koha::Exceptions::BadParameter') ) {
172             return $c->render(
173                 status  => 400,
174                 openapi => {
175                           error => "Given "
176                         . $to_api_mapping->{ $_->parameter }
177                         . " does not exist"
178                 }
179             );
180         }
181         else {
182             $c->unhandled_exception($_);
183         }
184     };
185 }
186
187
188 =head3 update
189
190 Controller function that handles updating a Koha::Patron object
191
192 =cut
193
194 sub update {
195     my $c = shift->openapi->valid_input or return;
196
197     my $patron_id = $c->validation->param('patron_id');
198     my $patron    = Koha::Patrons->find( $patron_id );
199
200     unless ($patron) {
201          return $c->render(
202              status  => 404,
203              openapi => { error => "Patron not found" }
204          );
205      }
206
207     return try {
208         my $body = $c->validation->param('body');
209         my $user = $c->stash('koha.user');
210
211         if ( $patron->is_superlibrarian and !$user->is_superlibrarian ) {
212             my $put_email     = $body->{email} // qw{};
213             my $db_email      = $patron->email // qw{};
214             my $put_email_pro = $body->{secondary_email} // qw{};
215             my $db_email_pro  = $patron->emailpro // qw{};
216             my $put_email_B   = $body->{altaddress_email} // qw{};
217             my $db_email_B    = $patron->B_email // qw{};
218
219             return $c->render(
220                 status  => 403,
221                 openapi => {
222                     error =>
223                       "Not enough privileges to change a superlibrarian's email"
224                 }
225               )
226               if ($put_email ne $db_email)
227               || ($put_email_pro ne $db_email_pro)
228               || ($put_email_B ne $db_email_B);
229         }
230
231         $patron->set_from_api($c->validation->param('body'))->store;
232         $patron->discard_changes;
233         return $c->render( status => 200, openapi => $patron->to_api );
234     }
235     catch {
236         unless ( blessed $_ && $_->can('rethrow') ) {
237             return $c->render(
238                 status  => 500,
239                 openapi => {
240                     error => "Something went wrong, check Koha logs for details."
241                 }
242             );
243         }
244         if ( $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
245             return $c->render(
246                 status  => 409,
247                 openapi => { error => $_->error, conflict => $_->duplicate_id }
248             );
249         }
250         elsif ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) {
251             return $c->render(
252                 status  => 400,
253                 openapi => { error => "Given " .
254                             $patron->to_api_mapping->{$_->broken_fk}
255                             . " does not exist" }
256             );
257         }
258         elsif ( $_->isa('Koha::Exceptions::MissingParameter') ) {
259             return $c->render(
260                 status  => 400,
261                 openapi => {
262                     error      => "Missing mandatory parameter(s)",
263                     parameters => $_->parameter
264                 }
265             );
266         }
267         elsif ( $_->isa('Koha::Exceptions::BadParameter') ) {
268             return $c->render(
269                 status  => 400,
270                 openapi => {
271                     error      => "Invalid parameter(s)",
272                     parameters => $_->parameter
273                 }
274             );
275         }
276         elsif ( $_->isa('Koha::Exceptions::NoChanges') ) {
277             return $c->render(
278                 status  => 204,
279                 openapi => { error => "No changes have been made" }
280             );
281         }
282         else {
283             $c->unhandled_exception($_);
284         }
285     };
286 }
287
288 =head3 delete
289
290 Controller function that handles deleting a Koha::Patron object
291
292 =cut
293
294 sub delete {
295     my $c = shift->openapi->valid_input or return;
296
297     my $patron;
298
299     return try {
300         $patron = Koha::Patrons->find( $c->validation->param('patron_id') );
301
302         # check if loans, reservations, debarrment, etc. before deletion!
303         $patron->delete;
304         return $c->render(
305             status  => 204,
306             openapi => q{}
307         );
308     }
309     catch {
310         unless ($patron) {
311             return $c->render(
312                 status  => 404,
313                 openapi => { error => "Patron not found" }
314             );
315         }
316         else {
317             $c->unhandled_exception($_);
318         }
319     };
320 }
321
322 =head3 guarantors_can_see_charges
323
324 Method for setting whether guarantors can see the patron's charges.
325
326 =cut
327
328 sub guarantors_can_see_charges {
329     my $c = shift->openapi->valid_input or return;
330
331     return try {
332         if ( C4::Context->preference('AllowPatronToSetFinesVisibilityForGuarantor') ) {
333             my $patron = $c->stash( 'koha.user' );
334             my $privacy_setting = ($c->req->json->{allowed}) ? 1 : 0;
335
336             $patron->privacy_guarantor_fines( $privacy_setting )->store;
337
338             return $c->render(
339                 status  => 200,
340                 openapi => {}
341             );
342         }
343         else {
344             return $c->render(
345                 status  => 403,
346                 openapi => {
347                     error =>
348                       'The current configuration doesn\'t allow the requested action.'
349                 }
350             );
351         }
352     }
353     catch {
354         $c->unhandled_exception($_);
355     };
356 }
357
358 =head3 guarantors_can_see_checkouts
359
360 Method for setting whether guarantors can see the patron's checkouts.
361
362 =cut
363
364 sub guarantors_can_see_checkouts {
365     my $c = shift->openapi->valid_input or return;
366
367     return try {
368         if ( C4::Context->preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') ) {
369             my $patron = $c->stash( 'koha.user' );
370             my $privacy_setting = ( $c->req->json->{allowed} ) ? 1 : 0;
371
372             $patron->privacy_guarantor_checkouts( $privacy_setting )->store;
373
374             return $c->render(
375                 status  => 200,
376                 openapi => {}
377             );
378         }
379         else {
380             return $c->render(
381                 status  => 403,
382                 openapi => {
383                     error =>
384                       'The current configuration doesn\'t allow the requested action.'
385                 }
386             );
387         }
388     }
389     catch {
390         $c->unhandled_exception($_);
391     };
392 }
393
394 1;