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 under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation; either version 3 of the License, or (at your option) any later
10 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License along
15 # with Koha; if not, write to the Free Software Foundation, Inc.,
16 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 use Mojo
::Base
'Mojolicious::Controller';
25 use Scalar
::Util
qw(blessed);
30 Koha::REST::V1::Patrons
38 Controller function that handles listing Koha::Patron objects
43 my $c = shift->openapi->valid_input or return;
47 my $args = $c->validation->output;
48 my ( $params, $reserved_params ) = $c->extract_reserved_params( $args );
50 # Merge sorting into query attributes
51 $c->dbic_merge_sorting({ attributes
=> $attributes, params
=> $reserved_params });
53 # Merge pagination into query attributes
54 $c->dbic_merge_pagination({ filter
=> $attributes, params
=> $reserved_params });
56 my $restricted = $args->{restricted
};
58 $params = _to_model
($params)
60 # deal with string params
61 $params = $c->build_query_params( $params, $reserved_params );
63 # translate 'restricted' => 'debarred'
64 $params->{debarred
} = { '!=' => undef }
67 my $patrons = Koha
::Patrons
->search( $params, $attributes );
68 if ( $patrons->is_paged ) {
69 $c->add_pagination_headers(
71 total
=> $patrons->pager->total_entries,
76 my @patrons = $patrons->as_list;
77 @patrons = map { _to_api
( $_->TO_JSON ) } @patrons;
78 return $c->render( status
=> 200, openapi
=> \
@patrons );
81 if ( $_->isa('DBIx::Class::Exception') ) {
84 openapi
=> { error
=> $_->{msg
} }
90 openapi
=> { error
=> "Something went wrong, check the logs." }
99 Controller function that handles retrieving a single Koha::Patron object
104 my $c = shift->openapi->valid_input or return;
106 my $patron_id = $c->validation->param('patron_id');
107 my $patron = Koha
::Patrons
->find($patron_id);
110 return $c->render( status
=> 404, openapi
=> { error
=> "Patron not found." } );
113 return $c->render( status
=> 200, openapi
=> _to_api
( $patron->TO_JSON ) );
118 Controller function that handles adding a new Koha::Patron object
123 my $c = shift->openapi->valid_input or return;
127 my $body = _to_model
( $c->validation->param('body') );
129 my $patron = Koha
::Patron
->new( _to_model
($body) )->store;
130 $patron = _to_api
( $patron->TO_JSON );
132 return $c->render( status
=> 201, openapi
=> $patron );
135 unless ( blessed
$_ && $_->can('rethrow') ) {
138 openapi
=> { error
=> "Something went wrong, check Koha logs for details." }
141 if ( $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
144 openapi
=> { error
=> $_->error, conflict
=> $_->duplicate_id }
147 elsif ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) {
152 . $Koha::REST
::V1
::Patrons
::to_api_mapping
->{ $_->broken_fk }
157 elsif ( $_->isa('Koha::Exceptions::BadParameter') ) {
162 . $Koha::REST
::V1
::Patrons
::to_api_mapping
->{ $_->parameter }
170 openapi
=> { error
=> "Something went wrong, check Koha logs for details." }
179 Controller function that handles updating a Koha::Patron object
184 my $c = shift->openapi->valid_input or return;
186 my $patron_id = $c->validation->param('patron_id');
187 my $patron = Koha
::Patrons
->find( $patron_id );
192 openapi
=> { error
=> "Patron not found" }
197 my $body = _to_model
($c->validation->param('body'));
199 $patron->set($body)->store;
200 $patron->discard_changes;
201 return $c->render( status
=> 200, openapi
=> $patron );
204 unless ( blessed
$_ && $_->can('rethrow') ) {
208 error
=> "Something went wrong, check Koha logs for details."
212 if ( $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
215 openapi
=> { error
=> $_->error, conflict
=> $_->duplicate_id }
218 elsif ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) {
221 openapi
=> { error
=> "Given " .
222 $Koha::REST
::V1
::Patrons
::to_api_mapping
->{$_->broken_fk}
223 . " does not exist" }
226 elsif ( $_->isa('Koha::Exceptions::MissingParameter') ) {
230 error
=> "Missing mandatory parameter(s)",
231 parameters
=> $_->parameter
235 elsif ( $_->isa('Koha::Exceptions::BadParameter') ) {
239 error
=> "Invalid parameter(s)",
240 parameters
=> $_->parameter
244 elsif ( $_->isa('Koha::Exceptions::NoChanges') ) {
247 openapi
=> { error
=> "No changes have been made" }
255 "Something went wrong, check Koha logs for details."
264 Controller function that handles deleting a Koha::Patron object
269 my $c = shift->openapi->valid_input or return;
274 $patron = Koha
::Patrons
->find( $c->validation->param('patron_id') );
276 # check if loans, reservations, debarrment, etc. before deletion!
277 my $res = $patron->delete;
278 return $c->render( status
=> 200, openapi
=> {} );
284 openapi
=> { error
=> "Patron not found" }
292 "Something went wrong, check Koha logs for details."
301 Helper function that maps unblessed Koha::Patron objects into REST api
308 my $patron_id = $patron->{ borrowernumber
};
311 foreach my $column ( keys %{ $Koha::REST
::V1
::Patrons
::to_api_mapping
} ) {
312 my $mapped_column = $Koha::REST
::V1
::Patrons
::to_api_mapping
->{$column};
313 if ( exists $patron->{ $column }
314 && defined $mapped_column )
317 $patron->{ $mapped_column } = delete $patron->{ $column };
319 elsif ( exists $patron->{ $column }
320 && !defined $mapped_column )
323 delete $patron->{ $column };
327 # Calculate the 'restricted' field
328 my $patron_obj = Koha
::Patrons
->find( $patron_id );
329 $patron->{ restricted
} = ($patron_obj->is_debarred) ? Mojo
::JSON
->true : Mojo
::JSON
->false;
336 Helper function that maps REST api objects into Koha::Patron
344 foreach my $attribute ( keys %{ $Koha::REST
::V1
::Patrons
::to_model_mapping
} ) {
345 my $mapped_attribute = $Koha::REST
::V1
::Patrons
::to_model_mapping
->{$attribute};
346 if ( exists $patron->{ $attribute }
347 && defined $mapped_attribute )
350 $patron->{ $mapped_attribute } = delete $patron->{ $attribute };
352 elsif ( exists $patron->{ $attribute }
353 && !defined $mapped_attribute )
355 # key => undef / to be deleted
356 delete $patron->{ $attribute };
360 # TODO: Get rid of this once write operations are based on Koha::Patron
361 if ( exists $patron->{lost
} ) {
362 $patron->{lost
} = ($patron->{lost
}) ?
1 : 0;
365 if ( exists $patron->{ gonenoaddress
} ) {
366 $patron->{gonenoaddress
} = ($patron->{gonenoaddress
}) ?
1 : 0;
369 if ( exists $patron->{lastseen
} ) {
370 $patron->{lastseen
} = output_pref
({ str
=> $patron->{lastseen
}, dateformat
=> 'sql' });
373 if ( exists $patron->{updated_on
} ) {
374 $patron->{updated_on
} = output_pref
({ str
=> $patron->{updated_on
}, dateformat
=> 'sql' });
380 =head2 Global variables
382 =head3 $to_api_mapping
386 our $to_api_mapping = {
387 borrowernotes
=> 'staff_notes',
388 borrowernumber
=> 'patron_id',
389 branchcode
=> 'library_id',
390 categorycode
=> 'category_id',
391 checkprevcheckout
=> 'check_previous_checkout',
392 contactfirstname
=> undef, # Unused
393 contactname
=> undef, # Unused
394 contactnote
=> 'altaddress_notes',
395 contacttitle
=> undef, # Unused
396 dateenrolled
=> 'date_enrolled',
397 dateexpiry
=> 'expiry_date',
398 dateofbirth
=> 'date_of_birth',
399 debarred
=> undef, # replaced by 'restricted'
400 debarredcomment
=> undef, # calculated, API consumers will use /restrictions instead
401 emailpro
=> 'secondary_email',
402 flags
=> undef, # permissions manipulation handled in /permissions
403 gonenoaddress
=> 'incorrect_address',
404 guarantorid
=> 'guarantor_id',
405 lastseen
=> 'last_seen',
406 lost
=> 'patron_card_lost',
407 opacnote
=> 'opac_notes',
408 othernames
=> 'other_name',
409 password
=> undef, # password manipulation handled in /password
410 phonepro
=> 'secondary_phone',
411 relationship
=> 'relationship_type',
413 smsalertnumber
=> 'sms_number',
414 sort1
=> 'statistics_1',
415 sort2
=> 'statistics_2',
416 streetnumber
=> 'street_number',
417 streettype
=> 'street_type',
418 zipcode
=> 'postal_code',
419 B_address
=> 'altaddress_address',
420 B_address2
=> 'altaddress_address2',
421 B_city
=> 'altaddress_city',
422 B_country
=> 'altaddress_country',
423 B_email
=> 'altaddress_email',
424 B_phone
=> 'altaddress_phone',
425 B_state
=> 'altaddress_state',
426 B_streetnumber
=> 'altaddress_street_number',
427 B_streettype
=> 'altaddress_street_type',
428 B_zipcode
=> 'altaddress_postal_code',
429 altcontactaddress1
=> 'altcontact_address',
430 altcontactaddress2
=> 'altcontact_address2',
431 altcontactaddress3
=> 'altcontact_city',
432 altcontactcountry
=> 'altcontact_country',
433 altcontactfirstname
=> 'altcontact_firstname',
434 altcontactphone
=> 'altcontact_phone',
435 altcontactsurname
=> 'altcontact_surname',
436 altcontactstate
=> 'altcontact_state',
437 altcontactzipcode
=> 'altcontact_postal_code'
440 =head3 $to_model_mapping
444 our $to_model_mapping = {
445 altaddress_notes
=> 'contactnote',
446 category_id
=> 'categorycode',
447 check_previous_checkout
=> 'checkprevcheckout',
448 date_enrolled
=> 'dateenrolled',
449 date_of_birth
=> 'dateofbirth',
450 expiry_date
=> 'dateexpiry',
452 guarantor_id
=> 'guarantorid',
453 incorrect_address
=> 'gonenoaddress',
454 last_seen
=> 'lastseen',
455 library_id
=> 'branchcode',
456 opac_notes
=> 'opacnote',
457 other_name
=> 'othernames',
458 patron_card_lost
=> 'lost',
459 patron_id
=> 'borrowernumber',
460 postal_code
=> 'zipcode',
461 relationship_type
=> 'relationship',
463 secondary_email
=> 'emailpro',
464 secondary_phone
=> 'phonepro',
465 sms_number
=> 'smsalertnumber',
466 staff_notes
=> 'borrowernotes',
467 statistics_1
=> 'sort1',
468 statistics_2
=> 'sort2',
469 street_number
=> 'streetnumber',
470 street_type
=> 'streettype',
471 altaddress_address
=> 'B_address',
472 altaddress_address2
=> 'B_address2',
473 altaddress_city
=> 'B_city',
474 altaddress_country
=> 'B_country',
475 altaddress_email
=> 'B_email',
476 altaddress_phone
=> 'B_phone',
477 altaddress_state
=> 'B_state',
478 altaddress_street_number
=> 'B_streetnumber',
479 altaddress_street_type
=> 'B_streettype',
480 altaddress_postal_code
=> 'B_zipcode',
481 altcontact_firstname
=> 'altcontactfirstname',
482 altcontact_surname
=> 'altcontactsurname',
483 altcontact_address
=> 'altcontactaddress1',
484 altcontact_address2
=> 'altcontactaddress2',
485 altcontact_city
=> 'altcontactaddress3',
486 altcontact_state
=> 'altcontactstate',
487 altcontact_postal_code
=> 'altcontactzipcode',
488 altcontact_country
=> 'altcontactcountry',
489 altcontact_phone
=> 'altcontactphone'