1 package OpenILS::Application::Actor;
2 use base qw/OpenSRF::Application/;
3 use strict; use warnings;
5 $Data::Dumper::Indent = 0;
8 use Digest::MD5 qw(md5_hex);
10 use OpenSRF::EX qw(:try);
13 use OpenILS::Application::AppUtils;
15 use OpenILS::Utils::Fieldmapper;
16 use OpenILS::Utils::ModsParser;
17 use OpenSRF::Utils::Logger qw/$logger/;
18 use OpenSRF::Utils qw/:datetime/;
20 use OpenSRF::Utils::Cache;
24 use DateTime::Format::ISO8601;
25 use OpenILS::Const qw/:const/;
27 use OpenILS::Application::Actor::Container;
28 use OpenILS::Application::Actor::ClosedDates;
30 use OpenILS::Utils::CStoreEditor qw/:funcs/;
32 use OpenILS::Application::Actor::UserGroups;
34 OpenILS::Application::Actor::Container->initialize();
35 OpenILS::Application::Actor::UserGroups->initialize();
36 OpenILS::Application::Actor::ClosedDates->initialize();
39 my $apputils = "OpenILS::Application::AppUtils";
42 sub _d { warn "Patron:\n" . Dumper(shift()); }
47 my $set_user_settings;
50 __PACKAGE__->register_method(
51 method => "set_user_settings",
52 api_name => "open-ils.actor.patron.settings.update",
54 sub set_user_settings {
55 my( $self, $client, $user_session, $uid, $settings ) = @_;
57 $logger->debug("Setting user settings: $user_session, $uid, " . Dumper($settings));
59 my( $staff, $user, $evt ) =
60 $apputils->checkses_requestor( $user_session, $uid, 'UPDATE_USER' );
64 [{ usr => $user->id, name => $_}, {value => $$settings{$_}}] } keys %$settings;
66 $_->[1]->{value} = JSON->perl2JSON($_->[1]->{value}) for @params;
68 $logger->activity("User " . $staff->id . " updating user $uid settings with: " . Dumper(\@params));
70 my $ses = $U->start_db_session();
71 my $stat = $ses->request(
72 'open-ils.storage.direct.actor.user_setting.batch.merge', @params )->gather(1);
73 $U->commit_db_session($ses);
80 __PACKAGE__->register_method(
81 method => "set_ou_settings",
82 api_name => "open-ils.actor.org_unit.settings.update",
85 my( $self, $client, $user_session, $ouid, $settings ) = @_;
87 my( $staff, $evt ) = $apputils->checkses( $user_session );
89 $evt = $apputils->check_perms( $staff->id, $ouid, 'UPDATE_ORG_SETTING' );
93 for my $set (keys %$settings) {
95 my $json = JSON->perl2JSON($$settings{$set});
96 $logger->activity("updating org_unit.setting: $ouid : $set : $json");
99 { org_unit => $ouid, name => $set },
100 { value => $json } );
103 my $ses = $U->start_db_session();
104 my $stat = $ses->request(
105 'open-ils.storage.direct.actor.org_unit_setting.merge', @params )->gather(1);
106 $U->commit_db_session($ses);
112 my $fetch_user_settings;
113 my $fetch_ou_settings;
115 __PACKAGE__->register_method(
116 method => "user_settings",
117 api_name => "open-ils.actor.patron.settings.retrieve",
120 my( $self, $client, $user_session, $uid ) = @_;
122 my( $staff, $user, $evt ) =
123 $apputils->checkses_requestor( $user_session, $uid, 'VIEW_USER' );
126 $logger->debug("User " . $staff->id . " fetching user $uid\n");
127 my $s = $apputils->simplereq(
129 'open-ils.cstore.direct.actor.user_setting.search.atomic', { usr => $uid } );
131 return { map { ( $_->name => JSON->JSON2perl($_->value) ) } @$s };
136 __PACKAGE__->register_method(
137 method => "ou_settings",
138 api_name => "open-ils.actor.org_unit.settings.retrieve",
141 my( $self, $client, $ouid ) = @_;
143 $logger->info("Fetching org unit settings for org $ouid");
145 my $s = $apputils->simplereq(
147 'open-ils.cstore.direct.actor.org_unit_setting.search.atomic', {org_unit => $ouid});
149 return { map { ( $_->name => JSON->JSON2perl($_->value) ) } @$s };
152 __PACKAGE__->register_method (
153 method => "ou_setting_delete",
154 api_name => 'open-ils.actor.org_setting.delete',
156 Deletes a specific org unit setting for a specific location
157 @param authtoken The login session key
158 @param orgid The org unit whose setting we're changing
159 @param setting The name of the setting to delete
160 @return True value on success.
164 sub ou_setting_delete {
165 my( $self, $conn, $authtoken, $orgid, $setting ) = @_;
166 my( $reqr, $evt) = $U->checkses($authtoken);
168 $evt = $U->check_perms($reqr->id, $orgid, 'UPDATE_ORG_SETTING');
171 my $id = $U->cstorereq(
172 'open-ils.cstore.direct.actor.org_unit_setting.id_list',
173 { name => $setting, org_unit => $orgid } );
175 $logger->debug("Retrieved setting $id in org unit setting delete");
177 my $s = $U->cstorereq(
178 'open-ils.cstore.direct.actor.org_unit_setting.delete', $id );
180 $logger->activity("User ".$reqr->id." deleted org unit setting $id") if $s;
186 __PACKAGE__->register_method(
187 method => "update_patron",
188 api_name => "open-ils.actor.patron.update",);
191 my( $self, $client, $user_session, $patron ) = @_;
193 my $session = $apputils->start_db_session();
197 $logger->info("Creating new patron...") if $patron->isnew;
198 $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
200 my( $user_obj, $evt ) = $U->checkses($user_session);
203 $evt = check_group_perm($session, $user_obj, $patron);
207 # XXX does this user have permission to add/create users. Granularity?
208 # $new_patron is the patron in progress. $patron is the original patron
209 # passed in with the method. new_patron will change as the components
210 # of patron are added/updated.
214 # unflesh the real items on the patron
215 $patron->card( $patron->card->id ) if(ref($patron->card));
216 $patron->billing_address( $patron->billing_address->id )
217 if(ref($patron->billing_address));
218 $patron->mailing_address( $patron->mailing_address->id )
219 if(ref($patron->mailing_address));
221 # create/update the patron first so we can use his id
222 if($patron->isnew()) {
223 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
225 } else { $new_patron = $patron; }
227 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
230 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
233 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
236 # re-update the patron if anything has happened to him during this process
237 if($new_patron->ischanged()) {
238 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
242 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
245 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
248 ($new_patron, $evt) = _create_standing_penalties($session, $user_session, $patron, $new_patron, $user_obj);
251 $logger->activity("user ".$user_obj->id." updating/creating user ".$new_patron->id);
254 if(!$patron->isnew) {
255 $opatron = new_editor()->retrieve_actor_user($new_patron->id);
258 $apputils->commit_db_session($session);
259 my $fuser = flesh_user($new_patron->id());
262 # Log the new and old patron for investigation
263 $logger->info("$user_session updating patron object. orig patron object = ".
264 JSON->perl2JSON($opatron). " |||| new patron = ".JSON->perl2JSON($fuser));
274 return new_flesh_user($id, [
277 "standing_penalties",
281 "stat_cat_entries" ] );
289 # clone and clear stuff that would break the database
293 my $new_patron = $patron->clone;
295 $new_patron->clear_billing_address();
296 $new_patron->clear_mailing_address();
297 $new_patron->clear_addresses();
298 $new_patron->clear_card();
299 $new_patron->clear_cards();
300 $new_patron->clear_id();
301 $new_patron->clear_isnew();
302 $new_patron->clear_ischanged();
303 $new_patron->clear_isdeleted();
304 $new_patron->clear_stat_cat_entries();
305 $new_patron->clear_permissions();
306 $new_patron->clear_standing_penalties();
316 my $user_obj = shift;
318 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
319 return (undef, $evt) if $evt;
321 my $ex = $session->request(
322 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
324 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
327 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
329 my $id = $session->request(
330 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
331 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
333 $logger->info("Successfully created new user [$id] in DB");
335 return ( $session->request(
336 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
340 sub check_group_perm {
341 my( $session, $requestor, $patron ) = @_;
344 # first let's see if the requestor has
345 # priveleges to update this user in any way
346 if( ! $patron->isnew ) {
347 my $p = $session->request(
348 'open-ils.storage.direct.actor.user.retrieve', $patron->id )->gather(1);
350 # If we are the requestor (trying to update our own account)
351 # and we are not trying to change our profile, we're good
352 if( $p->id == $requestor->id and
353 $p->profile == $patron->profile ) {
358 $evt = group_perm_failed($session, $requestor, $p);
362 # They are allowed to edit this patron.. can they put the
363 # patron into the group requested?
364 $evt = group_perm_failed($session, $requestor, $patron);
370 sub group_perm_failed {
371 my( $session, $requestor, $patron ) = @_;
375 my $grpid = $patron->profile;
379 $logger->debug("user update looking for group perm for group $grpid");
380 $grp = $session->request(
381 'open-ils.storage.direct.permission.grp_tree.retrieve', $grpid )->gather(1);
382 return OpenILS::Event->new('PERMISSION_GRP_TREE_NOT_FOUND') unless $grp;
384 } while( !($perm = $grp->application_perm) and ($grpid = $grp->parent) );
386 $logger->info("user update checking perm $perm on user ".
387 $requestor->id." for update/create on user username=".$patron->usrname);
389 my $evt = $U->check_perms($requestor->id, $patron->home_ou, $perm);
397 my( $session, $patron, $user_obj, $noperm) = @_;
399 $logger->info("Updating patron ".$patron->id." in DB");
404 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
405 return (undef, $evt) if $evt;
408 # update the password by itself to avoid the password protection magic
409 if( $patron->passwd ) {
410 my $s = $session->request(
411 'open-ils.storage.direct.actor.user.remote_update',
412 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
413 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
414 $patron->clear_passwd;
417 if(!$patron->ident_type) {
418 $patron->clear_ident_type;
419 $patron->clear_ident_value;
422 my $stat = $session->request(
423 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
424 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
429 sub _check_dup_ident {
430 my( $session, $patron ) = @_;
432 return undef unless $patron->ident_value;
435 ident_type => $patron->ident_type,
436 ident_value => $patron->ident_value,
439 $logger->debug("patron update searching for dup ident values: " .
440 $patron->ident_type . ':' . $patron->ident_value);
442 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
444 my $dups = $session->request(
445 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
448 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
455 sub _add_update_addresses {
459 my $new_patron = shift;
463 my $current_id; # id of the address before creation
465 for my $address (@{$patron->addresses()}) {
467 next unless ref $address;
468 $current_id = $address->id();
470 if( $patron->billing_address() and
471 $patron->billing_address() == $current_id ) {
472 $logger->info("setting billing addr to $current_id");
473 $new_patron->billing_address($address->id());
474 $new_patron->ischanged(1);
477 if( $patron->mailing_address() and
478 $patron->mailing_address() == $current_id ) {
479 $new_patron->mailing_address($address->id());
480 $logger->info("setting mailing addr to $current_id");
481 $new_patron->ischanged(1);
485 if($address->isnew()) {
487 $address->usr($new_patron->id());
489 ($address, $evt) = _add_address($session,$address);
490 return (undef, $evt) if $evt;
492 # we need to get the new id
493 if( $patron->billing_address() and
494 $patron->billing_address() == $current_id ) {
495 $new_patron->billing_address($address->id());
496 $logger->info("setting billing addr to $current_id");
497 $new_patron->ischanged(1);
500 if( $patron->mailing_address() and
501 $patron->mailing_address() == $current_id ) {
502 $new_patron->mailing_address($address->id());
503 $logger->info("setting mailing addr to $current_id");
504 $new_patron->ischanged(1);
507 } elsif($address->ischanged() ) {
509 ($address, $evt) = _update_address($session, $address);
510 return (undef, $evt) if $evt;
512 } elsif($address->isdeleted() ) {
514 if( $address->id() == $new_patron->mailing_address() ) {
515 $new_patron->clear_mailing_address();
516 ($new_patron, $evt) = _update_patron($session, $new_patron);
517 return (undef, $evt) if $evt;
520 if( $address->id() == $new_patron->billing_address() ) {
521 $new_patron->clear_billing_address();
522 ($new_patron, $evt) = _update_patron($session, $new_patron);
523 return (undef, $evt) if $evt;
526 $evt = _delete_address($session, $address);
527 return (undef, $evt) if $evt;
531 return ( $new_patron, undef );
535 # adds an address to the db and returns the address with new id
537 my($session, $address) = @_;
538 $address->clear_id();
540 $logger->info("Creating new address at street ".$address->street1);
542 # put the address into the database
543 my $id = $session->request(
544 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
545 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
548 return ($address, undef);
552 sub _update_address {
553 my( $session, $address ) = @_;
555 $logger->info("Updating address ".$address->id." in the DB");
557 my $stat = $session->request(
558 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
560 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
561 return ($address, undef);
566 sub _add_update_cards {
570 my $new_patron = shift;
574 my $virtual_id; #id of the card before creation
575 for my $card (@{$patron->cards()}) {
577 $card->usr($new_patron->id());
579 if(ref($card) and $card->isnew()) {
581 $virtual_id = $card->id();
582 ( $card, $evt ) = _add_card($session,$card);
583 return (undef, $evt) if $evt;
585 #if(ref($patron->card)) { $patron->card($patron->card->id); }
586 if($patron->card() == $virtual_id) {
587 $new_patron->card($card->id());
588 $new_patron->ischanged(1);
591 } elsif( ref($card) and $card->ischanged() ) {
592 $evt = _update_card($session, $card);
593 return (undef, $evt) if $evt;
597 return ( $new_patron, undef );
601 # adds an card to the db and returns the card with new id
603 my( $session, $card ) = @_;
606 $logger->info("Adding new patron card ".$card->barcode);
608 my $id = $session->request(
609 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
610 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
611 $logger->info("Successfully created patron card $id");
614 return ( $card, undef );
618 # returns event on error. returns undef otherwise
620 my( $session, $card ) = @_;
621 $logger->info("Updating patron card ".$card->id);
623 my $stat = $session->request(
624 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
625 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
632 # returns event on error. returns undef otherwise
633 sub _delete_address {
634 my( $session, $address ) = @_;
636 $logger->info("Deleting address ".$address->id." from DB");
638 my $stat = $session->request(
639 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
641 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
647 sub _add_survey_responses {
648 my ($session, $patron, $new_patron) = @_;
650 $logger->info( "Updating survey responses for patron ".$new_patron->id );
652 my $responses = $patron->survey_responses;
656 $_->usr($new_patron->id) for (@$responses);
658 my $evt = $U->simplereq( "open-ils.circ",
659 "open-ils.circ.survey.submit.user_id", $responses );
661 return (undef, $evt) if defined($U->event_code($evt));
665 return ( $new_patron, undef );
669 sub _create_stat_maps {
671 my($session, $user_session, $patron, $new_patron) = @_;
673 my $maps = $patron->stat_cat_entries();
675 for my $map (@$maps) {
677 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
679 if ($map->isdeleted()) {
680 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
682 } elsif ($map->isnew()) {
683 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
688 $map->target_usr($new_patron->id);
691 $logger->info("Updating stat entry with method $method and map $map");
693 my $stat = $session->request($method, $map)->gather(1);
694 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
698 return ($new_patron, undef);
701 sub _create_perm_maps {
703 my($session, $user_session, $patron, $new_patron) = @_;
705 my $maps = $patron->permissions;
707 for my $map (@$maps) {
709 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
710 if ($map->isdeleted()) {
711 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
712 } elsif ($map->isnew()) {
713 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
718 $map->usr($new_patron->id);
720 #warn( "Updating permissions with method $method and session $user_session and map $map" );
721 $logger->info( "Updating permissions with method $method and map $map" );
723 my $stat = $session->request($method, $map)->gather(1);
724 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
728 return ($new_patron, undef);
732 __PACKAGE__->register_method(
733 method => "set_user_perms",
734 api_name => "open-ils.actor.user.permissions.update",
743 my $session = $apputils->start_db_session();
745 my( $user_obj, $evt ) = $U->checkses($ses);
748 my $perms = $session->request('open-ils.storage.permission.user_perms.atomic', $user_obj->id)->gather(1);
751 $all = 1 if ($user_obj->super_user());
752 $all = 1 unless ($U->check_perms($user_obj->id, $user_obj->home_ou, 'EVERYTHING'));
754 for my $map (@$maps) {
756 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
757 if ($map->isdeleted()) {
758 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
759 } elsif ($map->isnew()) {
760 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
764 next if (!$all || !grep { $_->perm eq $map->perm and $_->grantable == 1 and $_->depth <= $map->depth } @$perms);
766 #warn( "Updating permissions with method $method and session $ses and map $map" );
767 $logger->info( "Updating permissions with method $method and map $map" );
769 my $stat = $session->request($method, $map)->gather(1);
770 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
774 $apputils->commit_db_session($session);
776 return scalar(@$maps);
780 sub _create_standing_penalties {
782 my($session, $user_session, $patron, $new_patron) = @_;
784 my $maps = $patron->standing_penalties;
787 for my $map (@$maps) {
789 if ($map->isdeleted()) {
790 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
791 } elsif ($map->isnew()) {
792 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
798 $map->usr($new_patron->id);
800 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
802 my $stat = $session->request($method, $map)->gather(1);
803 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
806 return ($new_patron, undef);
811 __PACKAGE__->register_method(
812 method => "search_username",
813 api_name => "open-ils.actor.user.search.username",
816 sub search_username {
817 my($self, $client, $username) = @_;
818 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
820 "open-ils.cstore.direct.actor.user.search.atomic",
821 { usrname => $username }
829 __PACKAGE__->register_method(
830 method => "user_retrieve_by_barcode",
831 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
833 sub user_retrieve_by_barcode {
834 my($self, $client, $user_session, $barcode) = @_;
836 $logger->debug("Searching for user with barcode $barcode");
837 my ($user_obj, $evt) = $apputils->checkses($user_session);
840 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
842 "open-ils.cstore.direct.actor.card.search.atomic",
843 { barcode => $barcode }
846 if(!$card || !$card->[0]) {
847 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
851 my $user = flesh_user($card->usr());
853 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
856 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
863 __PACKAGE__->register_method(
864 method => "get_user_by_id",
865 api_name => "open-ils.actor.user.retrieve",);
868 my ($self, $client, $auth, $id) = @_;
869 my $e = new_editor(authtoken=>$auth);
870 return $e->event unless $e->checkauth;
871 my $user = $e->retrieve_actor_user($id)
873 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
879 __PACKAGE__->register_method(
880 method => "get_org_types",
881 api_name => "open-ils.actor.org_types.retrieve",);
885 my($self, $client) = @_;
886 return $org_types if $org_types;
887 return $org_types = new_editor()->retrieve_all_actor_org_unit_type();
892 __PACKAGE__->register_method(
893 method => "get_user_ident_types",
894 api_name => "open-ils.actor.user.ident_types.retrieve",
897 sub get_user_ident_types {
898 return $ident_types if $ident_types;
899 return $ident_types =
900 new_editor()->retrieve_all_config_identification_type();
906 __PACKAGE__->register_method(
907 method => "get_org_unit",
908 api_name => "open-ils.actor.org_unit.retrieve",
912 my( $self, $client, $user_session, $org_id ) = @_;
913 my $e = new_editor(authtoken => $user_session);
915 return $e->event unless $e->checkauth;
916 $org_id = $e->requestor->ws_ou;
918 my $o = $e->retrieve_actor_org_unit($org_id)
923 __PACKAGE__->register_method(
924 method => "search_org_unit",
925 api_name => "open-ils.actor.org_unit_list.search",
928 sub search_org_unit {
930 my( $self, $client, $field, $value ) = @_;
932 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
934 "open-ils.cstore.direct.actor.org_unit.search.atomic",
935 { $field => $value } );
943 __PACKAGE__->register_method(
944 method => "get_org_tree",
945 api_name => "open-ils.actor.org_tree.retrieve",
947 note => "Returns the entire org tree structure",
951 my( $self, $client) = @_;
953 $cache = OpenSRF::Utils::Cache->new("global", 0) unless $cache;
954 my $tree = $cache->get_cache('orgtree');
955 return $tree if $tree;
957 $tree = new_editor()->search_actor_org_unit(
959 {"parent_ou" => undef },
962 flesh_fields => { aou => ['children'] },
963 order_by => { aou => 'name'}
968 $cache->put_cache('orgtree', $tree);
973 # turns an org list into an org tree
976 my( $self, $orglist) = @_;
978 return $orglist unless (
979 ref($orglist) and @$orglist > 1 );
982 $a->ou_type <=> $b->ou_type ||
983 $a->name cmp $b->name } @$orglist;
985 for my $org (@list) {
987 next unless ($org and defined($org->parent_ou));
988 my ($parent) = grep { $_->id == $org->parent_ou } @list;
991 $parent->children([]) unless defined($parent->children);
992 push( @{$parent->children}, $org );
1000 __PACKAGE__->register_method(
1001 method => "get_org_descendants",
1002 api_name => "open-ils.actor.org_tree.descendants.retrieve"
1005 # depth is optional. org_unit is the id
1006 sub get_org_descendants {
1007 my( $self, $client, $org_unit, $depth ) = @_;
1008 my $orglist = $apputils->simple_scalar_request(
1010 "open-ils.storage.actor.org_unit.descendants.atomic",
1011 $org_unit, $depth );
1012 return $self->build_org_tree($orglist);
1016 __PACKAGE__->register_method(
1017 method => "get_org_ancestors",
1018 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
1021 # depth is optional. org_unit is the id
1022 sub get_org_ancestors {
1023 my( $self, $client, $org_unit, $depth ) = @_;
1024 my $orglist = $apputils->simple_scalar_request(
1026 "open-ils.storage.actor.org_unit.ancestors.atomic",
1027 $org_unit, $depth );
1028 return $self->build_org_tree($orglist);
1032 __PACKAGE__->register_method(
1033 method => "get_standings",
1034 api_name => "open-ils.actor.standings.retrieve"
1039 return $user_standings if $user_standings;
1040 return $user_standings =
1041 $apputils->simple_scalar_request(
1043 "open-ils.cstore.direct.config.standing.search.atomic",
1044 { id => { "!=" => undef } }
1050 __PACKAGE__->register_method(
1051 method => "get_my_org_path",
1052 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1055 sub get_my_org_path {
1056 my( $self, $client, $user_session, $org_id ) = @_;
1057 my $user_obj = $apputils->check_user_session($user_session);
1058 if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
1060 return $apputils->simple_scalar_request(
1062 "open-ils.storage.actor.org_unit.full_path.atomic",
1067 __PACKAGE__->register_method(
1068 method => "patron_adv_search",
1069 api_name => "open-ils.actor.patron.search.advanced" );
1070 sub patron_adv_search {
1071 my( $self, $client, $auth, $search_hash, $search_limit, $search_sort, $include_inactive ) = @_;
1072 my $e = new_editor(authtoken=>$auth);
1073 return $e->event unless $e->checkauth;
1074 return $e->event unless $e->allowed('VIEW_USER');
1075 return $U->storagereq(
1076 "open-ils.storage.actor.user.crazy_search",
1077 $search_hash, $search_limit, $search_sort, $include_inactive);
1082 sub _verify_password {
1083 my($user_session, $password) = @_;
1084 my $user_obj = $apputils->check_user_session($user_session);
1086 #grab the user with password
1087 $user_obj = $apputils->simple_scalar_request(
1089 "open-ils.cstore.direct.actor.user.retrieve",
1092 if($user_obj->passwd eq $password) {
1100 __PACKAGE__->register_method(
1101 method => "update_password",
1102 api_name => "open-ils.actor.user.password.update");
1104 __PACKAGE__->register_method(
1105 method => "update_password",
1106 api_name => "open-ils.actor.user.username.update");
1108 __PACKAGE__->register_method(
1109 method => "update_password",
1110 api_name => "open-ils.actor.user.email.update");
1112 sub update_password {
1113 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1117 my $user_obj = $apputils->check_user_session($user_session);
1119 if($self->api_name =~ /password/o) {
1121 #make sure they know the current password
1122 if(!_verify_password($user_session, md5_hex($current_password))) {
1123 return OpenILS::Event->new('INCORRECT_PASSWORD');
1126 $logger->debug("update_password setting new password $new_value");
1127 $user_obj->passwd($new_value);
1129 } elsif($self->api_name =~ /username/o) {
1130 my $users = search_username(undef, undef, $new_value);
1131 if( $users and $users->[0] ) {
1132 return OpenILS::Event->new('USERNAME_EXISTS');
1134 $user_obj->usrname($new_value);
1136 } elsif($self->api_name =~ /email/o) {
1137 #warn "Updating email to $new_value\n";
1138 $user_obj->email($new_value);
1141 my $session = $apputils->start_db_session();
1143 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1144 return $evt if $evt;
1146 $apputils->commit_db_session($session);
1148 if($user_obj) { return 1; }
1153 __PACKAGE__->register_method(
1154 method => "check_user_perms",
1155 api_name => "open-ils.actor.user.perm.check",
1156 notes => <<" NOTES");
1157 Takes a login session, user id, an org id, and an array of perm type strings. For each
1158 perm type, if the user does *not* have the given permission it is added
1159 to a list which is returned from the method. If all permissions
1160 are allowed, an empty list is returned
1161 if the logged in user does not match 'user_id', then the logged in user must
1162 have VIEW_PERMISSION priveleges.
1165 sub check_user_perms {
1166 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1168 my( $staff, $evt ) = $apputils->checkses($login_session);
1169 return $evt if $evt;
1171 if($staff->id ne $user_id) {
1172 if( $evt = $apputils->check_perms(
1173 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1179 for my $perm (@$perm_types) {
1180 if($apputils->check_perms($user_id, $org_id, $perm)) {
1181 push @not_allowed, $perm;
1185 return \@not_allowed
1188 __PACKAGE__->register_method(
1189 method => "check_user_perms2",
1190 api_name => "open-ils.actor.user.perm.check.multi_org",
1192 Checks the permissions on a list of perms and orgs for a user
1193 @param authtoken The login session key
1194 @param user_id The id of the user to check
1195 @param orgs The array of org ids
1196 @param perms The array of permission names
1197 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1198 if the logged in user does not match 'user_id', then the logged in user must
1199 have VIEW_PERMISSION priveleges.
1202 sub check_user_perms2 {
1203 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1205 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1206 $authtoken, $user_id, 'VIEW_PERMISSION' );
1207 return $evt if $evt;
1210 for my $org (@$orgs) {
1211 for my $perm (@$perms) {
1212 if($apputils->check_perms($user_id, $org, $perm)) {
1213 push @not_allowed, [ $org, $perm ];
1218 return \@not_allowed
1222 __PACKAGE__->register_method(
1223 method => 'check_user_perms3',
1224 api_name => 'open-ils.actor.user.perm.highest_org',
1226 Returns the highest org unit id at which a user has a given permission
1227 If the requestor does not match the target user, the requestor must have
1228 'VIEW_PERMISSION' rights at the home org unit of the target user
1229 @param authtoken The login session key
1230 @param userid The id of the user in question
1231 @param perm The permission to check
1232 @return The org unit highest in the org tree within which the user has
1233 the requested permission
1236 sub check_user_perms3 {
1237 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1239 my( $staff, $target, $org, $evt );
1241 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1242 $authtoken, $userid, 'VIEW_PERMISSION' );
1243 return $evt if $evt;
1245 my $tree = $self->get_org_tree();
1246 return _find_highest_perm_org( $perm, $userid, $target->ws_ou, $tree );
1250 sub _find_highest_perm_org {
1251 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1252 my $org = $apputils->find_org($org_tree, $start_org );
1256 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1258 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1264 __PACKAGE__->register_method(
1265 method => 'check_user_perms4',
1266 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1268 Returns the highest org unit id at which a user has a given permission
1269 If the requestor does not match the target user, the requestor must have
1270 'VIEW_PERMISSION' rights at the home org unit of the target user
1271 @param authtoken The login session key
1272 @param userid The id of the user in question
1273 @param perms An array of perm names to check
1274 @return An array of orgId's representing the org unit
1275 highest in the org tree within which the user has the requested permission
1276 The arrah of orgId's has matches the order of the perms array
1279 sub check_user_perms4 {
1280 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1282 my( $staff, $target, $org, $evt );
1284 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1285 $authtoken, $userid, 'VIEW_PERMISSION' );
1286 return $evt if $evt;
1289 return [] unless ref($perms);
1290 my $tree = $self->get_org_tree();
1292 for my $p (@$perms) {
1293 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1301 __PACKAGE__->register_method(
1302 method => "user_fines_summary",
1303 api_name => "open-ils.actor.user.fines.summary",
1304 notes => <<" NOTES");
1305 Returns a short summary of the users total open fines, excluding voided fines
1306 Params are login_session, user_id
1307 Returns a 'mous' object.
1310 sub user_fines_summary {
1311 my( $self, $client, $auth, $user_id ) = @_;
1312 my $e = new_editor(authtoken=>$auth);
1313 return $e->event unless $e->checkauth;
1314 my $user = $e->retrieve_actor_user($user_id)
1315 or return $e->event;
1317 if( $user_id ne $e->requestor->id ) {
1318 return $e->event unless
1319 $e->allowed('VIEW_USER_FINES_SUMMARY', $user->home_ou);
1322 return $apputils->simple_scalar_request(
1324 'open-ils.storage.money.open_user_summary.search', $user_id );
1330 __PACKAGE__->register_method(
1331 method => "user_transactions",
1332 api_name => "open-ils.actor.user.transactions",
1333 notes => <<" NOTES");
1334 Returns a list of open user transactions (mbts objects);
1335 Params are login_session, user_id
1336 Optional third parameter is the transactions type. defaults to all
1339 __PACKAGE__->register_method(
1340 method => "user_transactions",
1341 api_name => "open-ils.actor.user.transactions.have_charge",
1342 notes => <<" NOTES");
1343 Returns a list of all open user transactions (mbts objects) that have an initial charge
1344 Params are login_session, user_id
1345 Optional third parameter is the transactions type. defaults to all
1348 __PACKAGE__->register_method(
1349 method => "user_transactions",
1350 api_name => "open-ils.actor.user.transactions.have_balance",
1351 notes => <<" NOTES");
1352 Returns a list of all open user transactions (mbts objects) that have a balance
1353 Params are login_session, user_id
1354 Optional third parameter is the transactions type. defaults to all
1357 __PACKAGE__->register_method(
1358 method => "user_transactions",
1359 api_name => "open-ils.actor.user.transactions.fleshed",
1360 notes => <<" NOTES");
1361 Returns an object/hash of transaction, circ, title where transaction = an open
1362 user transactions (mbts objects), circ is the attached circluation, and title
1363 is the title the circ points to
1364 Params are login_session, user_id
1365 Optional third parameter is the transactions type. defaults to all
1368 __PACKAGE__->register_method(
1369 method => "user_transactions",
1370 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1371 notes => <<" NOTES");
1372 Returns an object/hash of transaction, circ, title where transaction = an open
1373 user transactions that has an initial charge (mbts objects), circ is the
1374 attached circluation, and title is the title the circ points to
1375 Params are login_session, user_id
1376 Optional third parameter is the transactions type. defaults to all
1379 __PACKAGE__->register_method(
1380 method => "user_transactions",
1381 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1382 notes => <<" NOTES");
1383 Returns an object/hash of transaction, circ, title where transaction = an open
1384 user transaction that has a balance (mbts objects), circ is the attached
1385 circluation, and title is the title the circ points to
1386 Params are login_session, user_id
1387 Optional third parameter is the transaction type. defaults to all
1390 __PACKAGE__->register_method(
1391 method => "user_transactions",
1392 api_name => "open-ils.actor.user.transactions.count",
1393 notes => <<" NOTES");
1394 Returns an object/hash of transaction, circ, title where transaction = an open
1395 user transactions (mbts objects), circ is the attached circluation, and title
1396 is the title the circ points to
1397 Params are login_session, user_id
1398 Optional third parameter is the transactions type. defaults to all
1401 __PACKAGE__->register_method(
1402 method => "user_transactions",
1403 api_name => "open-ils.actor.user.transactions.have_charge.count",
1404 notes => <<" NOTES");
1405 Returns an object/hash of transaction, circ, title where transaction = an open
1406 user transactions that has an initial charge (mbts objects), circ is the
1407 attached circluation, and title is the title the circ points to
1408 Params are login_session, user_id
1409 Optional third parameter is the transactions type. defaults to all
1412 __PACKAGE__->register_method(
1413 method => "user_transactions",
1414 api_name => "open-ils.actor.user.transactions.have_balance.count",
1415 notes => <<" NOTES");
1416 Returns an object/hash of transaction, circ, title where transaction = an open
1417 user transaction that has a balance (mbts objects), circ is the attached
1418 circluation, and title is the title the circ points to
1419 Params are login_session, user_id
1420 Optional third parameter is the transaction type. defaults to all
1423 __PACKAGE__->register_method(
1424 method => "user_transactions",
1425 api_name => "open-ils.actor.user.transactions.have_balance.total",
1426 notes => <<" NOTES");
1427 Returns an object/hash of transaction, circ, title where transaction = an open
1428 user transaction that has a balance (mbts objects), circ is the attached
1429 circluation, and title is the title the circ points to
1430 Params are login_session, user_id
1431 Optional third parameter is the transaction type. defaults to all
1436 sub user_transactions {
1437 my( $self, $client, $login_session, $user_id, $type ) = @_;
1439 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1440 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1441 return $evt if $evt;
1443 my $api = $self->api_name();
1447 if(defined($type)) { @xact = (xact_type => $type);
1449 } else { @xact = (); }
1452 ->method_lookup('open-ils.actor.user.transactions.history.still_open')
1453 ->run($login_session => $user_id => $type);
1455 if($api =~ /have_charge/o) {
1457 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1459 } elsif($api =~ /have_balance/o) {
1461 $trans = [ grep { int($_->balance_owed * 100) != 0 } @$trans ];
1464 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1468 if($api =~ /total/o) {
1470 for my $t (@$trans) {
1471 $total += $t->balance_owed;
1474 $logger->debug("Total balance owed by user $user_id: $total");
1478 if($api =~ /count/o) { return scalar @$trans; }
1479 if($api !~ /fleshed/o) { return $trans; }
1482 for my $t (@$trans) {
1484 if( $t->xact_type ne 'circulation' ) {
1485 push @resp, {transaction => $t};
1489 my $circ = $apputils->simple_scalar_request(
1491 "open-ils.cstore.direct.action.circulation.retrieve",
1496 my $title = $apputils->simple_scalar_request(
1498 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1499 $circ->target_copy );
1503 my $u = OpenILS::Utils::ModsParser->new();
1504 $u->start_mods_batch($title->marc());
1505 my $mods = $u->finish_mods_batch();
1506 $mods->doc_id($title->id) if $mods;
1508 push @resp, {transaction => $t, circ => $circ, record => $mods };
1516 __PACKAGE__->register_method(
1517 method => "user_transaction_retrieve",
1518 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1520 notes => <<" NOTES");
1521 Returns a fleshedtransaction record
1523 __PACKAGE__->register_method(
1524 method => "user_transaction_retrieve",
1525 api_name => "open-ils.actor.user.transaction.retrieve",
1527 notes => <<" NOTES");
1528 Returns a transaction record
1530 sub user_transaction_retrieve {
1531 my( $self, $client, $login_session, $bill_id ) = @_;
1533 my $trans = $apputils->simple_scalar_request(
1535 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1539 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1540 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1541 return $evt if $evt;
1543 my $api = $self->api_name();
1544 if($api !~ /fleshed/o) { return $trans; }
1546 if( $trans->xact_type ne 'circulation' ) {
1547 $logger->debug("Returning non-circ transaction");
1548 return {transaction => $trans};
1551 my $circ = $apputils->simple_scalar_request(
1553 "open-ils..direct.action.circulation.retrieve",
1556 return {transaction => $trans} unless $circ;
1557 $logger->debug("Found the circ transaction");
1559 my $title = $apputils->simple_scalar_request(
1561 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1562 $circ->target_copy );
1564 return {transaction => $trans, circ => $circ } unless $title;
1565 $logger->debug("Found the circ title");
1569 my $u = OpenILS::Utils::ModsParser->new();
1570 $u->start_mods_batch($title->marc());
1571 $mods = $u->finish_mods_batch();
1573 if ($title->id == OILS_PRECAT_RECORD) {
1574 my $copy = $apputils->simple_scalar_request(
1576 "open-ils.cstore.direct.asset.copy.retrieve",
1577 $circ->target_copy );
1579 $mods = new Fieldmapper::metabib::virtual_record;
1580 $mods->doc_id(OILS_PRECAT_RECORD);
1581 $mods->title($copy->dummy_title);
1582 $mods->author($copy->dummy_author);
1586 $logger->debug("MODSized the circ title");
1588 return {transaction => $trans, circ => $circ, record => $mods };
1592 __PACKAGE__->register_method(
1593 method => "hold_request_count",
1594 api_name => "open-ils.actor.user.hold_requests.count",
1596 notes => <<" NOTES");
1597 Returns hold ready/total counts
1599 sub hold_request_count {
1600 my( $self, $client, $login_session, $userid ) = @_;
1602 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1603 $login_session, $userid, 'VIEW_HOLD' );
1604 return $evt if $evt;
1607 my $holds = $apputils->simple_scalar_request(
1609 "open-ils.cstore.direct.action.hold_request.search.atomic",
1612 fulfillment_time => {"=" => undef },
1613 cancel_time => undef,
1618 for my $h (@$holds) {
1619 next unless $h->capture_time and $h->current_copy;
1621 my $copy = $apputils->simple_scalar_request(
1623 "open-ils.cstore.direct.asset.copy.retrieve",
1627 if ($copy and $copy->status == 8) {
1632 return { total => scalar(@$holds), ready => scalar(@ready) };
1636 __PACKAGE__->register_method(
1637 method => "checkedout_count",
1638 api_name => "open-ils.actor.user.checked_out.count__",
1640 notes => <<" NOTES");
1641 Returns a transaction record
1645 sub checkedout_count {
1646 my( $self, $client, $login_session, $userid ) = @_;
1648 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1649 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1650 return $evt if $evt;
1652 my $circs = $apputils->simple_scalar_request(
1654 "open-ils.cstore.direct.action.circulation.search.atomic",
1655 { usr => $userid, stop_fines => undef }
1656 #{ usr => $userid, checkin_time => {"=" => undef } }
1659 my $parser = DateTime::Format::ISO8601->new;
1662 for my $c (@$circs) {
1663 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1664 my $due = $due_dt->epoch;
1666 if ($due < DateTime->today->epoch) {
1671 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1675 __PACKAGE__->register_method(
1676 method => "checked_out",
1677 api_name => "open-ils.actor.user.checked_out",
1680 Returns a structure of circulations objects sorted by
1681 out, overdue, lost, claims_returned, long_overdue.
1682 A list of IDs are returned of each type.
1683 lost, long_overdue, and claims_returned circ will not
1684 be "finished" (there is an outstanding balance or some
1685 other pending action on the circ).
1687 The .count method also includes a 'total' field which
1688 sums all "open" circs
1692 __PACKAGE__->register_method(
1693 method => "checked_out",
1694 api_name => "open-ils.actor.user.checked_out.count",
1696 signature => q/@see open-ils.actor.user.checked_out/
1700 my( $self, $conn, $auth, $userid ) = @_;
1702 my $e = new_editor(authtoken=>$auth);
1703 return $e->event unless $e->checkauth;
1705 if( $userid ne $e->requestor->id ) {
1706 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1709 my $count = $self->api_name =~ /count/;
1710 return _checked_out( $count, $e, $userid );
1714 my( $iscount, $e, $userid ) = @_;
1717 my $meth = 'open-ils.storage.actor.user.checked_out';
1718 $meth = "$meth.count" if $iscount;
1719 return $U->storagereq($meth, $userid);
1721 # XXX Old code - moved to storage
1722 #------------------------------------------------------------------------------
1723 #------------------------------------------------------------------------------
1724 my $circs = $e->search_action_circulation(
1725 { usr => $userid, checkin_time => undef });
1727 my $parser = DateTime::Format::ISO8601->new;
1729 # split the circs up into overdue and not-overdue circs
1731 for my $c (@$circs) {
1732 if( $c->due_date ) {
1733 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1734 my $due = $due_dt->epoch;
1735 if ($due < DateTime->today->epoch) {
1745 my( @open, @od, @lost, @cr, @lo );
1747 while (my $c = shift(@out)) {
1748 push( @open, $c->id ) if (!$c->stop_fines || $c->stop_fines eq 'MAXFINES' || $c->stop_fines eq 'RENEW');
1749 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1750 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1751 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1754 while (my $c = shift(@overdue)) {
1755 push( @od, $c->id ) if (!$c->stop_fines || $c->stop_fines eq 'MAXFINES' || $c->stop_fines eq 'RENEW');
1756 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1757 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1758 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1763 total => @open + @od + @lost + @cr + @lo,
1764 out => scalar(@open),
1765 overdue => scalar(@od),
1766 lost => scalar(@lost),
1767 claims_returned => scalar(@cr),
1768 long_overdue => scalar(@lo)
1776 claims_returned => \@cr,
1777 long_overdue => \@lo
1782 sub _checked_out_WHAT {
1783 my( $iscount, $e, $userid ) = @_;
1785 my $circs = $e->search_action_circulation(
1786 { usr => $userid, stop_fines => undef });
1788 my $mcircs = $e->search_action_circulation(
1791 checkin_time => undef,
1792 xact_finish => undef,
1796 push( @$circs, @$mcircs );
1798 my $parser = DateTime::Format::ISO8601->new;
1800 # split the circs up into overdue and not-overdue circs
1802 for my $c (@$circs) {
1803 if( $c->due_date ) {
1804 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1805 my $due = $due_dt->epoch;
1806 if ($due < DateTime->today->epoch) {
1807 push @overdue, $c->id;
1816 # grab all of the lost, claims-returned, and longoverdue circs
1817 #my $open = $e->search_action_circulation(
1818 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1821 # these items have stop_fines, but no xact_finish, so money
1822 # is owed on them and they have not been checked in
1823 my $open = $e->search_action_circulation(
1826 stop_fines => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] },
1827 xact_finish => undef,
1828 checkin_time => undef,
1833 my( @lost, @cr, @lo );
1834 for my $c (@$open) {
1835 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1836 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1837 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1843 total => @$circs + @lost + @cr + @lo,
1844 out => scalar(@out),
1845 overdue => scalar(@overdue),
1846 lost => scalar(@lost),
1847 claims_returned => scalar(@cr),
1848 long_overdue => scalar(@lo)
1854 overdue => \@overdue,
1856 claims_returned => \@cr,
1857 long_overdue => \@lo
1863 __PACKAGE__->register_method(
1864 method => "checked_in_with_fines",
1865 api_name => "open-ils.actor.user.checked_in_with_fines",
1867 signature => q/@see open-ils.actor.user.checked_out/
1869 sub checked_in_with_fines {
1870 my( $self, $conn, $auth, $userid ) = @_;
1872 my $e = new_editor(authtoken=>$auth);
1873 return $e->event unless $e->checkauth;
1875 if( $userid ne $e->requestor->id ) {
1876 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1879 # money is owed on these items and they are checked in
1880 my $open = $e->search_action_circulation(
1883 xact_finish => undef,
1884 checkin_time => { "!=" => undef },
1889 my( @lost, @cr, @lo );
1890 for my $c (@$open) {
1891 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1892 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1893 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1898 claims_returned => \@cr,
1899 long_overdue => \@lo
1911 __PACKAGE__->register_method(
1912 method => "user_transaction_history",
1913 api_name => "open-ils.actor.user.transactions.history",
1915 notes => <<" NOTES");
1916 Returns a list of billable transaction ids for a user, optionally by type
1918 __PACKAGE__->register_method(
1919 method => "user_transaction_history",
1920 api_name => "open-ils.actor.user.transactions.history.have_charge",
1922 notes => <<" NOTES");
1923 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1925 __PACKAGE__->register_method(
1926 method => "user_transaction_history",
1927 api_name => "open-ils.actor.user.transactions.history.have_balance",
1929 notes => <<" NOTES");
1930 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1932 __PACKAGE__->register_method(
1933 method => "user_transaction_history",
1934 api_name => "open-ils.actor.user.transactions.history.still_open",
1936 notes => <<" NOTES");
1937 Returns a list of billable transaction ids for a user that are not finished
1939 __PACKAGE__->register_method(
1940 method => "user_transaction_history",
1941 api_name => "open-ils.actor.user.transactions.history.have_bill",
1943 notes => <<" NOTES");
1944 Returns a list of billable transaction ids for a user that has billings
1950 sub _user_transaction_history {
1951 my( $self, $client, $login_session, $user_id, $type ) = @_;
1953 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1954 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1955 return $evt if $evt;
1957 my $api = $self->api_name();
1962 @xact = (xact_type => $type) if(defined($type));
1963 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1964 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1966 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1968 my $trans = $apputils->simple_scalar_request(
1970 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1971 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1973 return [ map { $_->id } @$trans ];
1981 for my $x (@xacts) {
1982 my $s = new Fieldmapper::money::billable_transaction_summary;
1985 $s->xact_start( $x->xact_start );
1986 $s->xact_finish( $x->xact_finish );
1990 for my $b (@{ $x->billings }) {
1991 next if ($U->is_true($b->voided));
1992 $to += ($b->amount * 100);
1993 $lb ||= $b->billing_ts;
1994 if ($b->billing_ts ge $lb) {
1995 $lb = $b->billing_ts;
1996 $s->last_billing_note($b->note);
1997 $s->last_billing_ts($b->billing_ts);
1998 $s->last_billing_type($b->billing_type);
2002 $s->total_owed( sprintf('%0.2f', $to / 100 ) );
2006 for my $p (@{ $x->payments }) {
2007 next if ($U->is_true($p->voided));
2008 $tp += ($p->amount * 100);
2009 $lp ||= $p->payment_ts;
2010 if ($p->payment_ts ge $lp) {
2011 $lp = $p->payment_ts;
2012 $s->last_payment_note($p->note);
2013 $s->last_payment_ts($p->payment_ts);
2014 $s->last_payment_type($p->payment_type);
2017 $s->total_paid( sprintf('%0.2f', $tp / 100 ) );
2019 $s->balance_owed( sprintf('%0.2f', ($to - $tp) / 100) );
2021 $s->xact_type( 'grocery' ) if ($x->grocery);
2022 $s->xact_type( 'circulation' ) if ($x->circulation);
2030 sub user_transaction_history {
2031 my( $self, $conn, $auth, $userid, $type ) = @_;
2032 my $e = new_editor(authtoken=>$auth);
2033 return $e->event unless $e->checkauth;
2034 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
2036 my $api = $self->api_name;
2037 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
2039 my @xacts = @{ $e->search_money_billable_transaction(
2040 [ { usr => $userid, @xact_finish },
2042 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
2043 order_by => { mbt => 'xact_start DESC' },
2048 my @mbts = _make_mbts( @xacts );
2050 if(defined($type)) {
2051 @mbts = grep { $_->xact_type eq $type } @mbts;
2054 if($api =~ /have_balance/o) {
2055 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
2058 if($api =~ /have_charge/o) {
2059 @mbts = grep { defined($_->last_billing_ts) } @mbts;
2062 if($api =~ /have_bill/o) {
2063 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
2071 __PACKAGE__->register_method(
2072 method => "user_perms",
2073 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2075 notes => <<" NOTES");
2076 Returns a list of permissions
2079 my( $self, $client, $authtoken, $user ) = @_;
2081 my( $staff, $evt ) = $apputils->checkses($authtoken);
2082 return $evt if $evt;
2084 $user ||= $staff->id;
2086 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2090 return $apputils->simple_scalar_request(
2092 "open-ils.storage.permission.user_perms.atomic",
2096 __PACKAGE__->register_method(
2097 method => "retrieve_perms",
2098 api_name => "open-ils.actor.permissions.retrieve",
2099 notes => <<" NOTES");
2100 Returns a list of permissions
2102 sub retrieve_perms {
2103 my( $self, $client ) = @_;
2104 return $apputils->simple_scalar_request(
2106 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2107 { id => { '!=' => undef } }
2111 __PACKAGE__->register_method(
2112 method => "retrieve_groups",
2113 api_name => "open-ils.actor.groups.retrieve",
2114 notes => <<" NOTES");
2115 Returns a list of user groupss
2117 sub retrieve_groups {
2118 my( $self, $client ) = @_;
2119 return new_editor()->retrieve_all_permission_grp_tree();
2122 __PACKAGE__->register_method(
2123 method => "retrieve_org_address",
2124 api_name => "open-ils.actor.org_unit.address.retrieve",
2125 notes => <<' NOTES');
2126 Returns an org_unit address by ID
2127 @param An org_address ID
2129 sub retrieve_org_address {
2130 my( $self, $client, $id ) = @_;
2131 return $apputils->simple_scalar_request(
2133 "open-ils.cstore.direct.actor.org_address.retrieve",
2138 __PACKAGE__->register_method(
2139 method => "retrieve_groups_tree",
2140 api_name => "open-ils.actor.groups.tree.retrieve",
2141 notes => <<" NOTES");
2142 Returns a list of user groups
2144 sub retrieve_groups_tree {
2145 my( $self, $client ) = @_;
2146 return new_editor()->search_permission_grp_tree(
2151 flesh_fields => { pgt => ["children"] },
2152 order_by => { pgt => 'name'}
2159 # turns an org list into an org tree
2161 sub build_group_tree {
2163 my( $self, $grplist) = @_;
2165 return $grplist unless (
2166 ref($grplist) and @$grplist > 1 );
2168 my @list = sort { $a->name cmp $b->name } @$grplist;
2171 for my $grp (@list) {
2173 if ($grp and !defined($grp->parent)) {
2177 my ($parent) = grep { $_->id == $grp->parent} @list;
2179 $parent->children([]) unless defined($parent->children);
2180 push( @{$parent->children}, $grp );
2188 __PACKAGE__->register_method(
2189 method => "add_user_to_groups",
2190 api_name => "open-ils.actor.user.set_groups",
2191 notes => <<" NOTES");
2192 Adds a user to one or more permission groups
2195 sub add_user_to_groups {
2196 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2198 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2199 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2200 return $evt if $evt;
2202 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2203 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2204 return $evt if $evt;
2206 $apputils->simplereq(
2208 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2210 for my $group (@$groups) {
2211 my $link = Fieldmapper::permission::usr_grp_map->new;
2213 $link->usr($userid);
2215 my $id = $apputils->simplereq(
2217 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2223 __PACKAGE__->register_method(
2224 method => "get_user_perm_groups",
2225 api_name => "open-ils.actor.user.get_groups",
2226 notes => <<" NOTES");
2227 Retrieve a user's permission groups.
2231 sub get_user_perm_groups {
2232 my( $self, $client, $authtoken, $userid ) = @_;
2234 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2235 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2236 return $evt if $evt;
2238 return $apputils->simplereq(
2240 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2245 __PACKAGE__->register_method (
2246 method => 'register_workstation',
2247 api_name => 'open-ils.actor.workstation.register.override',
2248 signature => q/@see open-ils.actor.workstation.register/);
2250 __PACKAGE__->register_method (
2251 method => 'register_workstation',
2252 api_name => 'open-ils.actor.workstation.register',
2254 Registers a new workstion in the system
2255 @param authtoken The login session key
2256 @param name The name of the workstation id
2257 @param owner The org unit that owns this workstation
2258 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2259 if the name is already in use.
2262 sub _register_workstation {
2263 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2264 my( $requestor, $evt ) = $U->checkses($authtoken);
2265 return $evt if $evt;
2266 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2267 return $evt if $evt;
2269 my $ws = $U->cstorereq(
2270 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2271 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2273 $ws = Fieldmapper::actor::workstation->new;
2274 $ws->owning_lib($owner);
2277 my $id = $U->storagereq(
2278 'open-ils.storage.direct.actor.workstation.create', $ws );
2279 return $U->DB_UPDATE_FAILED($ws) unless $id;
2285 sub register_workstation {
2286 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2288 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2289 return $e->event unless $e->checkauth;
2290 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2291 my $existing = $e->search_actor_workstation({name => $name});
2294 if( $self->api_name =~ /override/o ) {
2295 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2296 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2298 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2302 my $ws = Fieldmapper::actor::workstation->new;
2303 $ws->owning_lib($owner);
2305 $e->create_actor_workstation($ws) or return $e->event;
2307 return $ws->id; # note: editor sets the id on the new object for us
2311 __PACKAGE__->register_method (
2312 method => 'fetch_patron_note',
2313 api_name => 'open-ils.actor.note.retrieve.all',
2315 Returns a list of notes for a given user
2316 Requestor must have VIEW_USER permission if pub==false and
2317 @param authtoken The login session key
2318 @param args Hash of params including
2319 patronid : the patron's id
2320 pub : true if retrieving only public notes
2324 sub fetch_patron_note {
2325 my( $self, $conn, $authtoken, $args ) = @_;
2326 my $patronid = $$args{patronid};
2328 my($reqr, $evt) = $U->checkses($authtoken);
2329 return $evt if $evt;
2332 ($patron, $evt) = $U->fetch_user($patronid);
2333 return $evt if $evt;
2336 if( $patronid ne $reqr->id ) {
2337 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2338 return $evt if $evt;
2340 return $U->cstorereq(
2341 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2342 { usr => $patronid, pub => 't' } );
2345 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2346 return $evt if $evt;
2348 return $U->cstorereq(
2349 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2352 __PACKAGE__->register_method (
2353 method => 'create_user_note',
2354 api_name => 'open-ils.actor.note.create',
2356 Creates a new note for the given user
2357 @param authtoken The login session key
2358 @param note The note object
2361 sub create_user_note {
2362 my( $self, $conn, $authtoken, $note ) = @_;
2363 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2364 return $e->die_event unless $e->checkauth;
2366 my $user = $e->retrieve_actor_user($note->usr)
2367 or return $e->die_event;
2369 return $e->die_event unless
2370 $e->allowed('UPDATE_USER',$user->home_ou);
2372 $note->creator($e->requestor->id);
2373 $e->create_actor_usr_note($note) or return $e->die_event;
2379 __PACKAGE__->register_method (
2380 method => 'delete_user_note',
2381 api_name => 'open-ils.actor.note.delete',
2383 Deletes a note for the given user
2384 @param authtoken The login session key
2385 @param noteid The note id
2388 sub delete_user_note {
2389 my( $self, $conn, $authtoken, $noteid ) = @_;
2391 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2392 return $e->die_event unless $e->checkauth;
2393 my $note = $e->retrieve_actor_usr_note($noteid)
2394 or return $e->die_event;
2395 my $user = $e->retrieve_actor_user($note->usr)
2396 or return $e->die_event;
2397 return $e->die_event unless
2398 $e->allowed('UPDATE_USER', $user->home_ou);
2400 $e->delete_actor_usr_note($note) or return $e->die_event;
2406 __PACKAGE__->register_method (
2407 method => 'update_user_note',
2408 api_name => 'open-ils.actor.note.update',
2410 @param authtoken The login session key
2411 @param note The note
2415 sub update_user_note {
2416 my( $self, $conn, $auth, $note ) = @_;
2417 my $e = new_editor(authtoken=>$auth, xact=>1);
2418 return $e->event unless $e->checkauth;
2419 my $patron = $e->retrieve_actor_user($note->usr)
2420 or return $e->event;
2421 return $e->event unless
2422 $e->allowed('UPDATE_USER', $patron->home_ou);
2423 $e->update_actor_user_note($note)
2424 or return $e->event;
2432 __PACKAGE__->register_method (
2433 method => 'create_closed_date',
2434 api_name => 'open-ils.actor.org_unit.closed_date.create',
2436 Creates a new closing entry for the given org_unit
2437 @param authtoken The login session key
2438 @param note The closed_date object
2441 sub create_closed_date {
2442 my( $self, $conn, $authtoken, $cd ) = @_;
2444 my( $user, $evt ) = $U->checkses($authtoken);
2445 return $evt if $evt;
2447 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2448 return $evt if $evt;
2450 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2452 my $id = $U->storagereq(
2453 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2454 return $U->DB_UPDATE_FAILED($cd) unless $id;
2459 __PACKAGE__->register_method (
2460 method => 'delete_closed_date',
2461 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2463 Deletes a closing entry for the given org_unit
2464 @param authtoken The login session key
2465 @param noteid The close_date id
2468 sub delete_closed_date {
2469 my( $self, $conn, $authtoken, $cd ) = @_;
2471 my( $user, $evt ) = $U->checkses($authtoken);
2472 return $evt if $evt;
2475 ($cd_obj, $evt) = fetch_closed_date($cd);
2476 return $evt if $evt;
2478 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2479 return $evt if $evt;
2481 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2483 my $stat = $U->storagereq(
2484 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2485 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2490 __PACKAGE__->register_method(
2491 method => 'usrname_exists',
2492 api_name => 'open-ils.actor.username.exists',
2494 Returns 1 if the requested username exists, returns 0 otherwise
2498 sub usrname_exists {
2499 my( $self, $conn, $auth, $usrname ) = @_;
2500 my $e = new_editor(authtoken=>$auth);
2501 return $e->event unless $e->checkauth;
2502 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2503 return $$a[0] if $a and @$a;
2507 __PACKAGE__->register_method(
2508 method => 'barcode_exists',
2509 api_name => 'open-ils.actor.barcode.exists',
2511 Returns 1 if the requested barcode exists, returns 0 otherwise
2515 sub barcode_exists {
2516 my( $self, $conn, $auth, $barcode ) = @_;
2517 my $e = new_editor(authtoken=>$auth);
2518 return $e->event unless $e->checkauth;
2519 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2520 return $$a[0] if $a and @$a;
2525 __PACKAGE__->register_method(
2526 method => 'retrieve_net_levels',
2527 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2530 sub retrieve_net_levels {
2531 my( $self, $conn, $auth ) = @_;
2532 my $e = new_editor(authtoken=>$auth);
2533 return $e->event unless $e->checkauth;
2534 return $e->retrieve_all_config_net_access_level();
2538 __PACKAGE__->register_method(
2539 method => 'fetch_org_by_shortname',
2540 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2542 sub fetch_org_by_shortname {
2543 my( $self, $conn, $sname ) = @_;
2544 my $e = new_editor();
2545 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2546 return $e->event unless $org;
2551 __PACKAGE__->register_method(
2552 method => 'session_home_lib',
2553 api_name => 'open-ils.actor.session.home_lib',
2556 sub session_home_lib {
2557 my( $self, $conn, $auth ) = @_;
2558 my $e = new_editor(authtoken=>$auth);
2559 return undef unless $e->checkauth;
2560 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2561 return $org->shortname;
2566 __PACKAGE__->register_method(
2567 method => 'slim_tree',
2568 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2571 my $tree = new_editor()->search_actor_org_unit(
2573 {"parent_ou" => undef },
2576 flesh_fields => { aou => ['children'] },
2577 order_by => { aou => 'name'},
2578 select => { aou => ["id","shortname", "name"]},
2583 return trim_tree($tree);
2589 return undef unless $tree;
2591 code => $tree->shortname,
2592 name => $tree->name,
2594 if( $tree->children and @{$tree->children} ) {
2595 $htree->{children} = [];
2596 for my $c (@{$tree->children}) {
2597 push( @{$htree->{children}}, trim_tree($c) );
2605 __PACKAGE__->register_method(
2606 method => "update_penalties",
2607 api_name => "open-ils.actor.user.penalties.update");
2608 sub update_penalties {
2609 my( $self, $conn, $auth, $userid ) = @_;
2610 my $e = new_editor(authtoken=>$auth);
2611 return $e->event unless $e->checkauth;
2612 $U->update_patron_penalties(
2614 patronid => $userid,
2621 __PACKAGE__->register_method(
2622 method => "user_retrieve_fleshed_by_id",
2623 api_name => "open-ils.actor.user.fleshed.retrieve",);
2625 sub user_retrieve_fleshed_by_id {
2626 my( $self, $client, $auth, $user_id, $fields ) = @_;
2627 my $e = new_editor(authtoken => $auth);
2628 return $e->event unless $e->checkauth;
2630 if( $e->requestor->id != $user_id ) {
2631 return $e->event unless $e->allowed('VIEW_USER');
2637 "standing_penalties",
2641 "stat_cat_entries" ];
2642 return new_flesh_user($user_id, $fields, $e);
2646 sub new_flesh_user {
2649 my $fields = shift || [];
2650 my $e = shift || new_editor(xact=>1);
2652 my $user = $e->retrieve_actor_user(
2657 "flesh_fields" => { "au" => $fields }
2660 ) or return $e->event;
2663 if( grep { $_ eq 'addresses' } @$fields ) {
2665 $user->addresses([]) unless @{$user->addresses};
2667 if( ref $user->billing_address ) {
2668 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2669 push( @{$user->addresses}, $user->billing_address );
2673 if( ref $user->mailing_address ) {
2674 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2675 push( @{$user->addresses}, $user->mailing_address );
2681 $user->clear_passwd();
2688 __PACKAGE__->register_method(
2689 method => "user_retrieve_parts",
2690 api_name => "open-ils.actor.user.retrieve.parts",);
2692 sub user_retrieve_parts {
2693 my( $self, $client, $auth, $user_id, $fields ) = @_;
2694 my $e = new_editor(authtoken => $auth);
2695 return $e->event unless $e->checkauth;
2696 if( $e->requestor->id != $user_id ) {
2697 return $e->event unless $e->allowed('VIEW_USER');
2700 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2701 push(@resp, $user->$_()) for(@$fields);