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 # run this inside a transaction to prevent replication delay errors
1323 my $ses = $U->start_db_session();
1324 my $s = $ses->request(
1325 'open-ils.storage.money.open_user_summary.search', $user_id )->gather(1);
1326 $U->rollback_db_session($ses);
1333 __PACKAGE__->register_method(
1334 method => "user_transactions",
1335 api_name => "open-ils.actor.user.transactions",
1336 notes => <<" NOTES");
1337 Returns a list of open user transactions (mbts objects);
1338 Params are login_session, user_id
1339 Optional third parameter is the transactions type. defaults to all
1342 __PACKAGE__->register_method(
1343 method => "user_transactions",
1344 api_name => "open-ils.actor.user.transactions.have_charge",
1345 notes => <<" NOTES");
1346 Returns a list of all open user transactions (mbts objects) that have an initial charge
1347 Params are login_session, user_id
1348 Optional third parameter is the transactions type. defaults to all
1351 __PACKAGE__->register_method(
1352 method => "user_transactions",
1353 api_name => "open-ils.actor.user.transactions.have_balance",
1354 notes => <<" NOTES");
1355 Returns a list of all open user transactions (mbts objects) that have a balance
1356 Params are login_session, user_id
1357 Optional third parameter is the transactions type. defaults to all
1360 __PACKAGE__->register_method(
1361 method => "user_transactions",
1362 api_name => "open-ils.actor.user.transactions.fleshed",
1363 notes => <<" NOTES");
1364 Returns an object/hash of transaction, circ, title where transaction = an open
1365 user transactions (mbts objects), circ is the attached circluation, and title
1366 is the title the circ points to
1367 Params are login_session, user_id
1368 Optional third parameter is the transactions type. defaults to all
1371 __PACKAGE__->register_method(
1372 method => "user_transactions",
1373 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1374 notes => <<" NOTES");
1375 Returns an object/hash of transaction, circ, title where transaction = an open
1376 user transactions that has an initial charge (mbts objects), circ is the
1377 attached circluation, and title is the title the circ points to
1378 Params are login_session, user_id
1379 Optional third parameter is the transactions type. defaults to all
1382 __PACKAGE__->register_method(
1383 method => "user_transactions",
1384 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1385 notes => <<" NOTES");
1386 Returns an object/hash of transaction, circ, title where transaction = an open
1387 user transaction that has a balance (mbts objects), circ is the attached
1388 circluation, and title is the title the circ points to
1389 Params are login_session, user_id
1390 Optional third parameter is the transaction type. defaults to all
1393 __PACKAGE__->register_method(
1394 method => "user_transactions",
1395 api_name => "open-ils.actor.user.transactions.count",
1396 notes => <<" NOTES");
1397 Returns an object/hash of transaction, circ, title where transaction = an open
1398 user transactions (mbts objects), circ is the attached circluation, and title
1399 is the title the circ points to
1400 Params are login_session, user_id
1401 Optional third parameter is the transactions type. defaults to all
1404 __PACKAGE__->register_method(
1405 method => "user_transactions",
1406 api_name => "open-ils.actor.user.transactions.have_charge.count",
1407 notes => <<" NOTES");
1408 Returns an object/hash of transaction, circ, title where transaction = an open
1409 user transactions that has an initial charge (mbts objects), circ is the
1410 attached circluation, and title is the title the circ points to
1411 Params are login_session, user_id
1412 Optional third parameter is the transactions type. defaults to all
1415 __PACKAGE__->register_method(
1416 method => "user_transactions",
1417 api_name => "open-ils.actor.user.transactions.have_balance.count",
1418 notes => <<" NOTES");
1419 Returns an object/hash of transaction, circ, title where transaction = an open
1420 user transaction that has a balance (mbts objects), circ is the attached
1421 circluation, and title is the title the circ points to
1422 Params are login_session, user_id
1423 Optional third parameter is the transaction type. defaults to all
1426 __PACKAGE__->register_method(
1427 method => "user_transactions",
1428 api_name => "open-ils.actor.user.transactions.have_balance.total",
1429 notes => <<" NOTES");
1430 Returns an object/hash of transaction, circ, title where transaction = an open
1431 user transaction that has a balance (mbts objects), circ is the attached
1432 circluation, and title is the title the circ points to
1433 Params are login_session, user_id
1434 Optional third parameter is the transaction type. defaults to all
1439 sub user_transactions {
1440 my( $self, $client, $login_session, $user_id, $type ) = @_;
1442 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1443 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1444 return $evt if $evt;
1446 my $api = $self->api_name();
1450 if(defined($type)) { @xact = (xact_type => $type);
1452 } else { @xact = (); }
1455 ->method_lookup('open-ils.actor.user.transactions.history.still_open')
1456 ->run($login_session => $user_id => $type);
1458 if($api =~ /have_charge/o) {
1460 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1462 } elsif($api =~ /have_balance/o) {
1464 $trans = [ grep { int($_->balance_owed * 100) != 0 } @$trans ];
1467 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1471 if($api =~ /total/o) {
1473 for my $t (@$trans) {
1474 $total += $t->balance_owed;
1477 $logger->debug("Total balance owed by user $user_id: $total");
1481 if($api =~ /count/o) { return scalar @$trans; }
1482 if($api !~ /fleshed/o) { return $trans; }
1485 for my $t (@$trans) {
1487 if( $t->xact_type ne 'circulation' ) {
1488 push @resp, {transaction => $t};
1492 my $circ = $apputils->simple_scalar_request(
1494 "open-ils.cstore.direct.action.circulation.retrieve",
1499 my $title = $apputils->simple_scalar_request(
1501 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1502 $circ->target_copy );
1506 my $u = OpenILS::Utils::ModsParser->new();
1507 $u->start_mods_batch($title->marc());
1508 my $mods = $u->finish_mods_batch();
1509 $mods->doc_id($title->id) if $mods;
1511 push @resp, {transaction => $t, circ => $circ, record => $mods };
1519 __PACKAGE__->register_method(
1520 method => "user_transaction_retrieve",
1521 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1523 notes => <<" NOTES");
1524 Returns a fleshedtransaction record
1526 __PACKAGE__->register_method(
1527 method => "user_transaction_retrieve",
1528 api_name => "open-ils.actor.user.transaction.retrieve",
1530 notes => <<" NOTES");
1531 Returns a transaction record
1533 sub user_transaction_retrieve {
1534 my( $self, $client, $login_session, $bill_id ) = @_;
1536 my $trans = $apputils->simple_scalar_request(
1538 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1542 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1543 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1544 return $evt if $evt;
1546 my $api = $self->api_name();
1547 if($api !~ /fleshed/o) { return $trans; }
1549 if( $trans->xact_type ne 'circulation' ) {
1550 $logger->debug("Returning non-circ transaction");
1551 return {transaction => $trans};
1554 my $circ = $apputils->simple_scalar_request(
1556 "open-ils..direct.action.circulation.retrieve",
1559 return {transaction => $trans} unless $circ;
1560 $logger->debug("Found the circ transaction");
1562 my $title = $apputils->simple_scalar_request(
1564 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1565 $circ->target_copy );
1567 return {transaction => $trans, circ => $circ } unless $title;
1568 $logger->debug("Found the circ title");
1572 my $u = OpenILS::Utils::ModsParser->new();
1573 $u->start_mods_batch($title->marc());
1574 $mods = $u->finish_mods_batch();
1576 if ($title->id == OILS_PRECAT_RECORD) {
1577 my $copy = $apputils->simple_scalar_request(
1579 "open-ils.cstore.direct.asset.copy.retrieve",
1580 $circ->target_copy );
1582 $mods = new Fieldmapper::metabib::virtual_record;
1583 $mods->doc_id(OILS_PRECAT_RECORD);
1584 $mods->title($copy->dummy_title);
1585 $mods->author($copy->dummy_author);
1589 $logger->debug("MODSized the circ title");
1591 return {transaction => $trans, circ => $circ, record => $mods };
1595 __PACKAGE__->register_method(
1596 method => "hold_request_count",
1597 api_name => "open-ils.actor.user.hold_requests.count",
1599 notes => <<" NOTES");
1600 Returns hold ready/total counts
1602 sub hold_request_count {
1603 my( $self, $client, $login_session, $userid ) = @_;
1605 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1606 $login_session, $userid, 'VIEW_HOLD' );
1607 return $evt if $evt;
1610 my $holds = $apputils->simple_scalar_request(
1612 "open-ils.cstore.direct.action.hold_request.search.atomic",
1615 fulfillment_time => {"=" => undef },
1616 cancel_time => undef,
1621 for my $h (@$holds) {
1622 next unless $h->capture_time and $h->current_copy;
1624 my $copy = $apputils->simple_scalar_request(
1626 "open-ils.cstore.direct.asset.copy.retrieve",
1630 if ($copy and $copy->status == 8) {
1635 return { total => scalar(@$holds), ready => scalar(@ready) };
1639 __PACKAGE__->register_method(
1640 method => "checkedout_count",
1641 api_name => "open-ils.actor.user.checked_out.count__",
1643 notes => <<" NOTES");
1644 Returns a transaction record
1648 sub checkedout_count {
1649 my( $self, $client, $login_session, $userid ) = @_;
1651 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1652 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1653 return $evt if $evt;
1655 my $circs = $apputils->simple_scalar_request(
1657 "open-ils.cstore.direct.action.circulation.search.atomic",
1658 { usr => $userid, stop_fines => undef }
1659 #{ usr => $userid, checkin_time => {"=" => undef } }
1662 my $parser = DateTime::Format::ISO8601->new;
1665 for my $c (@$circs) {
1666 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1667 my $due = $due_dt->epoch;
1669 if ($due < DateTime->today->epoch) {
1674 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1678 __PACKAGE__->register_method(
1679 method => "checked_out",
1680 api_name => "open-ils.actor.user.checked_out",
1683 Returns a structure of circulations objects sorted by
1684 out, overdue, lost, claims_returned, long_overdue.
1685 A list of IDs are returned of each type.
1686 lost, long_overdue, and claims_returned circ will not
1687 be "finished" (there is an outstanding balance or some
1688 other pending action on the circ).
1690 The .count method also includes a 'total' field which
1691 sums all "open" circs
1695 __PACKAGE__->register_method(
1696 method => "checked_out",
1697 api_name => "open-ils.actor.user.checked_out.count",
1699 signature => q/@see open-ils.actor.user.checked_out/
1703 my( $self, $conn, $auth, $userid ) = @_;
1705 my $e = new_editor(authtoken=>$auth);
1706 return $e->event unless $e->checkauth;
1708 if( $userid ne $e->requestor->id ) {
1709 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1712 my $count = $self->api_name =~ /count/;
1713 return _checked_out( $count, $e, $userid );
1717 my( $iscount, $e, $userid ) = @_;
1720 my $meth = 'open-ils.storage.actor.user.checked_out';
1721 $meth = "$meth.count" if $iscount;
1722 return $U->storagereq($meth, $userid);
1724 # XXX Old code - moved to storage
1725 #------------------------------------------------------------------------------
1726 #------------------------------------------------------------------------------
1727 my $circs = $e->search_action_circulation(
1728 { usr => $userid, checkin_time => undef });
1730 my $parser = DateTime::Format::ISO8601->new;
1732 # split the circs up into overdue and not-overdue circs
1734 for my $c (@$circs) {
1735 if( $c->due_date ) {
1736 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1737 my $due = $due_dt->epoch;
1738 if ($due < DateTime->today->epoch) {
1748 my( @open, @od, @lost, @cr, @lo );
1750 while (my $c = shift(@out)) {
1751 push( @open, $c->id ) if (!$c->stop_fines || $c->stop_fines eq 'MAXFINES' || $c->stop_fines eq 'RENEW');
1752 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1753 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1754 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1757 while (my $c = shift(@overdue)) {
1758 push( @od, $c->id ) if (!$c->stop_fines || $c->stop_fines eq 'MAXFINES' || $c->stop_fines eq 'RENEW');
1759 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1760 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1761 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1766 total => @open + @od + @lost + @cr + @lo,
1767 out => scalar(@open),
1768 overdue => scalar(@od),
1769 lost => scalar(@lost),
1770 claims_returned => scalar(@cr),
1771 long_overdue => scalar(@lo)
1779 claims_returned => \@cr,
1780 long_overdue => \@lo
1785 sub _checked_out_WHAT {
1786 my( $iscount, $e, $userid ) = @_;
1788 my $circs = $e->search_action_circulation(
1789 { usr => $userid, stop_fines => undef });
1791 my $mcircs = $e->search_action_circulation(
1794 checkin_time => undef,
1795 xact_finish => undef,
1799 push( @$circs, @$mcircs );
1801 my $parser = DateTime::Format::ISO8601->new;
1803 # split the circs up into overdue and not-overdue circs
1805 for my $c (@$circs) {
1806 if( $c->due_date ) {
1807 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1808 my $due = $due_dt->epoch;
1809 if ($due < DateTime->today->epoch) {
1810 push @overdue, $c->id;
1819 # grab all of the lost, claims-returned, and longoverdue circs
1820 #my $open = $e->search_action_circulation(
1821 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1824 # these items have stop_fines, but no xact_finish, so money
1825 # is owed on them and they have not been checked in
1826 my $open = $e->search_action_circulation(
1829 stop_fines => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] },
1830 xact_finish => undef,
1831 checkin_time => undef,
1836 my( @lost, @cr, @lo );
1837 for my $c (@$open) {
1838 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1839 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1840 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1846 total => @$circs + @lost + @cr + @lo,
1847 out => scalar(@out),
1848 overdue => scalar(@overdue),
1849 lost => scalar(@lost),
1850 claims_returned => scalar(@cr),
1851 long_overdue => scalar(@lo)
1857 overdue => \@overdue,
1859 claims_returned => \@cr,
1860 long_overdue => \@lo
1866 __PACKAGE__->register_method(
1867 method => "checked_in_with_fines",
1868 api_name => "open-ils.actor.user.checked_in_with_fines",
1870 signature => q/@see open-ils.actor.user.checked_out/
1872 sub checked_in_with_fines {
1873 my( $self, $conn, $auth, $userid ) = @_;
1875 my $e = new_editor(authtoken=>$auth);
1876 return $e->event unless $e->checkauth;
1878 if( $userid ne $e->requestor->id ) {
1879 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1882 # money is owed on these items and they are checked in
1883 my $open = $e->search_action_circulation(
1886 xact_finish => undef,
1887 checkin_time => { "!=" => undef },
1892 my( @lost, @cr, @lo );
1893 for my $c (@$open) {
1894 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1895 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1896 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1901 claims_returned => \@cr,
1902 long_overdue => \@lo
1914 __PACKAGE__->register_method(
1915 method => "user_transaction_history",
1916 api_name => "open-ils.actor.user.transactions.history",
1918 notes => <<" NOTES");
1919 Returns a list of billable transaction ids for a user, optionally by type
1921 __PACKAGE__->register_method(
1922 method => "user_transaction_history",
1923 api_name => "open-ils.actor.user.transactions.history.have_charge",
1925 notes => <<" NOTES");
1926 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1928 __PACKAGE__->register_method(
1929 method => "user_transaction_history",
1930 api_name => "open-ils.actor.user.transactions.history.have_balance",
1932 notes => <<" NOTES");
1933 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1935 __PACKAGE__->register_method(
1936 method => "user_transaction_history",
1937 api_name => "open-ils.actor.user.transactions.history.still_open",
1939 notes => <<" NOTES");
1940 Returns a list of billable transaction ids for a user that are not finished
1942 __PACKAGE__->register_method(
1943 method => "user_transaction_history",
1944 api_name => "open-ils.actor.user.transactions.history.have_bill",
1946 notes => <<" NOTES");
1947 Returns a list of billable transaction ids for a user that has billings
1953 sub _user_transaction_history {
1954 my( $self, $client, $login_session, $user_id, $type ) = @_;
1956 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1957 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1958 return $evt if $evt;
1960 my $api = $self->api_name();
1965 @xact = (xact_type => $type) if(defined($type));
1966 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1967 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1969 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1971 my $trans = $apputils->simple_scalar_request(
1973 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1974 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1976 return [ map { $_->id } @$trans ];
1980 =head SEE APPUTILS.PM
1985 for my $x (@xacts) {
1986 my $s = new Fieldmapper::money::billable_transaction_summary;
1989 $s->xact_start( $x->xact_start );
1990 $s->xact_finish( $x->xact_finish );
1994 for my $b (@{ $x->billings }) {
1995 next if ($U->is_true($b->voided));
1996 $to += ($b->amount * 100);
1997 $lb ||= $b->billing_ts;
1998 if ($b->billing_ts ge $lb) {
1999 $lb = $b->billing_ts;
2000 $s->last_billing_note($b->note);
2001 $s->last_billing_ts($b->billing_ts);
2002 $s->last_billing_type($b->billing_type);
2006 $s->total_owed( sprintf('%0.2f', $to / 100 ) );
2010 for my $p (@{ $x->payments }) {
2011 next if ($U->is_true($p->voided));
2012 $tp += ($p->amount * 100);
2013 $lp ||= $p->payment_ts;
2014 if ($p->payment_ts ge $lp) {
2015 $lp = $p->payment_ts;
2016 $s->last_payment_note($p->note);
2017 $s->last_payment_ts($p->payment_ts);
2018 $s->last_payment_type($p->payment_type);
2021 $s->total_paid( sprintf('%0.2f', $tp / 100 ) );
2023 $s->balance_owed( sprintf('%0.2f', ($to - $tp) / 100) );
2025 $s->xact_type( 'grocery' ) if ($x->grocery);
2026 $s->xact_type( 'circulation' ) if ($x->circulation);
2035 sub user_transaction_history {
2036 my( $self, $conn, $auth, $userid, $type ) = @_;
2038 # run inside of a transaction to prevent replication delays
2039 my $e = new_editor(xact=>1, authtoken=>$auth);
2040 return $e->die_event unless $e->checkauth;
2041 return $e->die_event unless $e->allowed('VIEW_USER_TRANSACTIONS');
2043 my $api = $self->api_name;
2044 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
2046 my @xacts = @{ $e->search_money_billable_transaction(
2047 [ { usr => $userid, @xact_finish },
2049 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
2050 order_by => { mbt => 'xact_start DESC' },
2057 #my @mbts = _make_mbts( @xacts );
2058 my @mbts = $U->make_mbts( @xacts );
2060 if(defined($type)) {
2061 @mbts = grep { $_->xact_type eq $type } @mbts;
2064 if($api =~ /have_balance/o) {
2065 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
2068 if($api =~ /have_charge/o) {
2069 @mbts = grep { defined($_->last_billing_ts) } @mbts;
2072 if($api =~ /have_bill/o) {
2073 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
2081 __PACKAGE__->register_method(
2082 method => "user_perms",
2083 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2085 notes => <<" NOTES");
2086 Returns a list of permissions
2089 my( $self, $client, $authtoken, $user ) = @_;
2091 my( $staff, $evt ) = $apputils->checkses($authtoken);
2092 return $evt if $evt;
2094 $user ||= $staff->id;
2096 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2100 return $apputils->simple_scalar_request(
2102 "open-ils.storage.permission.user_perms.atomic",
2106 __PACKAGE__->register_method(
2107 method => "retrieve_perms",
2108 api_name => "open-ils.actor.permissions.retrieve",
2109 notes => <<" NOTES");
2110 Returns a list of permissions
2112 sub retrieve_perms {
2113 my( $self, $client ) = @_;
2114 return $apputils->simple_scalar_request(
2116 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2117 { id => { '!=' => undef } }
2121 __PACKAGE__->register_method(
2122 method => "retrieve_groups",
2123 api_name => "open-ils.actor.groups.retrieve",
2124 notes => <<" NOTES");
2125 Returns a list of user groupss
2127 sub retrieve_groups {
2128 my( $self, $client ) = @_;
2129 return new_editor()->retrieve_all_permission_grp_tree();
2132 __PACKAGE__->register_method(
2133 method => "retrieve_org_address",
2134 api_name => "open-ils.actor.org_unit.address.retrieve",
2135 notes => <<' NOTES');
2136 Returns an org_unit address by ID
2137 @param An org_address ID
2139 sub retrieve_org_address {
2140 my( $self, $client, $id ) = @_;
2141 return $apputils->simple_scalar_request(
2143 "open-ils.cstore.direct.actor.org_address.retrieve",
2148 __PACKAGE__->register_method(
2149 method => "retrieve_groups_tree",
2150 api_name => "open-ils.actor.groups.tree.retrieve",
2151 notes => <<" NOTES");
2152 Returns a list of user groups
2154 sub retrieve_groups_tree {
2155 my( $self, $client ) = @_;
2156 return new_editor()->search_permission_grp_tree(
2161 flesh_fields => { pgt => ["children"] },
2162 order_by => { pgt => 'name'}
2169 # turns an org list into an org tree
2171 sub build_group_tree {
2173 my( $self, $grplist) = @_;
2175 return $grplist unless (
2176 ref($grplist) and @$grplist > 1 );
2178 my @list = sort { $a->name cmp $b->name } @$grplist;
2181 for my $grp (@list) {
2183 if ($grp and !defined($grp->parent)) {
2187 my ($parent) = grep { $_->id == $grp->parent} @list;
2189 $parent->children([]) unless defined($parent->children);
2190 push( @{$parent->children}, $grp );
2198 __PACKAGE__->register_method(
2199 method => "add_user_to_groups",
2200 api_name => "open-ils.actor.user.set_groups",
2201 notes => <<" NOTES");
2202 Adds a user to one or more permission groups
2205 sub add_user_to_groups {
2206 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2208 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2209 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2210 return $evt if $evt;
2212 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2213 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2214 return $evt if $evt;
2216 $apputils->simplereq(
2218 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2220 for my $group (@$groups) {
2221 my $link = Fieldmapper::permission::usr_grp_map->new;
2223 $link->usr($userid);
2225 my $id = $apputils->simplereq(
2227 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2233 __PACKAGE__->register_method(
2234 method => "get_user_perm_groups",
2235 api_name => "open-ils.actor.user.get_groups",
2236 notes => <<" NOTES");
2237 Retrieve a user's permission groups.
2241 sub get_user_perm_groups {
2242 my( $self, $client, $authtoken, $userid ) = @_;
2244 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2245 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2246 return $evt if $evt;
2248 return $apputils->simplereq(
2250 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2255 __PACKAGE__->register_method (
2256 method => 'register_workstation',
2257 api_name => 'open-ils.actor.workstation.register.override',
2258 signature => q/@see open-ils.actor.workstation.register/);
2260 __PACKAGE__->register_method (
2261 method => 'register_workstation',
2262 api_name => 'open-ils.actor.workstation.register',
2264 Registers a new workstion in the system
2265 @param authtoken The login session key
2266 @param name The name of the workstation id
2267 @param owner The org unit that owns this workstation
2268 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2269 if the name is already in use.
2272 sub _register_workstation {
2273 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2274 my( $requestor, $evt ) = $U->checkses($authtoken);
2275 return $evt if $evt;
2276 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2277 return $evt if $evt;
2279 my $ws = $U->cstorereq(
2280 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2281 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2283 $ws = Fieldmapper::actor::workstation->new;
2284 $ws->owning_lib($owner);
2287 my $id = $U->storagereq(
2288 'open-ils.storage.direct.actor.workstation.create', $ws );
2289 return $U->DB_UPDATE_FAILED($ws) unless $id;
2295 sub register_workstation {
2296 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2298 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2299 return $e->event unless $e->checkauth;
2300 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2301 my $existing = $e->search_actor_workstation({name => $name});
2304 if( $self->api_name =~ /override/o ) {
2305 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2306 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2308 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2312 my $ws = Fieldmapper::actor::workstation->new;
2313 $ws->owning_lib($owner);
2315 $e->create_actor_workstation($ws) or return $e->event;
2317 return $ws->id; # note: editor sets the id on the new object for us
2321 __PACKAGE__->register_method (
2322 method => 'fetch_patron_note',
2323 api_name => 'open-ils.actor.note.retrieve.all',
2325 Returns a list of notes for a given user
2326 Requestor must have VIEW_USER permission if pub==false and
2327 @param authtoken The login session key
2328 @param args Hash of params including
2329 patronid : the patron's id
2330 pub : true if retrieving only public notes
2334 sub fetch_patron_note {
2335 my( $self, $conn, $authtoken, $args ) = @_;
2336 my $patronid = $$args{patronid};
2338 my($reqr, $evt) = $U->checkses($authtoken);
2339 return $evt if $evt;
2342 ($patron, $evt) = $U->fetch_user($patronid);
2343 return $evt if $evt;
2346 if( $patronid ne $reqr->id ) {
2347 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2348 return $evt if $evt;
2350 return $U->cstorereq(
2351 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2352 { usr => $patronid, pub => 't' } );
2355 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2356 return $evt if $evt;
2358 return $U->cstorereq(
2359 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2362 __PACKAGE__->register_method (
2363 method => 'create_user_note',
2364 api_name => 'open-ils.actor.note.create',
2366 Creates a new note for the given user
2367 @param authtoken The login session key
2368 @param note The note object
2371 sub create_user_note {
2372 my( $self, $conn, $authtoken, $note ) = @_;
2373 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2374 return $e->die_event unless $e->checkauth;
2376 my $user = $e->retrieve_actor_user($note->usr)
2377 or return $e->die_event;
2379 return $e->die_event unless
2380 $e->allowed('UPDATE_USER',$user->home_ou);
2382 $note->creator($e->requestor->id);
2383 $e->create_actor_usr_note($note) or return $e->die_event;
2389 __PACKAGE__->register_method (
2390 method => 'delete_user_note',
2391 api_name => 'open-ils.actor.note.delete',
2393 Deletes a note for the given user
2394 @param authtoken The login session key
2395 @param noteid The note id
2398 sub delete_user_note {
2399 my( $self, $conn, $authtoken, $noteid ) = @_;
2401 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2402 return $e->die_event unless $e->checkauth;
2403 my $note = $e->retrieve_actor_usr_note($noteid)
2404 or return $e->die_event;
2405 my $user = $e->retrieve_actor_user($note->usr)
2406 or return $e->die_event;
2407 return $e->die_event unless
2408 $e->allowed('UPDATE_USER', $user->home_ou);
2410 $e->delete_actor_usr_note($note) or return $e->die_event;
2416 __PACKAGE__->register_method (
2417 method => 'update_user_note',
2418 api_name => 'open-ils.actor.note.update',
2420 @param authtoken The login session key
2421 @param note The note
2425 sub update_user_note {
2426 my( $self, $conn, $auth, $note ) = @_;
2427 my $e = new_editor(authtoken=>$auth, xact=>1);
2428 return $e->event unless $e->checkauth;
2429 my $patron = $e->retrieve_actor_user($note->usr)
2430 or return $e->event;
2431 return $e->event unless
2432 $e->allowed('UPDATE_USER', $patron->home_ou);
2433 $e->update_actor_user_note($note)
2434 or return $e->event;
2442 __PACKAGE__->register_method (
2443 method => 'create_closed_date',
2444 api_name => 'open-ils.actor.org_unit.closed_date.create',
2446 Creates a new closing entry for the given org_unit
2447 @param authtoken The login session key
2448 @param note The closed_date object
2451 sub create_closed_date {
2452 my( $self, $conn, $authtoken, $cd ) = @_;
2454 my( $user, $evt ) = $U->checkses($authtoken);
2455 return $evt if $evt;
2457 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2458 return $evt if $evt;
2460 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2462 my $id = $U->storagereq(
2463 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2464 return $U->DB_UPDATE_FAILED($cd) unless $id;
2469 __PACKAGE__->register_method (
2470 method => 'delete_closed_date',
2471 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2473 Deletes a closing entry for the given org_unit
2474 @param authtoken The login session key
2475 @param noteid The close_date id
2478 sub delete_closed_date {
2479 my( $self, $conn, $authtoken, $cd ) = @_;
2481 my( $user, $evt ) = $U->checkses($authtoken);
2482 return $evt if $evt;
2485 ($cd_obj, $evt) = fetch_closed_date($cd);
2486 return $evt if $evt;
2488 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2489 return $evt if $evt;
2491 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2493 my $stat = $U->storagereq(
2494 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2495 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2500 __PACKAGE__->register_method(
2501 method => 'usrname_exists',
2502 api_name => 'open-ils.actor.username.exists',
2504 Returns 1 if the requested username exists, returns 0 otherwise
2508 sub usrname_exists {
2509 my( $self, $conn, $auth, $usrname ) = @_;
2510 my $e = new_editor(authtoken=>$auth);
2511 return $e->event unless $e->checkauth;
2512 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2513 return $$a[0] if $a and @$a;
2517 __PACKAGE__->register_method(
2518 method => 'barcode_exists',
2519 api_name => 'open-ils.actor.barcode.exists',
2521 Returns 1 if the requested barcode exists, returns 0 otherwise
2525 sub barcode_exists {
2526 my( $self, $conn, $auth, $barcode ) = @_;
2527 my $e = new_editor(authtoken=>$auth);
2528 return $e->event unless $e->checkauth;
2529 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2530 return $$a[0] if $a and @$a;
2535 __PACKAGE__->register_method(
2536 method => 'retrieve_net_levels',
2537 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2540 sub retrieve_net_levels {
2541 my( $self, $conn, $auth ) = @_;
2542 my $e = new_editor(authtoken=>$auth);
2543 return $e->event unless $e->checkauth;
2544 return $e->retrieve_all_config_net_access_level();
2548 __PACKAGE__->register_method(
2549 method => 'fetch_org_by_shortname',
2550 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2552 sub fetch_org_by_shortname {
2553 my( $self, $conn, $sname ) = @_;
2554 my $e = new_editor();
2555 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2556 return $e->event unless $org;
2561 __PACKAGE__->register_method(
2562 method => 'session_home_lib',
2563 api_name => 'open-ils.actor.session.home_lib',
2566 sub session_home_lib {
2567 my( $self, $conn, $auth ) = @_;
2568 my $e = new_editor(authtoken=>$auth);
2569 return undef unless $e->checkauth;
2570 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2571 return $org->shortname;
2576 __PACKAGE__->register_method(
2577 method => 'slim_tree',
2578 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2581 my $tree = new_editor()->search_actor_org_unit(
2583 {"parent_ou" => undef },
2586 flesh_fields => { aou => ['children'] },
2587 order_by => { aou => 'name'},
2588 select => { aou => ["id","shortname", "name"]},
2593 return trim_tree($tree);
2599 return undef unless $tree;
2601 code => $tree->shortname,
2602 name => $tree->name,
2604 if( $tree->children and @{$tree->children} ) {
2605 $htree->{children} = [];
2606 for my $c (@{$tree->children}) {
2607 push( @{$htree->{children}}, trim_tree($c) );
2615 __PACKAGE__->register_method(
2616 method => "update_penalties",
2617 api_name => "open-ils.actor.user.penalties.update");
2618 sub update_penalties {
2619 my( $self, $conn, $auth, $userid ) = @_;
2620 my $e = new_editor(authtoken=>$auth);
2621 return $e->event unless $e->checkauth;
2622 $U->update_patron_penalties(
2624 patronid => $userid,
2631 __PACKAGE__->register_method(
2632 method => "user_retrieve_fleshed_by_id",
2633 api_name => "open-ils.actor.user.fleshed.retrieve",);
2635 sub user_retrieve_fleshed_by_id {
2636 my( $self, $client, $auth, $user_id, $fields ) = @_;
2637 my $e = new_editor(authtoken => $auth);
2638 return $e->event unless $e->checkauth;
2640 if( $e->requestor->id != $user_id ) {
2641 return $e->event unless $e->allowed('VIEW_USER');
2647 "standing_penalties",
2651 "stat_cat_entries" ];
2652 return new_flesh_user($user_id, $fields, $e);
2656 sub new_flesh_user {
2659 my $fields = shift || [];
2660 my $e = shift || new_editor(xact=>1);
2662 my $user = $e->retrieve_actor_user(
2667 "flesh_fields" => { "au" => $fields }
2670 ) or return $e->event;
2673 if( grep { $_ eq 'addresses' } @$fields ) {
2675 $user->addresses([]) unless @{$user->addresses};
2677 if( ref $user->billing_address ) {
2678 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2679 push( @{$user->addresses}, $user->billing_address );
2683 if( ref $user->mailing_address ) {
2684 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2685 push( @{$user->addresses}, $user->mailing_address );
2691 $user->clear_passwd();
2698 __PACKAGE__->register_method(
2699 method => "user_retrieve_parts",
2700 api_name => "open-ils.actor.user.retrieve.parts",);
2702 sub user_retrieve_parts {
2703 my( $self, $client, $auth, $user_id, $fields ) = @_;
2704 my $e = new_editor(authtoken => $auth);
2705 return $e->event unless $e->checkauth;
2706 if( $e->requestor->id != $user_id ) {
2707 return $e->event unless $e->allowed('VIEW_USER');
2710 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2711 push(@resp, $user->$_()) for(@$fields);