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, $setting ) = @_;
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 my $settings = { map { ( $_->name => JSON->JSON2perl($_->value) ) } @$s };
133 return $$settings{$setting} if $setting;
139 __PACKAGE__->register_method(
140 method => "ou_settings",
141 api_name => "open-ils.actor.org_unit.settings.retrieve",
144 my( $self, $client, $ouid ) = @_;
146 $logger->info("Fetching org unit settings for org $ouid");
148 my $s = $apputils->simplereq(
150 'open-ils.cstore.direct.actor.org_unit_setting.search.atomic', {org_unit => $ouid});
152 return { map { ( $_->name => JSON->JSON2perl($_->value) ) } @$s };
155 __PACKAGE__->register_method (
156 method => "ou_setting_delete",
157 api_name => 'open-ils.actor.org_setting.delete',
159 Deletes a specific org unit setting for a specific location
160 @param authtoken The login session key
161 @param orgid The org unit whose setting we're changing
162 @param setting The name of the setting to delete
163 @return True value on success.
167 sub ou_setting_delete {
168 my( $self, $conn, $authtoken, $orgid, $setting ) = @_;
169 my( $reqr, $evt) = $U->checkses($authtoken);
171 $evt = $U->check_perms($reqr->id, $orgid, 'UPDATE_ORG_SETTING');
174 my $id = $U->cstorereq(
175 'open-ils.cstore.direct.actor.org_unit_setting.id_list',
176 { name => $setting, org_unit => $orgid } );
178 $logger->debug("Retrieved setting $id in org unit setting delete");
180 my $s = $U->cstorereq(
181 'open-ils.cstore.direct.actor.org_unit_setting.delete', $id );
183 $logger->activity("User ".$reqr->id." deleted org unit setting $id") if $s;
189 __PACKAGE__->register_method(
190 method => "update_patron",
191 api_name => "open-ils.actor.patron.update",);
194 my( $self, $client, $user_session, $patron ) = @_;
196 my $session = $apputils->start_db_session();
200 $logger->info("Creating new patron...") if $patron->isnew;
201 $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
203 my( $user_obj, $evt ) = $U->checkses($user_session);
206 $evt = check_group_perm($session, $user_obj, $patron);
210 # XXX does this user have permission to add/create users. Granularity?
211 # $new_patron is the patron in progress. $patron is the original patron
212 # passed in with the method. new_patron will change as the components
213 # of patron are added/updated.
217 # unflesh the real items on the patron
218 $patron->card( $patron->card->id ) if(ref($patron->card));
219 $patron->billing_address( $patron->billing_address->id )
220 if(ref($patron->billing_address));
221 $patron->mailing_address( $patron->mailing_address->id )
222 if(ref($patron->mailing_address));
224 # create/update the patron first so we can use his id
225 if($patron->isnew()) {
226 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
228 } else { $new_patron = $patron; }
230 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
233 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
236 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
239 # re-update the patron if anything has happened to him during this process
240 if($new_patron->ischanged()) {
241 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
245 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
248 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
251 ($new_patron, $evt) = _create_standing_penalties($session, $user_session, $patron, $new_patron, $user_obj);
254 $logger->activity("user ".$user_obj->id." updating/creating user ".$new_patron->id);
257 if(!$patron->isnew) {
258 $opatron = new_editor()->retrieve_actor_user($new_patron->id);
261 $apputils->commit_db_session($session);
262 my $fuser = flesh_user($new_patron->id());
265 # Log the new and old patron for investigation
266 $logger->info("$user_session updating patron object. orig patron object = ".
267 JSON->perl2JSON($opatron). " |||| new patron = ".JSON->perl2JSON($fuser));
277 return new_flesh_user($id, [
280 "standing_penalties",
284 "stat_cat_entries" ] );
292 # clone and clear stuff that would break the database
296 my $new_patron = $patron->clone;
298 $new_patron->clear_billing_address();
299 $new_patron->clear_mailing_address();
300 $new_patron->clear_addresses();
301 $new_patron->clear_card();
302 $new_patron->clear_cards();
303 $new_patron->clear_id();
304 $new_patron->clear_isnew();
305 $new_patron->clear_ischanged();
306 $new_patron->clear_isdeleted();
307 $new_patron->clear_stat_cat_entries();
308 $new_patron->clear_permissions();
309 $new_patron->clear_standing_penalties();
319 my $user_obj = shift;
321 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
322 return (undef, $evt) if $evt;
324 my $ex = $session->request(
325 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
327 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
330 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
332 my $id = $session->request(
333 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
334 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
336 $logger->info("Successfully created new user [$id] in DB");
338 return ( $session->request(
339 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
343 sub check_group_perm {
344 my( $session, $requestor, $patron ) = @_;
347 # first let's see if the requestor has
348 # priveleges to update this user in any way
349 if( ! $patron->isnew ) {
350 my $p = $session->request(
351 'open-ils.storage.direct.actor.user.retrieve', $patron->id )->gather(1);
353 # If we are the requestor (trying to update our own account)
354 # and we are not trying to change our profile, we're good
355 if( $p->id == $requestor->id and
356 $p->profile == $patron->profile ) {
361 $evt = group_perm_failed($session, $requestor, $p);
365 # They are allowed to edit this patron.. can they put the
366 # patron into the group requested?
367 $evt = group_perm_failed($session, $requestor, $patron);
373 sub group_perm_failed {
374 my( $session, $requestor, $patron ) = @_;
378 my $grpid = $patron->profile;
382 $logger->debug("user update looking for group perm for group $grpid");
383 $grp = $session->request(
384 'open-ils.storage.direct.permission.grp_tree.retrieve', $grpid )->gather(1);
385 return OpenILS::Event->new('PERMISSION_GRP_TREE_NOT_FOUND') unless $grp;
387 } while( !($perm = $grp->application_perm) and ($grpid = $grp->parent) );
389 $logger->info("user update checking perm $perm on user ".
390 $requestor->id." for update/create on user username=".$patron->usrname);
392 my $evt = $U->check_perms($requestor->id, $patron->home_ou, $perm);
400 my( $session, $patron, $user_obj, $noperm) = @_;
402 $logger->info("Updating patron ".$patron->id." in DB");
407 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
408 return (undef, $evt) if $evt;
411 # update the password by itself to avoid the password protection magic
412 if( $patron->passwd ) {
413 my $s = $session->request(
414 'open-ils.storage.direct.actor.user.remote_update',
415 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
416 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
417 $patron->clear_passwd;
420 if(!$patron->ident_type) {
421 $patron->clear_ident_type;
422 $patron->clear_ident_value;
425 my $stat = $session->request(
426 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
427 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
432 sub _check_dup_ident {
433 my( $session, $patron ) = @_;
435 return undef unless $patron->ident_value;
438 ident_type => $patron->ident_type,
439 ident_value => $patron->ident_value,
442 $logger->debug("patron update searching for dup ident values: " .
443 $patron->ident_type . ':' . $patron->ident_value);
445 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
447 my $dups = $session->request(
448 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
451 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
458 sub _add_update_addresses {
462 my $new_patron = shift;
466 my $current_id; # id of the address before creation
468 for my $address (@{$patron->addresses()}) {
470 next unless ref $address;
471 $current_id = $address->id();
473 if( $patron->billing_address() and
474 $patron->billing_address() == $current_id ) {
475 $logger->info("setting billing addr to $current_id");
476 $new_patron->billing_address($address->id());
477 $new_patron->ischanged(1);
480 if( $patron->mailing_address() and
481 $patron->mailing_address() == $current_id ) {
482 $new_patron->mailing_address($address->id());
483 $logger->info("setting mailing addr to $current_id");
484 $new_patron->ischanged(1);
488 if($address->isnew()) {
490 $address->usr($new_patron->id());
492 ($address, $evt) = _add_address($session,$address);
493 return (undef, $evt) if $evt;
495 # we need to get the new id
496 if( $patron->billing_address() and
497 $patron->billing_address() == $current_id ) {
498 $new_patron->billing_address($address->id());
499 $logger->info("setting billing addr to $current_id");
500 $new_patron->ischanged(1);
503 if( $patron->mailing_address() and
504 $patron->mailing_address() == $current_id ) {
505 $new_patron->mailing_address($address->id());
506 $logger->info("setting mailing addr to $current_id");
507 $new_patron->ischanged(1);
510 } elsif($address->ischanged() ) {
512 ($address, $evt) = _update_address($session, $address);
513 return (undef, $evt) if $evt;
515 } elsif($address->isdeleted() ) {
517 if( $address->id() == $new_patron->mailing_address() ) {
518 $new_patron->clear_mailing_address();
519 ($new_patron, $evt) = _update_patron($session, $new_patron);
520 return (undef, $evt) if $evt;
523 if( $address->id() == $new_patron->billing_address() ) {
524 $new_patron->clear_billing_address();
525 ($new_patron, $evt) = _update_patron($session, $new_patron);
526 return (undef, $evt) if $evt;
529 $evt = _delete_address($session, $address);
530 return (undef, $evt) if $evt;
534 return ( $new_patron, undef );
538 # adds an address to the db and returns the address with new id
540 my($session, $address) = @_;
541 $address->clear_id();
543 $logger->info("Creating new address at street ".$address->street1);
545 # put the address into the database
546 my $id = $session->request(
547 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
548 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
551 return ($address, undef);
555 sub _update_address {
556 my( $session, $address ) = @_;
558 $logger->info("Updating address ".$address->id." in the DB");
560 my $stat = $session->request(
561 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
563 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
564 return ($address, undef);
569 sub _add_update_cards {
573 my $new_patron = shift;
577 my $virtual_id; #id of the card before creation
578 for my $card (@{$patron->cards()}) {
580 $card->usr($new_patron->id());
582 if(ref($card) and $card->isnew()) {
584 $virtual_id = $card->id();
585 ( $card, $evt ) = _add_card($session,$card);
586 return (undef, $evt) if $evt;
588 #if(ref($patron->card)) { $patron->card($patron->card->id); }
589 if($patron->card() == $virtual_id) {
590 $new_patron->card($card->id());
591 $new_patron->ischanged(1);
594 } elsif( ref($card) and $card->ischanged() ) {
595 $evt = _update_card($session, $card);
596 return (undef, $evt) if $evt;
600 return ( $new_patron, undef );
604 # adds an card to the db and returns the card with new id
606 my( $session, $card ) = @_;
609 $logger->info("Adding new patron card ".$card->barcode);
611 my $id = $session->request(
612 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
613 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
614 $logger->info("Successfully created patron card $id");
617 return ( $card, undef );
621 # returns event on error. returns undef otherwise
623 my( $session, $card ) = @_;
624 $logger->info("Updating patron card ".$card->id);
626 my $stat = $session->request(
627 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
628 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
635 # returns event on error. returns undef otherwise
636 sub _delete_address {
637 my( $session, $address ) = @_;
639 $logger->info("Deleting address ".$address->id." from DB");
641 my $stat = $session->request(
642 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
644 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
650 sub _add_survey_responses {
651 my ($session, $patron, $new_patron) = @_;
653 $logger->info( "Updating survey responses for patron ".$new_patron->id );
655 my $responses = $patron->survey_responses;
659 $_->usr($new_patron->id) for (@$responses);
661 my $evt = $U->simplereq( "open-ils.circ",
662 "open-ils.circ.survey.submit.user_id", $responses );
664 return (undef, $evt) if defined($U->event_code($evt));
668 return ( $new_patron, undef );
672 sub _create_stat_maps {
674 my($session, $user_session, $patron, $new_patron) = @_;
676 my $maps = $patron->stat_cat_entries();
678 for my $map (@$maps) {
680 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
682 if ($map->isdeleted()) {
683 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
685 } elsif ($map->isnew()) {
686 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
691 $map->target_usr($new_patron->id);
694 $logger->info("Updating stat entry with method $method and map $map");
696 my $stat = $session->request($method, $map)->gather(1);
697 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
701 return ($new_patron, undef);
704 sub _create_perm_maps {
706 my($session, $user_session, $patron, $new_patron) = @_;
708 my $maps = $patron->permissions;
710 for my $map (@$maps) {
712 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
713 if ($map->isdeleted()) {
714 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
715 } elsif ($map->isnew()) {
716 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
721 $map->usr($new_patron->id);
723 #warn( "Updating permissions with method $method and session $user_session and map $map" );
724 $logger->info( "Updating permissions with method $method and map $map" );
726 my $stat = $session->request($method, $map)->gather(1);
727 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
731 return ($new_patron, undef);
735 __PACKAGE__->register_method(
736 method => "set_user_perms",
737 api_name => "open-ils.actor.user.permissions.update",
746 my $session = $apputils->start_db_session();
748 my( $user_obj, $evt ) = $U->checkses($ses);
751 my $perms = $session->request('open-ils.storage.permission.user_perms.atomic', $user_obj->id)->gather(1);
754 $all = 1 if ($user_obj->super_user());
755 $all = 1 unless ($U->check_perms($user_obj->id, $user_obj->home_ou, 'EVERYTHING'));
757 for my $map (@$maps) {
759 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
760 if ($map->isdeleted()) {
761 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
762 } elsif ($map->isnew()) {
763 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
767 next if (!$all || !grep { $_->perm eq $map->perm and $_->grantable == 1 and $_->depth <= $map->depth } @$perms);
769 #warn( "Updating permissions with method $method and session $ses and map $map" );
770 $logger->info( "Updating permissions with method $method and map $map" );
772 my $stat = $session->request($method, $map)->gather(1);
773 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
777 $apputils->commit_db_session($session);
779 return scalar(@$maps);
783 sub _create_standing_penalties {
785 my($session, $user_session, $patron, $new_patron) = @_;
787 my $maps = $patron->standing_penalties;
790 for my $map (@$maps) {
792 if ($map->isdeleted()) {
793 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
794 } elsif ($map->isnew()) {
795 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
801 $map->usr($new_patron->id);
803 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
805 my $stat = $session->request($method, $map)->gather(1);
806 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
809 return ($new_patron, undef);
814 __PACKAGE__->register_method(
815 method => "search_username",
816 api_name => "open-ils.actor.user.search.username",
819 sub search_username {
820 my($self, $client, $username) = @_;
821 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
823 "open-ils.cstore.direct.actor.user.search.atomic",
824 { usrname => $username }
832 __PACKAGE__->register_method(
833 method => "user_retrieve_by_barcode",
834 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
836 sub user_retrieve_by_barcode {
837 my($self, $client, $user_session, $barcode) = @_;
839 $logger->debug("Searching for user with barcode $barcode");
840 my ($user_obj, $evt) = $apputils->checkses($user_session);
843 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
845 "open-ils.cstore.direct.actor.card.search.atomic",
846 { barcode => $barcode }
849 if(!$card || !$card->[0]) {
850 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
854 my $user = flesh_user($card->usr());
856 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
859 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
866 __PACKAGE__->register_method(
867 method => "get_user_by_id",
868 api_name => "open-ils.actor.user.retrieve",);
871 my ($self, $client, $auth, $id) = @_;
872 my $e = new_editor(authtoken=>$auth);
873 return $e->event unless $e->checkauth;
874 my $user = $e->retrieve_actor_user($id)
876 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
882 __PACKAGE__->register_method(
883 method => "get_org_types",
884 api_name => "open-ils.actor.org_types.retrieve",);
888 my($self, $client) = @_;
889 return $org_types if $org_types;
890 return $org_types = new_editor()->retrieve_all_actor_org_unit_type();
895 __PACKAGE__->register_method(
896 method => "get_user_ident_types",
897 api_name => "open-ils.actor.user.ident_types.retrieve",
900 sub get_user_ident_types {
901 return $ident_types if $ident_types;
902 return $ident_types =
903 new_editor()->retrieve_all_config_identification_type();
909 __PACKAGE__->register_method(
910 method => "get_org_unit",
911 api_name => "open-ils.actor.org_unit.retrieve",
915 my( $self, $client, $user_session, $org_id ) = @_;
916 my $e = new_editor(authtoken => $user_session);
918 return $e->event unless $e->checkauth;
919 $org_id = $e->requestor->ws_ou;
921 my $o = $e->retrieve_actor_org_unit($org_id)
926 __PACKAGE__->register_method(
927 method => "search_org_unit",
928 api_name => "open-ils.actor.org_unit_list.search",
931 sub search_org_unit {
933 my( $self, $client, $field, $value ) = @_;
935 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
937 "open-ils.cstore.direct.actor.org_unit.search.atomic",
938 { $field => $value } );
946 __PACKAGE__->register_method(
947 method => "get_org_tree",
948 api_name => "open-ils.actor.org_tree.retrieve",
950 note => "Returns the entire org tree structure",
954 my( $self, $client) = @_;
956 $cache = OpenSRF::Utils::Cache->new("global", 0) unless $cache;
957 my $tree = $cache->get_cache('orgtree');
958 return $tree if $tree;
960 $tree = new_editor()->search_actor_org_unit(
962 {"parent_ou" => undef },
965 flesh_fields => { aou => ['children'] },
966 order_by => { aou => 'name'}
971 $cache->put_cache('orgtree', $tree);
976 # turns an org list into an org tree
979 my( $self, $orglist) = @_;
981 return $orglist unless (
982 ref($orglist) and @$orglist > 1 );
985 $a->ou_type <=> $b->ou_type ||
986 $a->name cmp $b->name } @$orglist;
988 for my $org (@list) {
990 next unless ($org and defined($org->parent_ou));
991 my ($parent) = grep { $_->id == $org->parent_ou } @list;
994 $parent->children([]) unless defined($parent->children);
995 push( @{$parent->children}, $org );
1003 __PACKAGE__->register_method(
1004 method => "get_org_descendants",
1005 api_name => "open-ils.actor.org_tree.descendants.retrieve"
1008 # depth is optional. org_unit is the id
1009 sub get_org_descendants {
1010 my( $self, $client, $org_unit, $depth ) = @_;
1011 my $orglist = $apputils->simple_scalar_request(
1013 "open-ils.storage.actor.org_unit.descendants.atomic",
1014 $org_unit, $depth );
1015 return $self->build_org_tree($orglist);
1019 __PACKAGE__->register_method(
1020 method => "get_org_ancestors",
1021 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
1024 # depth is optional. org_unit is the id
1025 sub get_org_ancestors {
1026 my( $self, $client, $org_unit, $depth ) = @_;
1027 my $orglist = $apputils->simple_scalar_request(
1029 "open-ils.storage.actor.org_unit.ancestors.atomic",
1030 $org_unit, $depth );
1031 return $self->build_org_tree($orglist);
1035 __PACKAGE__->register_method(
1036 method => "get_standings",
1037 api_name => "open-ils.actor.standings.retrieve"
1042 return $user_standings if $user_standings;
1043 return $user_standings =
1044 $apputils->simple_scalar_request(
1046 "open-ils.cstore.direct.config.standing.search.atomic",
1047 { id => { "!=" => undef } }
1053 __PACKAGE__->register_method(
1054 method => "get_my_org_path",
1055 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1058 sub get_my_org_path {
1059 my( $self, $client, $auth, $org_id ) = @_;
1060 my $e = new_editor(authtoken=>$auth);
1061 return $e->event unless $e->checkauth;
1062 $org_id = $e->requestor->ws_ou unless defined $org_id;
1064 return $apputils->simple_scalar_request(
1066 "open-ils.storage.actor.org_unit.full_path.atomic",
1071 __PACKAGE__->register_method(
1072 method => "patron_adv_search",
1073 api_name => "open-ils.actor.patron.search.advanced" );
1074 sub patron_adv_search {
1075 my( $self, $client, $auth, $search_hash, $search_limit, $search_sort, $include_inactive ) = @_;
1076 my $e = new_editor(authtoken=>$auth);
1077 return $e->event unless $e->checkauth;
1078 return $e->event unless $e->allowed('VIEW_USER');
1079 return $U->storagereq(
1080 "open-ils.storage.actor.user.crazy_search",
1081 $search_hash, $search_limit, $search_sort, $include_inactive);
1086 sub _verify_password {
1087 my($user_session, $password) = @_;
1088 my $user_obj = $apputils->check_user_session($user_session);
1090 #grab the user with password
1091 $user_obj = $apputils->simple_scalar_request(
1093 "open-ils.cstore.direct.actor.user.retrieve",
1096 if($user_obj->passwd eq $password) {
1104 __PACKAGE__->register_method(
1105 method => "update_password",
1106 api_name => "open-ils.actor.user.password.update");
1108 __PACKAGE__->register_method(
1109 method => "update_password",
1110 api_name => "open-ils.actor.user.username.update");
1112 __PACKAGE__->register_method(
1113 method => "update_password",
1114 api_name => "open-ils.actor.user.email.update");
1116 sub update_password {
1117 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1121 my $user_obj = $apputils->check_user_session($user_session);
1123 if($self->api_name =~ /password/o) {
1125 #make sure they know the current password
1126 if(!_verify_password($user_session, md5_hex($current_password))) {
1127 return OpenILS::Event->new('INCORRECT_PASSWORD');
1130 $logger->debug("update_password setting new password $new_value");
1131 $user_obj->passwd($new_value);
1133 } elsif($self->api_name =~ /username/o) {
1134 my $users = search_username(undef, undef, $new_value);
1135 if( $users and $users->[0] ) {
1136 return OpenILS::Event->new('USERNAME_EXISTS');
1138 $user_obj->usrname($new_value);
1140 } elsif($self->api_name =~ /email/o) {
1141 #warn "Updating email to $new_value\n";
1142 $user_obj->email($new_value);
1145 my $session = $apputils->start_db_session();
1147 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1148 return $evt if $evt;
1150 $apputils->commit_db_session($session);
1152 if($user_obj) { return 1; }
1157 __PACKAGE__->register_method(
1158 method => "check_user_perms",
1159 api_name => "open-ils.actor.user.perm.check",
1160 notes => <<" NOTES");
1161 Takes a login session, user id, an org id, and an array of perm type strings. For each
1162 perm type, if the user does *not* have the given permission it is added
1163 to a list which is returned from the method. If all permissions
1164 are allowed, an empty list is returned
1165 if the logged in user does not match 'user_id', then the logged in user must
1166 have VIEW_PERMISSION priveleges.
1169 sub check_user_perms {
1170 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1172 my( $staff, $evt ) = $apputils->checkses($login_session);
1173 return $evt if $evt;
1175 if($staff->id ne $user_id) {
1176 if( $evt = $apputils->check_perms(
1177 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1183 for my $perm (@$perm_types) {
1184 if($apputils->check_perms($user_id, $org_id, $perm)) {
1185 push @not_allowed, $perm;
1189 return \@not_allowed
1192 __PACKAGE__->register_method(
1193 method => "check_user_perms2",
1194 api_name => "open-ils.actor.user.perm.check.multi_org",
1196 Checks the permissions on a list of perms and orgs for a user
1197 @param authtoken The login session key
1198 @param user_id The id of the user to check
1199 @param orgs The array of org ids
1200 @param perms The array of permission names
1201 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1202 if the logged in user does not match 'user_id', then the logged in user must
1203 have VIEW_PERMISSION priveleges.
1206 sub check_user_perms2 {
1207 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1209 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1210 $authtoken, $user_id, 'VIEW_PERMISSION' );
1211 return $evt if $evt;
1214 for my $org (@$orgs) {
1215 for my $perm (@$perms) {
1216 if($apputils->check_perms($user_id, $org, $perm)) {
1217 push @not_allowed, [ $org, $perm ];
1222 return \@not_allowed
1226 __PACKAGE__->register_method(
1227 method => 'check_user_perms3',
1228 api_name => 'open-ils.actor.user.perm.highest_org',
1230 Returns the highest org unit id at which a user has a given permission
1231 If the requestor does not match the target user, the requestor must have
1232 'VIEW_PERMISSION' rights at the home org unit of the target user
1233 @param authtoken The login session key
1234 @param userid The id of the user in question
1235 @param perm The permission to check
1236 @return The org unit highest in the org tree within which the user has
1237 the requested permission
1240 sub check_user_perms3 {
1241 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1243 my( $staff, $target, $org, $evt );
1245 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1246 $authtoken, $userid, 'VIEW_PERMISSION' );
1247 return $evt if $evt;
1249 my $tree = $self->get_org_tree();
1250 return _find_highest_perm_org( $perm, $userid, $target->ws_ou, $tree );
1254 sub _find_highest_perm_org {
1255 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1256 my $org = $apputils->find_org($org_tree, $start_org );
1260 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1262 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1268 __PACKAGE__->register_method(
1269 method => 'check_user_perms4',
1270 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1272 Returns the highest org unit id at which a user has a given permission
1273 If the requestor does not match the target user, the requestor must have
1274 'VIEW_PERMISSION' rights at the home org unit of the target user
1275 @param authtoken The login session key
1276 @param userid The id of the user in question
1277 @param perms An array of perm names to check
1278 @return An array of orgId's representing the org unit
1279 highest in the org tree within which the user has the requested permission
1280 The arrah of orgId's has matches the order of the perms array
1283 sub check_user_perms4 {
1284 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1286 my( $staff, $target, $org, $evt );
1288 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1289 $authtoken, $userid, 'VIEW_PERMISSION' );
1290 return $evt if $evt;
1293 return [] unless ref($perms);
1294 my $tree = $self->get_org_tree();
1296 for my $p (@$perms) {
1297 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1305 __PACKAGE__->register_method(
1306 method => "user_fines_summary",
1307 api_name => "open-ils.actor.user.fines.summary",
1308 notes => <<" NOTES");
1309 Returns a short summary of the users total open fines, excluding voided fines
1310 Params are login_session, user_id
1311 Returns a 'mous' object.
1314 sub user_fines_summary {
1315 my( $self, $client, $auth, $user_id ) = @_;
1316 my $e = new_editor(authtoken=>$auth);
1317 return $e->event unless $e->checkauth;
1318 my $user = $e->retrieve_actor_user($user_id)
1319 or return $e->event;
1321 if( $user_id ne $e->requestor->id ) {
1322 return $e->event unless
1323 $e->allowed('VIEW_USER_FINES_SUMMARY', $user->home_ou);
1326 # run this inside a transaction to prevent replication delay errors
1327 my $ses = $U->start_db_session();
1328 my $s = $ses->request(
1329 'open-ils.storage.money.open_user_summary.search', $user_id )->gather(1);
1330 $U->rollback_db_session($ses);
1337 __PACKAGE__->register_method(
1338 method => "user_transactions",
1339 api_name => "open-ils.actor.user.transactions",
1340 notes => <<" NOTES");
1341 Returns a list of open user transactions (mbts objects);
1342 Params are login_session, user_id
1343 Optional third parameter is the transactions type. defaults to all
1346 __PACKAGE__->register_method(
1347 method => "user_transactions",
1348 api_name => "open-ils.actor.user.transactions.have_charge",
1349 notes => <<" NOTES");
1350 Returns a list of all open user transactions (mbts objects) that have an initial charge
1351 Params are login_session, user_id
1352 Optional third parameter is the transactions type. defaults to all
1355 __PACKAGE__->register_method(
1356 method => "user_transactions",
1357 api_name => "open-ils.actor.user.transactions.have_balance",
1358 notes => <<" NOTES");
1359 Returns a list of all open user transactions (mbts objects) that have a balance
1360 Params are login_session, user_id
1361 Optional third parameter is the transactions type. defaults to all
1364 __PACKAGE__->register_method(
1365 method => "user_transactions",
1366 api_name => "open-ils.actor.user.transactions.fleshed",
1367 notes => <<" NOTES");
1368 Returns an object/hash of transaction, circ, title where transaction = an open
1369 user transactions (mbts objects), circ is the attached circluation, and title
1370 is the title the circ points to
1371 Params are login_session, user_id
1372 Optional third parameter is the transactions type. defaults to all
1375 __PACKAGE__->register_method(
1376 method => "user_transactions",
1377 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1378 notes => <<" NOTES");
1379 Returns an object/hash of transaction, circ, title where transaction = an open
1380 user transactions that has an initial charge (mbts objects), circ is the
1381 attached circluation, and title is the title the circ points to
1382 Params are login_session, user_id
1383 Optional third parameter is the transactions type. defaults to all
1386 __PACKAGE__->register_method(
1387 method => "user_transactions",
1388 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1389 notes => <<" NOTES");
1390 Returns an object/hash of transaction, circ, title where transaction = an open
1391 user transaction that has a balance (mbts objects), circ is the attached
1392 circluation, and title is the title the circ points to
1393 Params are login_session, user_id
1394 Optional third parameter is the transaction type. defaults to all
1397 __PACKAGE__->register_method(
1398 method => "user_transactions",
1399 api_name => "open-ils.actor.user.transactions.count",
1400 notes => <<" NOTES");
1401 Returns an object/hash of transaction, circ, title where transaction = an open
1402 user transactions (mbts objects), circ is the attached circluation, and title
1403 is the title the circ points to
1404 Params are login_session, user_id
1405 Optional third parameter is the transactions type. defaults to all
1408 __PACKAGE__->register_method(
1409 method => "user_transactions",
1410 api_name => "open-ils.actor.user.transactions.have_charge.count",
1411 notes => <<" NOTES");
1412 Returns an object/hash of transaction, circ, title where transaction = an open
1413 user transactions that has an initial charge (mbts objects), circ is the
1414 attached circluation, and title is the title the circ points to
1415 Params are login_session, user_id
1416 Optional third parameter is the transactions type. defaults to all
1419 __PACKAGE__->register_method(
1420 method => "user_transactions",
1421 api_name => "open-ils.actor.user.transactions.have_balance.count",
1422 notes => <<" NOTES");
1423 Returns an object/hash of transaction, circ, title where transaction = an open
1424 user transaction that has a balance (mbts objects), circ is the attached
1425 circluation, and title is the title the circ points to
1426 Params are login_session, user_id
1427 Optional third parameter is the transaction type. defaults to all
1430 __PACKAGE__->register_method(
1431 method => "user_transactions",
1432 api_name => "open-ils.actor.user.transactions.have_balance.total",
1433 notes => <<" NOTES");
1434 Returns an object/hash of transaction, circ, title where transaction = an open
1435 user transaction that has a balance (mbts objects), circ is the attached
1436 circluation, and title is the title the circ points to
1437 Params are login_session, user_id
1438 Optional third parameter is the transaction type. defaults to all
1443 sub user_transactions {
1444 my( $self, $client, $login_session, $user_id, $type ) = @_;
1446 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1447 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1448 return $evt if $evt;
1450 my $api = $self->api_name();
1454 if(defined($type)) { @xact = (xact_type => $type);
1456 } else { @xact = (); }
1459 ->method_lookup('open-ils.actor.user.transactions.history.still_open')
1460 ->run($login_session => $user_id => $type);
1462 if($api =~ /have_charge/o) {
1464 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1466 } elsif($api =~ /have_balance/o) {
1468 $trans = [ grep { int($_->balance_owed * 100) != 0 } @$trans ];
1471 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1475 if($api =~ /total/o) {
1477 for my $t (@$trans) {
1478 $total += $t->balance_owed;
1481 $logger->debug("Total balance owed by user $user_id: $total");
1485 if($api =~ /count/o) { return scalar @$trans; }
1486 if($api !~ /fleshed/o) { return $trans; }
1489 for my $t (@$trans) {
1491 if( $t->xact_type ne 'circulation' ) {
1492 push @resp, {transaction => $t};
1496 my $circ = $apputils->simple_scalar_request(
1498 "open-ils.cstore.direct.action.circulation.retrieve",
1503 my $title = $apputils->simple_scalar_request(
1505 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1506 $circ->target_copy );
1510 my $u = OpenILS::Utils::ModsParser->new();
1511 $u->start_mods_batch($title->marc());
1512 my $mods = $u->finish_mods_batch();
1513 $mods->doc_id($title->id) if $mods;
1515 push @resp, {transaction => $t, circ => $circ, record => $mods };
1523 __PACKAGE__->register_method(
1524 method => "user_transaction_retrieve",
1525 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1527 notes => <<" NOTES");
1528 Returns a fleshedtransaction record
1530 __PACKAGE__->register_method(
1531 method => "user_transaction_retrieve",
1532 api_name => "open-ils.actor.user.transaction.retrieve",
1534 notes => <<" NOTES");
1535 Returns a transaction record
1537 sub user_transaction_retrieve {
1538 my( $self, $client, $login_session, $bill_id ) = @_;
1540 # XXX I think I'm deprecated... make sure
1542 my $trans = $apputils->simple_scalar_request(
1544 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1548 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1549 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1550 return $evt if $evt;
1552 my $api = $self->api_name();
1553 if($api !~ /fleshed/o) { return $trans; }
1555 if( $trans->xact_type ne 'circulation' ) {
1556 $logger->debug("Returning non-circ transaction");
1557 return {transaction => $trans};
1560 my $circ = $apputils->simple_scalar_request(
1562 "open-ils..direct.action.circulation.retrieve",
1565 return {transaction => $trans} unless $circ;
1566 $logger->debug("Found the circ transaction");
1568 my $title = $apputils->simple_scalar_request(
1570 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1571 $circ->target_copy );
1573 return {transaction => $trans, circ => $circ } unless $title;
1574 $logger->debug("Found the circ title");
1578 my $u = OpenILS::Utils::ModsParser->new();
1579 $u->start_mods_batch($title->marc());
1580 $mods = $u->finish_mods_batch();
1582 if ($title->id == OILS_PRECAT_RECORD) {
1583 my $copy = $apputils->simple_scalar_request(
1585 "open-ils.cstore.direct.asset.copy.retrieve",
1586 $circ->target_copy );
1588 $mods = new Fieldmapper::metabib::virtual_record;
1589 $mods->doc_id(OILS_PRECAT_RECORD);
1590 $mods->title($copy->dummy_title);
1591 $mods->author($copy->dummy_author);
1595 $logger->debug("MODSized the circ title");
1597 return {transaction => $trans, circ => $circ, record => $mods };
1601 __PACKAGE__->register_method(
1602 method => "hold_request_count",
1603 api_name => "open-ils.actor.user.hold_requests.count",
1605 notes => <<" NOTES");
1606 Returns hold ready/total counts
1608 sub hold_request_count {
1609 my( $self, $client, $login_session, $userid ) = @_;
1611 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1612 $login_session, $userid, 'VIEW_HOLD' );
1613 return $evt if $evt;
1616 my $holds = $apputils->simple_scalar_request(
1618 "open-ils.cstore.direct.action.hold_request.search.atomic",
1621 fulfillment_time => {"=" => undef },
1622 cancel_time => undef,
1627 for my $h (@$holds) {
1628 next unless $h->capture_time and $h->current_copy;
1630 my $copy = $apputils->simple_scalar_request(
1632 "open-ils.cstore.direct.asset.copy.retrieve",
1636 if ($copy and $copy->status == 8) {
1641 return { total => scalar(@$holds), ready => scalar(@ready) };
1645 __PACKAGE__->register_method(
1646 method => "checkedout_count",
1647 api_name => "open-ils.actor.user.checked_out.count__",
1649 notes => <<" NOTES");
1650 Returns a transaction record
1654 sub checkedout_count {
1655 my( $self, $client, $login_session, $userid ) = @_;
1657 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1658 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1659 return $evt if $evt;
1661 my $circs = $apputils->simple_scalar_request(
1663 "open-ils.cstore.direct.action.circulation.search.atomic",
1664 { usr => $userid, stop_fines => undef }
1665 #{ usr => $userid, checkin_time => {"=" => undef } }
1668 my $parser = DateTime::Format::ISO8601->new;
1671 for my $c (@$circs) {
1672 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1673 my $due = $due_dt->epoch;
1675 if ($due < DateTime->today->epoch) {
1680 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1684 __PACKAGE__->register_method(
1685 method => "checked_out",
1686 api_name => "open-ils.actor.user.checked_out",
1689 Returns a structure of circulations objects sorted by
1690 out, overdue, lost, claims_returned, long_overdue.
1691 A list of IDs are returned of each type.
1692 lost, long_overdue, and claims_returned circ will not
1693 be "finished" (there is an outstanding balance or some
1694 other pending action on the circ).
1696 The .count method also includes a 'total' field which
1697 sums all "open" circs
1701 __PACKAGE__->register_method(
1702 method => "checked_out",
1703 api_name => "open-ils.actor.user.checked_out.count",
1705 signature => q/@see open-ils.actor.user.checked_out/
1709 my( $self, $conn, $auth, $userid ) = @_;
1711 my $e = new_editor(authtoken=>$auth);
1712 return $e->event unless $e->checkauth;
1714 if( $userid ne $e->requestor->id ) {
1715 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1718 my $count = $self->api_name =~ /count/;
1719 return _checked_out( $count, $e, $userid );
1723 my( $iscount, $e, $userid ) = @_;
1726 my $meth = 'open-ils.storage.actor.user.checked_out';
1727 $meth = "$meth.count" if $iscount;
1728 return $U->storagereq($meth, $userid);
1730 # XXX Old code - moved to storage
1731 #------------------------------------------------------------------------------
1732 #------------------------------------------------------------------------------
1733 my $circs = $e->search_action_circulation(
1734 { usr => $userid, checkin_time => undef });
1736 my $parser = DateTime::Format::ISO8601->new;
1738 # split the circs up into overdue and not-overdue circs
1740 for my $c (@$circs) {
1741 if( $c->due_date ) {
1742 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1743 my $due = $due_dt->epoch;
1744 if ($due < DateTime->today->epoch) {
1754 my( @open, @od, @lost, @cr, @lo );
1756 while (my $c = shift(@out)) {
1757 push( @open, $c->id ) if (!$c->stop_fines || $c->stop_fines eq 'MAXFINES' || $c->stop_fines eq 'RENEW');
1758 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1759 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1760 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1763 while (my $c = shift(@overdue)) {
1764 push( @od, $c->id ) if (!$c->stop_fines || $c->stop_fines eq 'MAXFINES' || $c->stop_fines eq 'RENEW');
1765 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1766 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1767 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1772 total => @open + @od + @lost + @cr + @lo,
1773 out => scalar(@open),
1774 overdue => scalar(@od),
1775 lost => scalar(@lost),
1776 claims_returned => scalar(@cr),
1777 long_overdue => scalar(@lo)
1785 claims_returned => \@cr,
1786 long_overdue => \@lo
1791 sub _checked_out_WHAT {
1792 my( $iscount, $e, $userid ) = @_;
1794 my $circs = $e->search_action_circulation(
1795 { usr => $userid, stop_fines => undef });
1797 my $mcircs = $e->search_action_circulation(
1800 checkin_time => undef,
1801 xact_finish => undef,
1805 push( @$circs, @$mcircs );
1807 my $parser = DateTime::Format::ISO8601->new;
1809 # split the circs up into overdue and not-overdue circs
1811 for my $c (@$circs) {
1812 if( $c->due_date ) {
1813 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1814 my $due = $due_dt->epoch;
1815 if ($due < DateTime->today->epoch) {
1816 push @overdue, $c->id;
1825 # grab all of the lost, claims-returned, and longoverdue circs
1826 #my $open = $e->search_action_circulation(
1827 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1830 # these items have stop_fines, but no xact_finish, so money
1831 # is owed on them and they have not been checked in
1832 my $open = $e->search_action_circulation(
1835 stop_fines => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] },
1836 xact_finish => undef,
1837 checkin_time => undef,
1842 my( @lost, @cr, @lo );
1843 for my $c (@$open) {
1844 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1845 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1846 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1852 total => @$circs + @lost + @cr + @lo,
1853 out => scalar(@out),
1854 overdue => scalar(@overdue),
1855 lost => scalar(@lost),
1856 claims_returned => scalar(@cr),
1857 long_overdue => scalar(@lo)
1863 overdue => \@overdue,
1865 claims_returned => \@cr,
1866 long_overdue => \@lo
1872 __PACKAGE__->register_method(
1873 method => "checked_in_with_fines",
1874 api_name => "open-ils.actor.user.checked_in_with_fines",
1876 signature => q/@see open-ils.actor.user.checked_out/
1878 sub checked_in_with_fines {
1879 my( $self, $conn, $auth, $userid ) = @_;
1881 my $e = new_editor(authtoken=>$auth);
1882 return $e->event unless $e->checkauth;
1884 if( $userid ne $e->requestor->id ) {
1885 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1888 # money is owed on these items and they are checked in
1889 my $open = $e->search_action_circulation(
1892 xact_finish => undef,
1893 checkin_time => { "!=" => undef },
1898 my( @lost, @cr, @lo );
1899 for my $c (@$open) {
1900 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1901 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1902 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1907 claims_returned => \@cr,
1908 long_overdue => \@lo
1920 __PACKAGE__->register_method(
1921 method => "user_transaction_history",
1922 api_name => "open-ils.actor.user.transactions.history",
1924 notes => <<" NOTES");
1925 Returns a list of billable transaction ids for a user, optionally by type
1927 __PACKAGE__->register_method(
1928 method => "user_transaction_history",
1929 api_name => "open-ils.actor.user.transactions.history.have_charge",
1931 notes => <<" NOTES");
1932 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1934 __PACKAGE__->register_method(
1935 method => "user_transaction_history",
1936 api_name => "open-ils.actor.user.transactions.history.have_balance",
1938 notes => <<" NOTES");
1939 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1941 __PACKAGE__->register_method(
1942 method => "user_transaction_history",
1943 api_name => "open-ils.actor.user.transactions.history.still_open",
1945 notes => <<" NOTES");
1946 Returns a list of billable transaction ids for a user that are not finished
1948 __PACKAGE__->register_method(
1949 method => "user_transaction_history",
1950 api_name => "open-ils.actor.user.transactions.history.have_bill",
1952 notes => <<" NOTES");
1953 Returns a list of billable transaction ids for a user that has billings
1959 sub _user_transaction_history {
1960 my( $self, $client, $login_session, $user_id, $type ) = @_;
1962 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1963 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1964 return $evt if $evt;
1966 my $api = $self->api_name();
1971 @xact = (xact_type => $type) if(defined($type));
1972 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1973 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1975 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1977 my $trans = $apputils->simple_scalar_request(
1979 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1980 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1982 return [ map { $_->id } @$trans ];
1986 =head SEE APPUTILS.PM
1991 for my $x (@xacts) {
1992 my $s = new Fieldmapper::money::billable_transaction_summary;
1995 $s->xact_start( $x->xact_start );
1996 $s->xact_finish( $x->xact_finish );
2000 for my $b (@{ $x->billings }) {
2001 next if ($U->is_true($b->voided));
2002 $to += ($b->amount * 100);
2003 $lb ||= $b->billing_ts;
2004 if ($b->billing_ts ge $lb) {
2005 $lb = $b->billing_ts;
2006 $s->last_billing_note($b->note);
2007 $s->last_billing_ts($b->billing_ts);
2008 $s->last_billing_type($b->billing_type);
2012 $s->total_owed( sprintf('%0.2f', $to / 100 ) );
2016 for my $p (@{ $x->payments }) {
2017 next if ($U->is_true($p->voided));
2018 $tp += ($p->amount * 100);
2019 $lp ||= $p->payment_ts;
2020 if ($p->payment_ts ge $lp) {
2021 $lp = $p->payment_ts;
2022 $s->last_payment_note($p->note);
2023 $s->last_payment_ts($p->payment_ts);
2024 $s->last_payment_type($p->payment_type);
2027 $s->total_paid( sprintf('%0.2f', $tp / 100 ) );
2029 $s->balance_owed( sprintf('%0.2f', ($to - $tp) / 100) );
2031 $s->xact_type( 'grocery' ) if ($x->grocery);
2032 $s->xact_type( 'circulation' ) if ($x->circulation);
2041 sub user_transaction_history {
2042 my( $self, $conn, $auth, $userid, $type ) = @_;
2044 # run inside of a transaction to prevent replication delays
2045 my $e = new_editor(xact=>1, authtoken=>$auth);
2046 return $e->die_event unless $e->checkauth;
2048 if( $e->requestor->id ne $userid ) {
2049 return $e->die_event
2050 unless $e->allowed('VIEW_USER_TRANSACTIONS');
2053 my $api = $self->api_name;
2054 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
2056 my @xacts = @{ $e->search_money_billable_transaction(
2057 [ { usr => $userid, @xact_finish },
2059 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
2060 order_by => { mbt => 'xact_start DESC' },
2068 #my @mbts = _make_mbts( @xacts );
2069 my @mbts = $U->make_mbts( @xacts );
2071 if(defined($type)) {
2072 @mbts = grep { $_->xact_type eq $type } @mbts;
2075 if($api =~ /have_balance/o) {
2076 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
2079 if($api =~ /have_charge/o) {
2080 @mbts = grep { defined($_->last_billing_ts) } @mbts;
2083 if($api =~ /have_bill/o) {
2084 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
2092 __PACKAGE__->register_method(
2093 method => "user_perms",
2094 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2096 notes => <<" NOTES");
2097 Returns a list of permissions
2100 my( $self, $client, $authtoken, $user ) = @_;
2102 my( $staff, $evt ) = $apputils->checkses($authtoken);
2103 return $evt if $evt;
2105 $user ||= $staff->id;
2107 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2111 return $apputils->simple_scalar_request(
2113 "open-ils.storage.permission.user_perms.atomic",
2117 __PACKAGE__->register_method(
2118 method => "retrieve_perms",
2119 api_name => "open-ils.actor.permissions.retrieve",
2120 notes => <<" NOTES");
2121 Returns a list of permissions
2123 sub retrieve_perms {
2124 my( $self, $client ) = @_;
2125 return $apputils->simple_scalar_request(
2127 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2128 { id => { '!=' => undef } }
2132 __PACKAGE__->register_method(
2133 method => "retrieve_groups",
2134 api_name => "open-ils.actor.groups.retrieve",
2135 notes => <<" NOTES");
2136 Returns a list of user groupss
2138 sub retrieve_groups {
2139 my( $self, $client ) = @_;
2140 return new_editor()->retrieve_all_permission_grp_tree();
2143 __PACKAGE__->register_method(
2144 method => "retrieve_org_address",
2145 api_name => "open-ils.actor.org_unit.address.retrieve",
2146 notes => <<' NOTES');
2147 Returns an org_unit address by ID
2148 @param An org_address ID
2150 sub retrieve_org_address {
2151 my( $self, $client, $id ) = @_;
2152 return $apputils->simple_scalar_request(
2154 "open-ils.cstore.direct.actor.org_address.retrieve",
2159 __PACKAGE__->register_method(
2160 method => "retrieve_groups_tree",
2161 api_name => "open-ils.actor.groups.tree.retrieve",
2162 notes => <<" NOTES");
2163 Returns a list of user groups
2165 sub retrieve_groups_tree {
2166 my( $self, $client ) = @_;
2167 return new_editor()->search_permission_grp_tree(
2172 flesh_fields => { pgt => ["children"] },
2173 order_by => { pgt => 'name'}
2180 # turns an org list into an org tree
2182 sub build_group_tree {
2184 my( $self, $grplist) = @_;
2186 return $grplist unless (
2187 ref($grplist) and @$grplist > 1 );
2189 my @list = sort { $a->name cmp $b->name } @$grplist;
2192 for my $grp (@list) {
2194 if ($grp and !defined($grp->parent)) {
2198 my ($parent) = grep { $_->id == $grp->parent} @list;
2200 $parent->children([]) unless defined($parent->children);
2201 push( @{$parent->children}, $grp );
2209 __PACKAGE__->register_method(
2210 method => "add_user_to_groups",
2211 api_name => "open-ils.actor.user.set_groups",
2212 notes => <<" NOTES");
2213 Adds a user to one or more permission groups
2216 sub add_user_to_groups {
2217 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2219 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2220 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2221 return $evt if $evt;
2223 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2224 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2225 return $evt if $evt;
2227 $apputils->simplereq(
2229 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2231 for my $group (@$groups) {
2232 my $link = Fieldmapper::permission::usr_grp_map->new;
2234 $link->usr($userid);
2236 my $id = $apputils->simplereq(
2238 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2244 __PACKAGE__->register_method(
2245 method => "get_user_perm_groups",
2246 api_name => "open-ils.actor.user.get_groups",
2247 notes => <<" NOTES");
2248 Retrieve a user's permission groups.
2252 sub get_user_perm_groups {
2253 my( $self, $client, $authtoken, $userid ) = @_;
2255 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2256 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2257 return $evt if $evt;
2259 return $apputils->simplereq(
2261 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2266 __PACKAGE__->register_method (
2267 method => 'register_workstation',
2268 api_name => 'open-ils.actor.workstation.register.override',
2269 signature => q/@see open-ils.actor.workstation.register/);
2271 __PACKAGE__->register_method (
2272 method => 'register_workstation',
2273 api_name => 'open-ils.actor.workstation.register',
2275 Registers a new workstion in the system
2276 @param authtoken The login session key
2277 @param name The name of the workstation id
2278 @param owner The org unit that owns this workstation
2279 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2280 if the name is already in use.
2283 sub _register_workstation {
2284 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2285 my( $requestor, $evt ) = $U->checkses($authtoken);
2286 return $evt if $evt;
2287 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2288 return $evt if $evt;
2290 my $ws = $U->cstorereq(
2291 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2292 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2294 $ws = Fieldmapper::actor::workstation->new;
2295 $ws->owning_lib($owner);
2298 my $id = $U->storagereq(
2299 'open-ils.storage.direct.actor.workstation.create', $ws );
2300 return $U->DB_UPDATE_FAILED($ws) unless $id;
2306 sub register_workstation {
2307 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2309 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2310 return $e->event unless $e->checkauth;
2311 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2312 my $existing = $e->search_actor_workstation({name => $name});
2315 if( $self->api_name =~ /override/o ) {
2316 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2317 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2319 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2323 my $ws = Fieldmapper::actor::workstation->new;
2324 $ws->owning_lib($owner);
2326 $e->create_actor_workstation($ws) or return $e->event;
2328 return $ws->id; # note: editor sets the id on the new object for us
2332 __PACKAGE__->register_method (
2333 method => 'fetch_patron_note',
2334 api_name => 'open-ils.actor.note.retrieve.all',
2336 Returns a list of notes for a given user
2337 Requestor must have VIEW_USER permission if pub==false and
2338 @param authtoken The login session key
2339 @param args Hash of params including
2340 patronid : the patron's id
2341 pub : true if retrieving only public notes
2345 sub fetch_patron_note {
2346 my( $self, $conn, $authtoken, $args ) = @_;
2347 my $patronid = $$args{patronid};
2349 my($reqr, $evt) = $U->checkses($authtoken);
2350 return $evt if $evt;
2353 ($patron, $evt) = $U->fetch_user($patronid);
2354 return $evt if $evt;
2357 if( $patronid ne $reqr->id ) {
2358 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2359 return $evt if $evt;
2361 return $U->cstorereq(
2362 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2363 { usr => $patronid, pub => 't' } );
2366 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2367 return $evt if $evt;
2369 return $U->cstorereq(
2370 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2373 __PACKAGE__->register_method (
2374 method => 'create_user_note',
2375 api_name => 'open-ils.actor.note.create',
2377 Creates a new note for the given user
2378 @param authtoken The login session key
2379 @param note The note object
2382 sub create_user_note {
2383 my( $self, $conn, $authtoken, $note ) = @_;
2384 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2385 return $e->die_event unless $e->checkauth;
2387 my $user = $e->retrieve_actor_user($note->usr)
2388 or return $e->die_event;
2390 return $e->die_event unless
2391 $e->allowed('UPDATE_USER',$user->home_ou);
2393 $note->creator($e->requestor->id);
2394 $e->create_actor_usr_note($note) or return $e->die_event;
2400 __PACKAGE__->register_method (
2401 method => 'delete_user_note',
2402 api_name => 'open-ils.actor.note.delete',
2404 Deletes a note for the given user
2405 @param authtoken The login session key
2406 @param noteid The note id
2409 sub delete_user_note {
2410 my( $self, $conn, $authtoken, $noteid ) = @_;
2412 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2413 return $e->die_event unless $e->checkauth;
2414 my $note = $e->retrieve_actor_usr_note($noteid)
2415 or return $e->die_event;
2416 my $user = $e->retrieve_actor_user($note->usr)
2417 or return $e->die_event;
2418 return $e->die_event unless
2419 $e->allowed('UPDATE_USER', $user->home_ou);
2421 $e->delete_actor_usr_note($note) or return $e->die_event;
2427 __PACKAGE__->register_method (
2428 method => 'update_user_note',
2429 api_name => 'open-ils.actor.note.update',
2431 @param authtoken The login session key
2432 @param note The note
2436 sub update_user_note {
2437 my( $self, $conn, $auth, $note ) = @_;
2438 my $e = new_editor(authtoken=>$auth, xact=>1);
2439 return $e->event unless $e->checkauth;
2440 my $patron = $e->retrieve_actor_user($note->usr)
2441 or return $e->event;
2442 return $e->event unless
2443 $e->allowed('UPDATE_USER', $patron->home_ou);
2444 $e->update_actor_user_note($note)
2445 or return $e->event;
2453 __PACKAGE__->register_method (
2454 method => 'create_closed_date',
2455 api_name => 'open-ils.actor.org_unit.closed_date.create',
2457 Creates a new closing entry for the given org_unit
2458 @param authtoken The login session key
2459 @param note The closed_date object
2462 sub create_closed_date {
2463 my( $self, $conn, $authtoken, $cd ) = @_;
2465 my( $user, $evt ) = $U->checkses($authtoken);
2466 return $evt if $evt;
2468 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2469 return $evt if $evt;
2471 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2473 my $id = $U->storagereq(
2474 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2475 return $U->DB_UPDATE_FAILED($cd) unless $id;
2480 __PACKAGE__->register_method (
2481 method => 'delete_closed_date',
2482 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2484 Deletes a closing entry for the given org_unit
2485 @param authtoken The login session key
2486 @param noteid The close_date id
2489 sub delete_closed_date {
2490 my( $self, $conn, $authtoken, $cd ) = @_;
2492 my( $user, $evt ) = $U->checkses($authtoken);
2493 return $evt if $evt;
2496 ($cd_obj, $evt) = fetch_closed_date($cd);
2497 return $evt if $evt;
2499 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2500 return $evt if $evt;
2502 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2504 my $stat = $U->storagereq(
2505 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2506 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2511 __PACKAGE__->register_method(
2512 method => 'usrname_exists',
2513 api_name => 'open-ils.actor.username.exists',
2515 Returns 1 if the requested username exists, returns 0 otherwise
2519 sub usrname_exists {
2520 my( $self, $conn, $auth, $usrname ) = @_;
2521 my $e = new_editor(authtoken=>$auth);
2522 return $e->event unless $e->checkauth;
2523 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2524 return $$a[0] if $a and @$a;
2528 __PACKAGE__->register_method(
2529 method => 'barcode_exists',
2530 api_name => 'open-ils.actor.barcode.exists',
2532 Returns 1 if the requested barcode exists, returns 0 otherwise
2536 sub barcode_exists {
2537 my( $self, $conn, $auth, $barcode ) = @_;
2538 my $e = new_editor(authtoken=>$auth);
2539 return $e->event unless $e->checkauth;
2540 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2541 return $$a[0] if $a and @$a;
2546 __PACKAGE__->register_method(
2547 method => 'retrieve_net_levels',
2548 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2551 sub retrieve_net_levels {
2552 my( $self, $conn, $auth ) = @_;
2553 my $e = new_editor(authtoken=>$auth);
2554 return $e->event unless $e->checkauth;
2555 return $e->retrieve_all_config_net_access_level();
2559 __PACKAGE__->register_method(
2560 method => 'fetch_org_by_shortname',
2561 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2563 sub fetch_org_by_shortname {
2564 my( $self, $conn, $sname ) = @_;
2565 my $e = new_editor();
2566 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2567 return $e->event unless $org;
2572 __PACKAGE__->register_method(
2573 method => 'session_home_lib',
2574 api_name => 'open-ils.actor.session.home_lib',
2577 sub session_home_lib {
2578 my( $self, $conn, $auth ) = @_;
2579 my $e = new_editor(authtoken=>$auth);
2580 return undef unless $e->checkauth;
2581 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2582 return $org->shortname;
2587 __PACKAGE__->register_method(
2588 method => 'slim_tree',
2589 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2592 my $tree = new_editor()->search_actor_org_unit(
2594 {"parent_ou" => undef },
2597 flesh_fields => { aou => ['children'] },
2598 order_by => { aou => 'name'},
2599 select => { aou => ["id","shortname", "name"]},
2604 return trim_tree($tree);
2610 return undef unless $tree;
2612 code => $tree->shortname,
2613 name => $tree->name,
2615 if( $tree->children and @{$tree->children} ) {
2616 $htree->{children} = [];
2617 for my $c (@{$tree->children}) {
2618 push( @{$htree->{children}}, trim_tree($c) );
2626 __PACKAGE__->register_method(
2627 method => "update_penalties",
2628 api_name => "open-ils.actor.user.penalties.update");
2629 sub update_penalties {
2630 my( $self, $conn, $auth, $userid ) = @_;
2631 my $e = new_editor(authtoken=>$auth);
2632 return $e->event unless $e->checkauth;
2633 $U->update_patron_penalties(
2635 patronid => $userid,
2642 __PACKAGE__->register_method(
2643 method => "user_retrieve_fleshed_by_id",
2644 api_name => "open-ils.actor.user.fleshed.retrieve",);
2646 sub user_retrieve_fleshed_by_id {
2647 my( $self, $client, $auth, $user_id, $fields ) = @_;
2648 my $e = new_editor(authtoken => $auth);
2649 return $e->event unless $e->checkauth;
2651 if( $e->requestor->id != $user_id ) {
2652 return $e->event unless $e->allowed('VIEW_USER');
2658 "standing_penalties",
2662 "stat_cat_entries" ];
2663 return new_flesh_user($user_id, $fields, $e);
2667 sub new_flesh_user {
2670 my $fields = shift || [];
2671 my $e = shift || new_editor(xact=>1);
2673 my $user = $e->retrieve_actor_user(
2678 "flesh_fields" => { "au" => $fields }
2681 ) or return $e->event;
2684 if( grep { $_ eq 'addresses' } @$fields ) {
2686 $user->addresses([]) unless @{$user->addresses};
2688 if( ref $user->billing_address ) {
2689 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2690 push( @{$user->addresses}, $user->billing_address );
2694 if( ref $user->mailing_address ) {
2695 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2696 push( @{$user->addresses}, $user->mailing_address );
2702 $user->clear_passwd();
2709 __PACKAGE__->register_method(
2710 method => "user_retrieve_parts",
2711 api_name => "open-ils.actor.user.retrieve.parts",);
2713 sub user_retrieve_parts {
2714 my( $self, $client, $auth, $user_id, $fields ) = @_;
2715 my $e = new_editor(authtoken => $auth);
2716 return $e->event unless $e->checkauth;
2717 if( $e->requestor->id != $user_id ) {
2718 return $e->event unless $e->allowed('VIEW_USER');
2721 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2722 push(@resp, $user->$_()) for(@$fields);