1 package Koha::REST::V1::Patrons;
3 # This file is part of Koha.
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.
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.
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>.
20 use Mojo::Base 'Mojolicious::Controller';
26 use List::MoreUtils qw(any);
27 use Scalar::Util qw( blessed );
28 use Try::Tiny qw( catch try );
32 Koha::REST::V1::Patrons
40 Controller function that handles listing Koha::Patron objects
45 my $c = shift->openapi->valid_input or return;
50 my $restricted = delete $c->validation->output->{restricted};
51 $query->{debarred} = { '!=' => undef }
54 my $patrons_rs = Koha::Patrons->search($query);
55 my $patrons = $c->objects->search( $patrons_rs );
63 $c->unhandled_exception($_);
69 Controller function that handles retrieving a single Koha::Patron object
74 my $c = shift->openapi->valid_input or return;
77 my $patron_id = $c->validation->param('patron_id');
78 my $patron = $c->objects->find( Koha::Patrons->search_limited, $patron_id );
83 openapi => { error => "Patron not found." }
93 $c->unhandled_exception($_);
99 Controller function that handles adding a new Koha::Patron object
104 my $c = shift->openapi->valid_input or return;
108 Koha::Database->new->schema->txn_do(
111 my $body = $c->validation->param('body');
113 my $extended_attributes = delete $body->{extended_attributes} // [];
115 my $patron = Koha::Patron->new_from_api($body)->store;
116 $patron->extended_attributes(
118 map { { code => $_->{type}, attribute => $_->{value} } }
119 @$extended_attributes
123 $c->res->headers->location($c->req->url->to_string . '/' . $patron->borrowernumber);
126 openapi => $patron->to_api
133 my $to_api_mapping = Koha::Patron->new->to_api_mapping;
136 if ( $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
139 openapi => { error => $_->error, conflict => $_->duplicate_id }
142 elsif ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) {
147 . $to_api_mapping->{ $_->broken_fk }
152 elsif ( $_->isa('Koha::Exceptions::BadParameter') ) {
157 . $to_api_mapping->{ $_->parameter }
163 $_->isa('Koha::Exceptions::Patron::MissingMandatoryExtendedAttribute')
168 openapi => { error => "$_" }
172 $_->isa('Koha::Exceptions::Patron::Attribute::InvalidType')
177 openapi => { error => "$_" }
181 $_->isa('Koha::Exceptions::Patron::Attribute::NonRepeatable')
186 openapi => { error => "$_" }
190 $_->isa('Koha::Exceptions::Patron::Attribute::UniqueIDConstraint')
195 openapi => { error => "$_" }
200 $c->unhandled_exception($_);
207 Controller function that handles updating a Koha::Patron object
212 my $c = shift->openapi->valid_input or return;
214 my $patron_id = $c->validation->param('patron_id');
215 my $patron = Koha::Patrons->find( $patron_id );
220 openapi => { error => "Patron not found" }
225 my $body = $c->validation->param('body');
226 my $user = $c->stash('koha.user');
229 $patron->is_superlibrarian
230 and !$user->is_superlibrarian
231 and ( exists $body->{email}
232 or exists $body->{secondary_email}
233 or exists $body->{altaddress_email} )
236 foreach my $email_field ( qw(email secondary_email altaddress_email) ) {
237 my $exists_email = exists $body->{$email_field};
238 next unless $exists_email;
240 # exists, verify if we are asked to change it
241 my $put_email = $body->{$email_field};
242 # As of writing this patch, 'email' is the only unmapped field
243 # (i.e. it preserves its name, hence this fallback)
244 my $db_email_field = $patron->to_api_mapping->{$email_field} // 'email';
245 my $db_email = $patron->$db_email_field;
249 openapi => { error => "Not enough privileges to change a superlibrarian's email" }
251 unless ( !defined $put_email and !defined $db_email )
252 or ( defined $put_email
253 and defined $db_email
254 and $put_email eq $db_email );
258 $patron->set_from_api($c->validation->param('body'))->store;
259 $patron->discard_changes;
260 return $c->render( status => 200, openapi => $patron->to_api );
263 unless ( blessed $_ && $_->can('rethrow') ) {
267 error => "Something went wrong, check Koha logs for details."
271 if ( $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
274 openapi => { error => $_->error, conflict => $_->duplicate_id }
277 elsif ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) {
280 openapi => { error => "Given " .
281 $patron->to_api_mapping->{$_->broken_fk}
282 . " does not exist" }
285 elsif ( $_->isa('Koha::Exceptions::MissingParameter') ) {
289 error => "Missing mandatory parameter(s)",
290 parameters => $_->parameter
294 elsif ( $_->isa('Koha::Exceptions::BadParameter') ) {
298 error => "Invalid parameter(s)",
299 parameters => $_->parameter
303 elsif ( $_->isa('Koha::Exceptions::NoChanges') ) {
306 openapi => { error => "No changes have been made" }
310 $c->unhandled_exception($_);
317 Controller function that handles deleting a Koha::Patron object
322 my $c = shift->openapi->valid_input or return;
324 my $patron = Koha::Patrons->find( $c->validation->param('patron_id') );
329 openapi => { error => "Patron not found" }
335 my $safe_to_delete = $patron->safe_to_delete;
337 if ( !$safe_to_delete ) {
338 # Pick the first error, if any
339 my ( $error ) = grep { $_->type eq 'error' } @{ $safe_to_delete->messages };
341 Koha::Exceptions::Exception->throw('Koha::Patron->safe_to_delete returned false but carried no error message');
344 my $error_descriptions = {
345 has_checkouts => 'Pending checkouts prevent deletion',
346 has_debt => 'Pending debts prevent deletion',
347 has_guarantees => 'Patron is a guarantor and it prevents deletion',
348 is_anonymous_patron => 'Anonymous patron cannot be deleted',
351 if ( any { $error->message eq $_ } keys %{$error_descriptions} ) {
355 error => $error_descriptions->{ $error->message },
356 error_code => $error->message,
360 Koha::Exceptions::Exception->throw( 'Koha::Patron->safe_to_delete carried an unexpected message: ' . $error->message );
364 return $patron->_result->result_source->schema->txn_do(
366 $patron->move_to_deleted;
377 $c->unhandled_exception($_);
381 =head3 guarantors_can_see_charges
383 Method for setting whether guarantors can see the patron's charges.
387 sub guarantors_can_see_charges {
388 my $c = shift->openapi->valid_input or return;
391 if ( C4::Context->preference('AllowPatronToSetFinesVisibilityForGuarantor') ) {
392 my $patron = $c->stash( 'koha.user' );
393 my $privacy_setting = ($c->req->json->{allowed}) ? 1 : 0;
395 $patron->privacy_guarantor_fines( $privacy_setting )->store;
407 'The current configuration doesn\'t allow the requested action.'
413 $c->unhandled_exception($_);
417 =head3 guarantors_can_see_checkouts
419 Method for setting whether guarantors can see the patron's checkouts.
423 sub guarantors_can_see_checkouts {
424 my $c = shift->openapi->valid_input or return;
427 if ( C4::Context->preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') ) {
428 my $patron = $c->stash( 'koha.user' );
429 my $privacy_setting = ( $c->req->json->{allowed} ) ? 1 : 0;
431 $patron->privacy_guarantor_checkouts( $privacy_setting )->store;
443 'The current configuration doesn\'t allow the requested action.'
449 $c->unhandled_exception($_);