1 package OpenILS::Application::Actor;
2 use OpenILS::Application;
3 use base qw/OpenILS::Application/;
4 use strict; use warnings;
6 $Data::Dumper::Indent = 0;
9 use Digest::MD5 qw(md5_hex);
11 use OpenSRF::EX qw(:try);
14 use OpenILS::Application::AppUtils;
16 use OpenILS::Utils::Fieldmapper;
17 use OpenILS::Utils::ModsParser;
18 use OpenSRF::Utils::Logger qw/$logger/;
19 use OpenSRF::Utils qw/:datetime/;
20 use OpenSRF::Utils::SettingsClient;
22 use OpenSRF::Utils::Cache;
24 use OpenSRF::Utils::JSON;
26 use DateTime::Format::ISO8601;
27 use OpenILS::Const qw/:const/;
29 use OpenILS::Application::Actor::Container;
30 use OpenILS::Application::Actor::ClosedDates;
31 use OpenILS::Application::Actor::UserGroups;
32 use OpenILS::Application::Actor::Friends;
34 use OpenILS::Utils::CStoreEditor qw/:funcs/;
35 use OpenILS::Utils::Penalty;
38 OpenILS::Application::Actor::Container->initialize();
39 OpenILS::Application::Actor::UserGroups->initialize();
40 OpenILS::Application::Actor::ClosedDates->initialize();
43 my $apputils = "OpenILS::Application::AppUtils";
46 sub _d { warn "Patron:\n" . Dumper(shift()); }
49 my $set_user_settings;
53 __PACKAGE__->register_method(
54 method => "update_user_setting",
55 api_name => "open-ils.actor.patron.settings.update",
57 sub update_user_setting {
58 my($self, $conn, $auth, $user_id, $settings) = @_;
59 my $e = new_editor(xact => 1, authtoken => $auth);
60 return $e->die_event unless $e->checkauth;
62 $user_id = $e->requestor->id unless defined $user_id;
64 unless($e->requestor->id == $user_id) {
65 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
66 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
69 for my $name (keys %$settings) {
70 my $val = $$settings{$name};
71 my $set = $e->search_actor_user_setting({usr => $user_id, name => $name})->[0];
74 $val = OpenSRF::Utils::JSON->perl2JSON($val);
77 $e->update_actor_user_setting($set) or return $e->die_event;
79 $set = Fieldmapper::actor::user_setting->new;
83 $e->create_actor_user_setting($set) or return $e->die_event;
86 $e->delete_actor_user_setting($set) or return $e->die_event;
95 __PACKAGE__->register_method(
96 method => "set_ou_settings",
97 api_name => "open-ils.actor.org_unit.settings.update",
100 my( $self, $client, $auth, $org_id, $settings ) = @_;
102 my $e = new_editor(authtoken => $auth, xact => 1);
103 return $e->die_event unless $e->checkauth;
105 my $all_allowed = $e->allowed("UPDATE_ORG_UNIT_SETTING_ALL", $org_id);
107 for my $name (keys %$settings) {
108 my $val = $$settings{$name};
110 my $type = $e->retrieve_config_org_unit_setting_type($name) or return $e->die_event;
111 my $set = $e->search_actor_org_unit_setting({org_unit => $org_id, name => $name})->[0];
113 unless($all_allowed) {
114 return $e->die_event unless $e->allowed("UPDATE_ORG_UNIT_SETTING.$name", $org_id);
118 $val = OpenSRF::Utils::JSON->perl2JSON($val);
121 $e->update_actor_org_unit_setting($set) or return $e->die_event;
123 $set = Fieldmapper::actor::org_unit_setting->new;
124 $set->org_unit($org_id);
127 $e->create_actor_org_unit_setting($set) or return $e->die_event;
130 $e->delete_actor_org_unit_setting($set) or return $e->die_event;
138 __PACKAGE__->register_method(
139 method => "user_settings",
140 api_name => "open-ils.actor.patron.settings.retrieve",
143 my( $self, $client, $auth, $user_id, $setting ) = @_;
145 my $e = new_editor(authtoken => $auth);
146 return $e->event unless $e->checkauth;
147 $user_id = $e->requestor->id unless defined $user_id;
149 my $patron = $e->retrieve_actor_user($user_id) or return $e->event;
150 if($e->requestor->id != $user_id) {
151 return $e->event unless $e->allowed('VIEW_USER', $patron->home_ou);
155 my $val = $e->search_actor_user_setting({usr => $user_id, name => $setting})->[0];
156 return '' unless $val;
157 return OpenSRF::Utils::JSON->JSON2perl($val->value);
159 my $s = $e->search_actor_user_setting({usr => $user_id});
160 return { map { ( $_->name => OpenSRF::Utils::JSON->JSON2perl($_->value) ) } @$s };
166 __PACKAGE__->register_method(
167 method => "ranged_ou_settings",
168 api_name => "open-ils.actor.org_unit_setting.values.ranged.retrieve",
170 sub ranged_ou_settings {
171 my( $self, $client, $auth, $org_id ) = @_;
173 my $e = new_editor(authtoken => $auth);
174 return $e->event unless $e->checkauth;
175 return $e->event unless $e->allowed('VIEW_ORG_SETTINGS', $org_id);
178 my $org_list = $U->get_org_ancestors($org_id);
179 my $settings = $e->search_actor_org_unit_setting({org_unit => $org_list});
180 $org_list = [ reverse @$org_list ];
182 # start at the context org and capture the setting value
183 # without clobbering settings we've already captured
184 for my $org_id (@$org_list) {
186 my @sets = grep { $_->org_unit == $org_id } @$settings;
188 for my $set (@sets) {
189 $ranged_settings{$set->name} = OpenSRF::Utils::JSON->JSON2perl($set->value)
190 unless defined $ranged_settings{$set->name};
194 return \%ranged_settings;
199 __PACKAGE__->register_method(
200 api_name => 'open-ils.actor.ou_setting.ancestor_default',
201 method => 'ou_ancestor_setting',
204 # ------------------------------------------------------------------
205 # Attempts to find the org setting value for a given org. if not
206 # found at the requested org, searches up the org tree until it
207 # finds a parent that has the requested setting.
208 # when found, returns { org => $id, value => $value }
209 # otherwise, returns NULL
210 # ------------------------------------------------------------------
211 sub ou_ancestor_setting {
212 my( $self, $client, $orgid, $name ) = @_;
213 return $U->ou_ancestor_setting($orgid, $name);
216 __PACKAGE__->register_method(
217 api_name => 'open-ils.actor.ou_setting.ancestor_default.batch',
218 method => 'ou_ancestor_setting_batch',
220 sub ou_ancestor_setting_batch {
221 my( $self, $client, $orgid, $name_list ) = @_;
223 $values{$_} = $U->ou_ancestor_setting($orgid, $_) for @$name_list;
231 __PACKAGE__->register_method(
232 method => "update_patron",
233 api_name => "open-ils.actor.patron.update",);
236 my( $self, $client, $user_session, $patron ) = @_;
238 my $session = $apputils->start_db_session();
241 $logger->info("Creating new patron...") if $patron->isnew;
242 $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
244 my( $user_obj, $evt ) = $U->checkses($user_session);
247 $evt = check_group_perm($session, $user_obj, $patron);
251 # $new_patron is the patron in progress. $patron is the original patron
252 # passed in with the method. new_patron will change as the components
253 # of patron are added/updated.
257 # unflesh the real items on the patron
258 $patron->card( $patron->card->id ) if(ref($patron->card));
259 $patron->billing_address( $patron->billing_address->id )
260 if(ref($patron->billing_address));
261 $patron->mailing_address( $patron->mailing_address->id )
262 if(ref($patron->mailing_address));
264 # create/update the patron first so we can use his id
265 if($patron->isnew()) {
266 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
268 } else { $new_patron = $patron; }
270 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
273 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
276 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
279 # re-update the patron if anything has happened to him during this process
280 if($new_patron->ischanged()) {
281 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
285 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
288 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
291 $apputils->commit_db_session($session);
293 $evt = apply_invalid_addr_penalty($patron);
296 my $tses = OpenSRF::AppSession->create('open-ils.trigger');
298 $tses->request('open-ils.trigger.event.autocreate', 'au.create', $new_patron, $new_patron->home_ou);
300 $tses->request('open-ils.trigger.event.autocreate', 'au.update', $new_patron, $new_patron->home_ou);
303 return flesh_user($new_patron->id(), new_editor(requestor => $user_obj));
306 sub apply_invalid_addr_penalty {
308 my $e = new_editor(xact => 1);
310 # grab the invalid address penalty if set
311 my $penalties = OpenILS::Utils::Penalty->retrieve_usr_penalties($e, $patron->id, $patron->home_ou);
313 my ($addr_penalty) = grep
314 { $_->standing_penalty->name eq 'INVALID_PATRON_ADDRESS' } @$penalties;
316 # do we enforce invalid address penalty
317 my $enforce = $U->ou_ancestor_setting_value(
318 $patron->home_ou, 'circ.patron_invalid_address_apply_penalty') || 0;
320 my $addrs = $e->search_actor_user_address(
321 {usr => $patron->id, valid => 'f', id => {'>' => 0}}, {idlist => 1});
322 my $addr_count = scalar(@$addrs);
324 if($addr_count == 0 and $addr_penalty) {
326 # regardless of any settings, remove the penalty when the user has no invalid addresses
327 $e->delete_actor_user_standing_penalty($addr_penalty) or return $e->die_event;
330 } elsif($enforce and $addr_count > 0 and !$addr_penalty) {
332 my $ptype = $e->retrieve_config_standing_penalty(29) or return $e->die_event;
333 my $depth = $ptype->org_depth;
334 my $ctx_org = $U->org_unit_ancestor_at_depth($patron->home_ou, $depth) if defined $depth;
335 $ctx_org = $patron->home_ou unless defined $ctx_org;
337 my $penalty = Fieldmapper::actor::user_standing_penalty->new;
338 $penalty->usr($patron->id);
339 $penalty->org_unit($ctx_org);
340 $penalty->standing_penalty(OILS_PENALTY_INVALID_PATRON_ADDRESS);
342 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
356 return new_flesh_user($id, [
359 "standing_penalties",
363 "stat_cat_entries" ], $e );
371 # clone and clear stuff that would break the database
375 my $new_patron = $patron->clone;
377 $new_patron->clear_billing_address();
378 $new_patron->clear_mailing_address();
379 $new_patron->clear_addresses();
380 $new_patron->clear_card();
381 $new_patron->clear_cards();
382 $new_patron->clear_id();
383 $new_patron->clear_isnew();
384 $new_patron->clear_ischanged();
385 $new_patron->clear_isdeleted();
386 $new_patron->clear_stat_cat_entries();
387 $new_patron->clear_permissions();
388 $new_patron->clear_standing_penalties();
398 my $user_obj = shift;
400 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
401 return (undef, $evt) if $evt;
403 my $ex = $session->request(
404 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
406 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
409 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
411 my $id = $session->request(
412 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
413 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
415 $logger->info("Successfully created new user [$id] in DB");
417 return ( $session->request(
418 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
422 sub check_group_perm {
423 my( $session, $requestor, $patron ) = @_;
426 # first let's see if the requestor has
427 # priveleges to update this user in any way
428 if( ! $patron->isnew ) {
429 my $p = $session->request(
430 'open-ils.storage.direct.actor.user.retrieve', $patron->id )->gather(1);
432 # If we are the requestor (trying to update our own account)
433 # and we are not trying to change our profile, we're good
434 if( $p->id == $requestor->id and
435 $p->profile == $patron->profile ) {
440 $evt = group_perm_failed($session, $requestor, $p);
444 # They are allowed to edit this patron.. can they put the
445 # patron into the group requested?
446 $evt = group_perm_failed($session, $requestor, $patron);
452 sub group_perm_failed {
453 my( $session, $requestor, $patron ) = @_;
457 my $grpid = $patron->profile;
461 $logger->debug("user update looking for group perm for group $grpid");
462 $grp = $session->request(
463 'open-ils.storage.direct.permission.grp_tree.retrieve', $grpid )->gather(1);
464 return OpenILS::Event->new('PERMISSION_GRP_TREE_NOT_FOUND') unless $grp;
466 } while( !($perm = $grp->application_perm) and ($grpid = $grp->parent) );
468 $logger->info("user update checking perm $perm on user ".
469 $requestor->id." for update/create on user username=".$patron->usrname);
471 my $evt = $U->check_perms($requestor->id, $patron->home_ou, $perm);
479 my( $session, $patron, $user_obj, $noperm) = @_;
481 $logger->info("Updating patron ".$patron->id." in DB");
486 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
487 return (undef, $evt) if $evt;
490 # update the password by itself to avoid the password protection magic
491 if( $patron->passwd ) {
492 my $s = $session->request(
493 'open-ils.storage.direct.actor.user.remote_update',
494 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
495 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
496 $patron->clear_passwd;
499 if(!$patron->ident_type) {
500 $patron->clear_ident_type;
501 $patron->clear_ident_value;
504 $evt = verify_last_xact($session, $patron);
505 return (undef, $evt) if $evt;
507 my $stat = $session->request(
508 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
509 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
514 sub verify_last_xact {
515 my( $session, $patron ) = @_;
516 return undef unless $patron->id and $patron->id > 0;
517 my $p = $session->request(
518 'open-ils.storage.direct.actor.user.retrieve', $patron->id)->gather(1);
519 my $xact = $p->last_xact_id;
520 return undef unless $xact;
521 $logger->info("user xact = $xact, saving with xact " . $patron->last_xact_id);
522 return OpenILS::Event->new('XACT_COLLISION')
523 if $xact != $patron->last_xact_id;
528 sub _check_dup_ident {
529 my( $session, $patron ) = @_;
531 return undef unless $patron->ident_value;
534 ident_type => $patron->ident_type,
535 ident_value => $patron->ident_value,
538 $logger->debug("patron update searching for dup ident values: " .
539 $patron->ident_type . ':' . $patron->ident_value);
541 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
543 my $dups = $session->request(
544 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
547 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
554 sub _add_update_addresses {
558 my $new_patron = shift;
562 my $current_id; # id of the address before creation
564 for my $address (@{$patron->addresses()}) {
566 next unless ref $address;
567 $current_id = $address->id();
569 if( $patron->billing_address() and
570 $patron->billing_address() == $current_id ) {
571 $logger->info("setting billing addr to $current_id");
572 $new_patron->billing_address($address->id());
573 $new_patron->ischanged(1);
576 if( $patron->mailing_address() and
577 $patron->mailing_address() == $current_id ) {
578 $new_patron->mailing_address($address->id());
579 $logger->info("setting mailing addr to $current_id");
580 $new_patron->ischanged(1);
584 if($address->isnew()) {
586 $address->usr($new_patron->id());
588 ($address, $evt) = _add_address($session,$address);
589 return (undef, $evt) if $evt;
591 # we need to get the new id
592 if( $patron->billing_address() and
593 $patron->billing_address() == $current_id ) {
594 $new_patron->billing_address($address->id());
595 $logger->info("setting billing addr to $current_id");
596 $new_patron->ischanged(1);
599 if( $patron->mailing_address() and
600 $patron->mailing_address() == $current_id ) {
601 $new_patron->mailing_address($address->id());
602 $logger->info("setting mailing addr to $current_id");
603 $new_patron->ischanged(1);
606 } elsif($address->ischanged() ) {
608 ($address, $evt) = _update_address($session, $address);
609 return (undef, $evt) if $evt;
611 } elsif($address->isdeleted() ) {
613 if( $address->id() == $new_patron->mailing_address() ) {
614 $new_patron->clear_mailing_address();
615 ($new_patron, $evt) = _update_patron($session, $new_patron);
616 return (undef, $evt) if $evt;
619 if( $address->id() == $new_patron->billing_address() ) {
620 $new_patron->clear_billing_address();
621 ($new_patron, $evt) = _update_patron($session, $new_patron);
622 return (undef, $evt) if $evt;
625 $evt = _delete_address($session, $address);
626 return (undef, $evt) if $evt;
630 return ( $new_patron, undef );
634 # adds an address to the db and returns the address with new id
636 my($session, $address) = @_;
637 $address->clear_id();
639 $logger->info("Creating new address at street ".$address->street1);
641 # put the address into the database
642 my $id = $session->request(
643 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
644 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
647 return ($address, undef);
651 sub _update_address {
652 my( $session, $address ) = @_;
654 $logger->info("Updating address ".$address->id." in the DB");
656 my $stat = $session->request(
657 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
659 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
660 return ($address, undef);
665 sub _add_update_cards {
669 my $new_patron = shift;
673 my $virtual_id; #id of the card before creation
674 for my $card (@{$patron->cards()}) {
676 $card->usr($new_patron->id());
678 if(ref($card) and $card->isnew()) {
680 $virtual_id = $card->id();
681 ( $card, $evt ) = _add_card($session,$card);
682 return (undef, $evt) if $evt;
684 #if(ref($patron->card)) { $patron->card($patron->card->id); }
685 if($patron->card() == $virtual_id) {
686 $new_patron->card($card->id());
687 $new_patron->ischanged(1);
690 } elsif( ref($card) and $card->ischanged() ) {
691 $evt = _update_card($session, $card);
692 return (undef, $evt) if $evt;
696 return ( $new_patron, undef );
700 # adds an card to the db and returns the card with new id
702 my( $session, $card ) = @_;
705 $logger->info("Adding new patron card ".$card->barcode);
707 my $id = $session->request(
708 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
709 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
710 $logger->info("Successfully created patron card $id");
713 return ( $card, undef );
717 # returns event on error. returns undef otherwise
719 my( $session, $card ) = @_;
720 $logger->info("Updating patron card ".$card->id);
722 my $stat = $session->request(
723 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
724 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
731 # returns event on error. returns undef otherwise
732 sub _delete_address {
733 my( $session, $address ) = @_;
735 $logger->info("Deleting address ".$address->id." from DB");
737 my $stat = $session->request(
738 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
740 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
746 sub _add_survey_responses {
747 my ($session, $patron, $new_patron) = @_;
749 $logger->info( "Updating survey responses for patron ".$new_patron->id );
751 my $responses = $patron->survey_responses;
755 $_->usr($new_patron->id) for (@$responses);
757 my $evt = $U->simplereq( "open-ils.circ",
758 "open-ils.circ.survey.submit.user_id", $responses );
760 return (undef, $evt) if defined($U->event_code($evt));
764 return ( $new_patron, undef );
768 sub _create_stat_maps {
770 my($session, $user_session, $patron, $new_patron) = @_;
772 my $maps = $patron->stat_cat_entries();
774 for my $map (@$maps) {
776 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
778 if ($map->isdeleted()) {
779 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
781 } elsif ($map->isnew()) {
782 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
787 $map->target_usr($new_patron->id);
790 $logger->info("Updating stat entry with method $method and map $map");
792 my $stat = $session->request($method, $map)->gather(1);
793 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
797 return ($new_patron, undef);
800 sub _create_perm_maps {
802 my($session, $user_session, $patron, $new_patron) = @_;
804 my $maps = $patron->permissions;
806 for my $map (@$maps) {
808 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
809 if ($map->isdeleted()) {
810 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
811 } elsif ($map->isnew()) {
812 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
817 $map->usr($new_patron->id);
819 #warn( "Updating permissions with method $method and session $user_session and map $map" );
820 $logger->info( "Updating permissions with method $method and map $map" );
822 my $stat = $session->request($method, $map)->gather(1);
823 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
827 return ($new_patron, undef);
831 __PACKAGE__->register_method(
832 method => "set_user_work_ous",
833 api_name => "open-ils.actor.user.work_ous.update",
836 sub set_user_work_ous {
842 my( $requestor, $evt ) = $apputils->checksesperm( $ses, 'ASSIGN_WORK_ORG_UNIT' );
845 my $session = $apputils->start_db_session();
847 for my $map (@$maps) {
849 my $method = "open-ils.storage.direct.permission.usr_work_ou_map.update";
850 if ($map->isdeleted()) {
851 $method = "open-ils.storage.direct.permission.usr_work_ou_map.delete";
852 } elsif ($map->isnew()) {
853 $method = "open-ils.storage.direct.permission.usr_work_ou_map.create";
857 #warn( "Updating permissions with method $method and session $ses and map $map" );
858 $logger->info( "Updating work_ou map with method $method and map $map" );
860 my $stat = $session->request($method, $map)->gather(1);
861 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
865 $apputils->commit_db_session($session);
867 return scalar(@$maps);
871 __PACKAGE__->register_method(
872 method => "set_user_perms",
873 api_name => "open-ils.actor.user.permissions.update",
882 my $session = $apputils->start_db_session();
884 my( $user_obj, $evt ) = $U->checkses($ses);
887 my $perms = $session->request('open-ils.storage.permission.user_perms.atomic', $user_obj->id)->gather(1);
890 $all = 1 if ($U->is_true($user_obj->super_user()));
891 $all = 1 unless ($U->check_perms($user_obj->id, $user_obj->home_ou, 'EVERYTHING'));
893 for my $map (@$maps) {
895 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
896 if ($map->isdeleted()) {
897 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
898 } elsif ($map->isnew()) {
899 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
903 next if (!$all and !grep { $_->perm eq $map->perm and $U->is_true($_->grantable) and $_->depth <= $map->depth } @$perms);
904 #warn( "Updating permissions with method $method and session $ses and map $map" );
905 $logger->info( "Updating permissions with method $method and map $map" );
907 my $stat = $session->request($method, $map)->gather(1);
908 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
912 $apputils->commit_db_session($session);
914 return scalar(@$maps);
918 __PACKAGE__->register_method(
919 method => "user_retrieve_by_barcode",
921 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
923 sub user_retrieve_by_barcode {
924 my($self, $client, $user_session, $barcode) = @_;
926 $logger->debug("Searching for user with barcode $barcode");
927 my ($user_obj, $evt) = $apputils->checkses($user_session);
930 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
932 "open-ils.cstore.direct.actor.card.search.atomic",
933 { barcode => $barcode }
936 if(!$card || !$card->[0]) {
937 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
941 my $user = flesh_user($card->usr(), new_editor(requestor => $user_obj));
943 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
946 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
953 __PACKAGE__->register_method(
954 method => "get_user_by_id",
956 api_name => "open-ils.actor.user.retrieve",);
959 my ($self, $client, $auth, $id) = @_;
960 my $e = new_editor(authtoken=>$auth);
961 return $e->event unless $e->checkauth;
962 my $user = $e->retrieve_actor_user($id)
964 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
970 __PACKAGE__->register_method(
971 method => "get_org_types",
972 api_name => "open-ils.actor.org_types.retrieve",);
975 return $U->get_org_types();
980 __PACKAGE__->register_method(
981 method => "get_user_ident_types",
982 api_name => "open-ils.actor.user.ident_types.retrieve",
985 sub get_user_ident_types {
986 return $ident_types if $ident_types;
987 return $ident_types =
988 new_editor()->retrieve_all_config_identification_type();
994 __PACKAGE__->register_method(
995 method => "get_org_unit",
996 api_name => "open-ils.actor.org_unit.retrieve",
1000 my( $self, $client, $user_session, $org_id ) = @_;
1001 my $e = new_editor(authtoken => $user_session);
1003 return $e->event unless $e->checkauth;
1004 $org_id = $e->requestor->ws_ou;
1006 my $o = $e->retrieve_actor_org_unit($org_id)
1007 or return $e->event;
1011 __PACKAGE__->register_method(
1012 method => "search_org_unit",
1013 api_name => "open-ils.actor.org_unit_list.search",
1016 sub search_org_unit {
1018 my( $self, $client, $field, $value ) = @_;
1020 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
1022 "open-ils.cstore.direct.actor.org_unit.search.atomic",
1023 { $field => $value } );
1029 # build the org tree
1031 __PACKAGE__->register_method(
1032 method => "get_org_tree",
1033 api_name => "open-ils.actor.org_tree.retrieve",
1035 note => "Returns the entire org tree structure",
1041 return $U->get_org_tree($client->session->session_locale);
1045 __PACKAGE__->register_method(
1046 method => "get_org_descendants",
1047 api_name => "open-ils.actor.org_tree.descendants.retrieve"
1050 # depth is optional. org_unit is the id
1051 sub get_org_descendants {
1052 my( $self, $client, $org_unit, $depth ) = @_;
1054 if(ref $org_unit eq 'ARRAY') {
1057 for my $i (0..scalar(@$org_unit)-1) {
1058 my $list = $U->simple_scalar_request(
1060 "open-ils.storage.actor.org_unit.descendants.atomic",
1061 $org_unit->[$i], $depth->[$i] );
1062 push(@trees, $U->build_org_tree($list));
1067 my $orglist = $apputils->simple_scalar_request(
1069 "open-ils.storage.actor.org_unit.descendants.atomic",
1070 $org_unit, $depth );
1071 return $U->build_org_tree($orglist);
1076 __PACKAGE__->register_method(
1077 method => "get_org_ancestors",
1078 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
1081 # depth is optional. org_unit is the id
1082 sub get_org_ancestors {
1083 my( $self, $client, $org_unit, $depth ) = @_;
1084 my $orglist = $apputils->simple_scalar_request(
1086 "open-ils.storage.actor.org_unit.ancestors.atomic",
1087 $org_unit, $depth );
1088 return $U->build_org_tree($orglist);
1092 __PACKAGE__->register_method(
1093 method => "get_standings",
1094 api_name => "open-ils.actor.standings.retrieve"
1099 return $user_standings if $user_standings;
1100 return $user_standings =
1101 $apputils->simple_scalar_request(
1103 "open-ils.cstore.direct.config.standing.search.atomic",
1104 { id => { "!=" => undef } }
1110 __PACKAGE__->register_method(
1111 method => "get_my_org_path",
1112 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1115 sub get_my_org_path {
1116 my( $self, $client, $auth, $org_id ) = @_;
1117 my $e = new_editor(authtoken=>$auth);
1118 return $e->event unless $e->checkauth;
1119 $org_id = $e->requestor->ws_ou unless defined $org_id;
1121 return $apputils->simple_scalar_request(
1123 "open-ils.storage.actor.org_unit.full_path.atomic",
1128 __PACKAGE__->register_method(
1129 method => "patron_adv_search",
1130 api_name => "open-ils.actor.patron.search.advanced" );
1131 sub patron_adv_search {
1132 my( $self, $client, $auth, $search_hash,
1133 $search_limit, $search_sort, $include_inactive, $search_depth ) = @_;
1135 my $e = new_editor(authtoken=>$auth);
1136 return $e->event unless $e->checkauth;
1137 return $e->event unless $e->allowed('VIEW_USER');
1138 return $U->storagereq(
1139 "open-ils.storage.actor.user.crazy_search", $search_hash,
1140 $search_limit, $search_sort, $include_inactive, $e->requestor->ws_ou, $search_depth);
1144 __PACKAGE__->register_method(
1145 method => "update_passwd",
1147 api_name => "open-ils.actor.user.password.update");
1149 __PACKAGE__->register_method(
1150 method => "update_passwd",
1151 api_name => "open-ils.actor.user.username.update");
1153 __PACKAGE__->register_method(
1154 method => "update_passwd",
1155 api_name => "open-ils.actor.user.email.update");
1158 my( $self, $conn, $auth, $new_val, $orig_pw ) = @_;
1159 my $e = new_editor(xact=>1, authtoken=>$auth);
1160 return $e->die_event unless $e->checkauth;
1162 my $db_user = $e->retrieve_actor_user($e->requestor->id)
1163 or return $e->die_event;
1164 my $api = $self->api_name;
1166 if( $api =~ /password/o ) {
1168 # make sure the original password matches the in-database password
1169 return OpenILS::Event->new('INCORRECT_PASSWORD')
1170 if md5_hex($orig_pw) ne $db_user->passwd;
1171 $db_user->passwd($new_val);
1175 # if we don't clear the password, the user will be updated with
1176 # a hashed version of the hashed version of their password
1177 $db_user->clear_passwd;
1179 if( $api =~ /username/o ) {
1181 # make sure no one else has this username
1182 my $exist = $e->search_actor_user({usrname=>$new_val},{idlist=>1});
1183 return OpenILS::Event->new('USERNAME_EXISTS') if @$exist;
1184 $db_user->usrname($new_val);
1186 } elsif( $api =~ /email/o ) {
1187 $db_user->email($new_val);
1191 $e->update_actor_user($db_user) or return $e->die_event;
1199 __PACKAGE__->register_method(
1200 method => "check_user_perms",
1201 api_name => "open-ils.actor.user.perm.check",
1202 notes => <<" NOTES");
1203 Takes a login session, user id, an org id, and an array of perm type strings. For each
1204 perm type, if the user does *not* have the given permission it is added
1205 to a list which is returned from the method. If all permissions
1206 are allowed, an empty list is returned
1207 if the logged in user does not match 'user_id', then the logged in user must
1208 have VIEW_PERMISSION priveleges.
1211 sub check_user_perms {
1212 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1214 my( $staff, $evt ) = $apputils->checkses($login_session);
1215 return $evt if $evt;
1217 if($staff->id ne $user_id) {
1218 if( $evt = $apputils->check_perms(
1219 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1225 for my $perm (@$perm_types) {
1226 if($apputils->check_perms($user_id, $org_id, $perm)) {
1227 push @not_allowed, $perm;
1231 return \@not_allowed
1234 __PACKAGE__->register_method(
1235 method => "check_user_perms2",
1236 api_name => "open-ils.actor.user.perm.check.multi_org",
1238 Checks the permissions on a list of perms and orgs for a user
1239 @param authtoken The login session key
1240 @param user_id The id of the user to check
1241 @param orgs The array of org ids
1242 @param perms The array of permission names
1243 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1244 if the logged in user does not match 'user_id', then the logged in user must
1245 have VIEW_PERMISSION priveleges.
1248 sub check_user_perms2 {
1249 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1251 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1252 $authtoken, $user_id, 'VIEW_PERMISSION' );
1253 return $evt if $evt;
1256 for my $org (@$orgs) {
1257 for my $perm (@$perms) {
1258 if($apputils->check_perms($user_id, $org, $perm)) {
1259 push @not_allowed, [ $org, $perm ];
1264 return \@not_allowed
1268 __PACKAGE__->register_method(
1269 method => 'check_user_perms3',
1270 api_name => 'open-ils.actor.user.perm.highest_org',
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 perm The permission to check
1278 @return The org unit highest in the org tree within which the user has
1279 the requested permission
1282 sub check_user_perms3 {
1283 my($self, $client, $authtoken, $user_id, $perm) = @_;
1284 my $e = new_editor(authtoken=>$authtoken);
1285 return $e->event unless $e->checkauth;
1287 my $tree = $U->get_org_tree();
1289 unless($e->requestor->id == $user_id) {
1290 my $user = $e->retrieve_actor_user($user_id)
1291 or return $e->event;
1292 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1293 return $U->find_highest_perm_org($perm, $user_id, $user->home_ou, $tree );
1296 return $U->find_highest_perm_org($perm, $user_id, $e->requestor->ws_ou, $tree);
1299 __PACKAGE__->register_method(
1300 method => 'user_has_work_perm_at',
1301 api_name => 'open-ils.actor.user.has_work_perm_at',
1305 Returns a set of org unit IDs which represent the highest orgs in
1306 the org tree where the user has the requested permission. The
1307 purpose of this method is to return the smallest set of org units
1308 which represent the full expanse of the user's ability to perform
1309 the requested action. The user whose perms this method should
1310 check is implied by the authtoken. /,
1312 {desc => 'authtoken', type => 'string'},
1313 {desc => 'permission name', type => 'string'},
1315 return => {desc => 'An array of org IDs'}
1319 sub user_has_work_perm_at {
1320 my($self, $conn, $auth, $perm) = @_;
1321 my $e = new_editor(authtoken=>$auth);
1322 return $e->event unless $e->checkauth;
1323 return $U->user_has_work_perm_at($e, $perm);
1326 __PACKAGE__->register_method(
1327 method => 'user_has_work_perm_at_batch',
1328 api_name => 'open-ils.actor.user.has_work_perm_at.batch',
1332 sub user_has_work_perm_at_batch {
1333 my($self, $conn, $auth, $perms) = @_;
1334 my $e = new_editor(authtoken=>$auth);
1335 return $e->event unless $e->checkauth;
1337 $map->{$_} = $U->user_has_work_perm_at($e, $_) for @$perms;
1343 __PACKAGE__->register_method(
1344 method => 'check_user_perms4',
1345 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1347 Returns the highest org unit id at which a user has a given permission
1348 If the requestor does not match the target user, the requestor must have
1349 'VIEW_PERMISSION' rights at the home org unit of the target user
1350 @param authtoken The login session key
1351 @param userid The id of the user in question
1352 @param perms An array of perm names to check
1353 @return An array of orgId's representing the org unit
1354 highest in the org tree within which the user has the requested permission
1355 The arrah of orgId's has matches the order of the perms array
1358 sub check_user_perms4 {
1359 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1361 my( $staff, $target, $org, $evt );
1363 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1364 $authtoken, $userid, 'VIEW_PERMISSION' );
1365 return $evt if $evt;
1368 return [] unless ref($perms);
1369 my $tree = $U->get_org_tree();
1371 for my $p (@$perms) {
1372 push( @arr, $U->find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1380 __PACKAGE__->register_method(
1381 method => "user_fines_summary",
1382 api_name => "open-ils.actor.user.fines.summary",
1384 notes => <<" NOTES");
1385 Returns a short summary of the users total open fines, excluding voided fines
1386 Params are login_session, user_id
1387 Returns a 'mous' object.
1390 sub user_fines_summary {
1391 my( $self, $client, $auth, $user_id ) = @_;
1392 my $e = new_editor(authtoken=>$auth);
1393 return $e->event unless $e->checkauth;
1394 my $user = $e->retrieve_actor_user($user_id)
1395 or return $e->event;
1397 if( $user_id ne $e->requestor->id ) {
1398 return $e->event unless
1399 $e->allowed('VIEW_USER_FINES_SUMMARY', $user->home_ou);
1402 # run this inside a transaction to prevent replication delay errors
1403 my $ses = $U->start_db_session();
1404 my $s = $ses->request(
1405 'open-ils.storage.money.open_user_summary.search', $user_id )->gather(1);
1406 $U->rollback_db_session($ses);
1413 __PACKAGE__->register_method(
1414 method => "user_transactions",
1415 api_name => "open-ils.actor.user.transactions",
1416 notes => <<" NOTES");
1417 Returns a list of open user transactions (mbts objects);
1418 Params are login_session, user_id
1419 Optional third parameter is the transactions type. defaults to all
1422 __PACKAGE__->register_method(
1423 method => "user_transactions",
1424 api_name => "open-ils.actor.user.transactions.have_charge",
1425 notes => <<" NOTES");
1426 Returns a list of all open user transactions (mbts objects) that have an initial charge
1427 Params are login_session, user_id
1428 Optional third parameter is the transactions type. defaults to all
1431 __PACKAGE__->register_method(
1432 method => "user_transactions",
1433 api_name => "open-ils.actor.user.transactions.have_balance",
1435 notes => <<" NOTES");
1436 Returns a list of all open user transactions (mbts objects) that have a balance
1437 Params are login_session, user_id
1438 Optional third parameter is the transactions type. defaults to all
1441 __PACKAGE__->register_method(
1442 method => "user_transactions",
1443 api_name => "open-ils.actor.user.transactions.fleshed",
1444 notes => <<" NOTES");
1445 Returns an object/hash of transaction, circ, title where transaction = an open
1446 user transactions (mbts objects), circ is the attached circluation, and title
1447 is the title the circ points to
1448 Params are login_session, user_id
1449 Optional third parameter is the transactions type. defaults to all
1452 __PACKAGE__->register_method(
1453 method => "user_transactions",
1454 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1455 notes => <<" NOTES");
1456 Returns an object/hash of transaction, circ, title where transaction = an open
1457 user transactions that has an initial charge (mbts objects), circ is the
1458 attached circluation, and title is the title the circ points to
1459 Params are login_session, user_id
1460 Optional third parameter is the transactions type. defaults to all
1463 __PACKAGE__->register_method(
1464 method => "user_transactions",
1465 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1467 notes => <<" NOTES");
1468 Returns an object/hash of transaction, circ, title where transaction = an open
1469 user transaction that has a balance (mbts objects), circ is the attached
1470 circluation, and title is the title the circ points to
1471 Params are login_session, user_id
1472 Optional third parameter is the transaction type. defaults to all
1475 __PACKAGE__->register_method(
1476 method => "user_transactions",
1477 api_name => "open-ils.actor.user.transactions.count",
1478 notes => <<" NOTES");
1479 Returns an object/hash of transaction, circ, title where transaction = an open
1480 user transactions (mbts objects), circ is the attached circluation, and title
1481 is the title the circ points to
1482 Params are login_session, user_id
1483 Optional third parameter is the transactions type. defaults to all
1486 __PACKAGE__->register_method(
1487 method => "user_transactions",
1488 api_name => "open-ils.actor.user.transactions.have_charge.count",
1489 notes => <<" NOTES");
1490 Returns an object/hash of transaction, circ, title where transaction = an open
1491 user transactions that has an initial charge (mbts objects), circ is the
1492 attached circluation, and title is the title the circ points to
1493 Params are login_session, user_id
1494 Optional third parameter is the transactions type. defaults to all
1497 __PACKAGE__->register_method(
1498 method => "user_transactions",
1499 api_name => "open-ils.actor.user.transactions.have_balance.count",
1501 notes => <<" NOTES");
1502 Returns an object/hash of transaction, circ, title where transaction = an open
1503 user transaction that has a balance (mbts objects), circ is the attached
1504 circluation, and title is the title the circ points to
1505 Params are login_session, user_id
1506 Optional third parameter is the transaction type. defaults to all
1509 __PACKAGE__->register_method(
1510 method => "user_transactions",
1511 api_name => "open-ils.actor.user.transactions.have_balance.total",
1513 notes => <<" NOTES");
1514 Returns an object/hash of transaction, circ, title where transaction = an open
1515 user transaction that has a balance (mbts objects), circ is the attached
1516 circluation, and title is the title the circ points to
1517 Params are login_session, user_id
1518 Optional third parameter is the transaction type. defaults to all
1523 sub user_transactions {
1524 my( $self, $client, $login_session, $user_id, $type ) = @_;
1526 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1527 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1528 return $evt if $evt;
1530 my $api = $self->api_name();
1534 if(defined($type)) { @xact = (xact_type => $type);
1536 } else { @xact = (); }
1539 ->method_lookup('open-ils.actor.user.transactions.history.still_open')
1540 ->run($login_session => $user_id => $type);
1542 if($api =~ /have_charge/o) {
1544 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1546 } elsif($api =~ /have_balance/o) {
1548 $trans = [ grep { int($_->balance_owed * 100) != 0 } @$trans ];
1551 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1555 if($api =~ /total/o) {
1557 for my $t (@$trans) {
1558 $total += $t->balance_owed;
1561 $logger->debug("Total balance owed by user $user_id: $total");
1565 if($api =~ /count/o) { return scalar @$trans; }
1566 if($api !~ /fleshed/o) { return $trans; }
1569 for my $t (@$trans) {
1571 if( $t->xact_type ne 'circulation' ) {
1572 push @resp, {transaction => $t};
1576 my $circ = $apputils->simple_scalar_request(
1578 "open-ils.cstore.direct.action.circulation.retrieve",
1583 my $title = $apputils->simple_scalar_request(
1585 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1586 $circ->target_copy );
1590 my $u = OpenILS::Utils::ModsParser->new();
1591 $u->start_mods_batch($title->marc());
1592 my $mods = $u->finish_mods_batch();
1593 $mods->doc_id($title->id) if $mods;
1595 push @resp, {transaction => $t, circ => $circ, record => $mods };
1603 __PACKAGE__->register_method(
1604 method => "user_transaction_retrieve",
1605 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1607 notes => <<" NOTES");
1608 Returns a fleshedtransaction record
1610 __PACKAGE__->register_method(
1611 method => "user_transaction_retrieve",
1612 api_name => "open-ils.actor.user.transaction.retrieve",
1614 notes => <<" NOTES");
1615 Returns a transaction record
1617 sub user_transaction_retrieve {
1618 my( $self, $client, $login_session, $bill_id ) = @_;
1620 # XXX I think I'm deprecated... make sure
1622 my $trans = $apputils->simple_scalar_request(
1624 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1628 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1629 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1630 return $evt if $evt;
1632 my $api = $self->api_name();
1633 if($api !~ /fleshed/o) { return $trans; }
1635 if( $trans->xact_type ne 'circulation' ) {
1636 $logger->debug("Returning non-circ transaction");
1637 return {transaction => $trans};
1640 my $circ = $apputils->simple_scalar_request(
1642 "open-ils..direct.action.circulation.retrieve",
1645 return {transaction => $trans} unless $circ;
1646 $logger->debug("Found the circ transaction");
1648 my $title = $apputils->simple_scalar_request(
1650 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1651 $circ->target_copy );
1653 return {transaction => $trans, circ => $circ } unless $title;
1654 $logger->debug("Found the circ title");
1658 my $u = OpenILS::Utils::ModsParser->new();
1659 $u->start_mods_batch($title->marc());
1660 $mods = $u->finish_mods_batch();
1662 if ($title->id == OILS_PRECAT_RECORD) {
1663 my $copy = $apputils->simple_scalar_request(
1665 "open-ils.cstore.direct.asset.copy.retrieve",
1666 $circ->target_copy );
1668 $mods = new Fieldmapper::metabib::virtual_record;
1669 $mods->doc_id(OILS_PRECAT_RECORD);
1670 $mods->title($copy->dummy_title);
1671 $mods->author($copy->dummy_author);
1675 $logger->debug("MODSized the circ title");
1677 return {transaction => $trans, circ => $circ, record => $mods };
1681 __PACKAGE__->register_method(
1682 method => "hold_request_count",
1683 api_name => "open-ils.actor.user.hold_requests.count",
1686 notes => <<" NOTES");
1687 Returns hold ready/total counts
1689 sub hold_request_count {
1690 my( $self, $client, $login_session, $userid ) = @_;
1692 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1693 $login_session, $userid, 'VIEW_HOLD' );
1694 return $evt if $evt;
1697 my $holds = $apputils->simple_scalar_request(
1699 "open-ils.cstore.direct.action.hold_request.search.atomic",
1702 fulfillment_time => {"=" => undef },
1703 cancel_time => undef,
1708 for my $h (@$holds) {
1709 next unless $h->capture_time and $h->current_copy;
1711 my $copy = $apputils->simple_scalar_request(
1713 "open-ils.cstore.direct.asset.copy.retrieve",
1717 if ($copy and $copy->status == 8) {
1722 return { total => scalar(@$holds), ready => scalar(@ready) };
1726 __PACKAGE__->register_method(
1727 method => "checkedout_count",
1728 api_name => "open-ils.actor.user.checked_out.count__",
1730 notes => <<" NOTES");
1731 Returns a transaction record
1735 sub checkedout_count {
1736 my( $self, $client, $login_session, $userid ) = @_;
1738 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1739 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1740 return $evt if $evt;
1742 my $circs = $apputils->simple_scalar_request(
1744 "open-ils.cstore.direct.action.circulation.search.atomic",
1745 { usr => $userid, stop_fines => undef }
1746 #{ usr => $userid, checkin_time => {"=" => undef } }
1749 my $parser = DateTime::Format::ISO8601->new;
1752 for my $c (@$circs) {
1753 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1754 my $due = $due_dt->epoch;
1756 if ($due < DateTime->today->epoch) {
1761 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1765 __PACKAGE__->register_method(
1766 method => "checked_out",
1767 api_name => "open-ils.actor.user.checked_out",
1771 Returns a structure of circulations objects sorted by
1772 out, overdue, lost, claims_returned, long_overdue.
1773 A list of IDs are returned of each type.
1774 lost, long_overdue, and claims_returned circ will not
1775 be "finished" (there is an outstanding balance or some
1776 other pending action on the circ).
1778 The .count method also includes a 'total' field which
1779 sums all "open" circs
1783 __PACKAGE__->register_method(
1784 method => "checked_out",
1785 api_name => "open-ils.actor.user.checked_out.count",
1788 signature => q/@see open-ils.actor.user.checked_out/
1792 my( $self, $conn, $auth, $userid ) = @_;
1794 my $e = new_editor(authtoken=>$auth);
1795 return $e->event unless $e->checkauth;
1797 if( $userid ne $e->requestor->id ) {
1798 my $user = $e->retrieve_actor_user($userid) or return $e->event;
1799 unless($e->allowed('VIEW_CIRCULATIONS', $user->home_ou)) {
1801 # see if there is a friend link allowing circ.view perms
1802 my $allowed = OpenILS::Application::Actor::Friends->friend_perm_allowed(
1803 $e, $userid, $e->requestor->id, 'circ.view');
1804 return $e->event unless $allowed;
1808 my $count = $self->api_name =~ /count/;
1809 return _checked_out( $count, $e, $userid );
1813 my( $iscount, $e, $userid ) = @_;
1814 my $meth = 'open-ils.storage.actor.user.checked_out';
1815 $meth = "$meth.count" if $iscount;
1816 return $U->storagereq($meth, $userid);
1820 sub _checked_out_WHAT {
1821 my( $iscount, $e, $userid ) = @_;
1823 my $circs = $e->search_action_circulation(
1824 { usr => $userid, stop_fines => undef });
1826 my $mcircs = $e->search_action_circulation(
1829 checkin_time => undef,
1830 xact_finish => undef,
1834 push( @$circs, @$mcircs );
1836 my $parser = DateTime::Format::ISO8601->new;
1838 # split the circs up into overdue and not-overdue circs
1840 for my $c (@$circs) {
1841 if( $c->due_date ) {
1842 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1843 my $due = $due_dt->epoch;
1844 if ($due < DateTime->today->epoch) {
1845 push @overdue, $c->id;
1854 # grab all of the lost, claims-returned, and longoverdue circs
1855 #my $open = $e->search_action_circulation(
1856 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1859 # these items have stop_fines, but no xact_finish, so money
1860 # is owed on them and they have not been checked in
1861 my $open = $e->search_action_circulation(
1864 stop_fines => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] },
1865 xact_finish => undef,
1866 checkin_time => undef,
1871 my( @lost, @cr, @lo );
1872 for my $c (@$open) {
1873 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1874 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1875 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1881 total => @$circs + @lost + @cr + @lo,
1882 out => scalar(@out),
1883 overdue => scalar(@overdue),
1884 lost => scalar(@lost),
1885 claims_returned => scalar(@cr),
1886 long_overdue => scalar(@lo)
1892 overdue => \@overdue,
1894 claims_returned => \@cr,
1895 long_overdue => \@lo
1901 __PACKAGE__->register_method(
1902 method => "checked_in_with_fines",
1903 api_name => "open-ils.actor.user.checked_in_with_fines",
1906 signature => q/@see open-ils.actor.user.checked_out/
1908 sub checked_in_with_fines {
1909 my( $self, $conn, $auth, $userid ) = @_;
1911 my $e = new_editor(authtoken=>$auth);
1912 return $e->event unless $e->checkauth;
1914 if( $userid ne $e->requestor->id ) {
1915 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1918 # money is owed on these items and they are checked in
1919 my $open = $e->search_action_circulation(
1922 xact_finish => undef,
1923 checkin_time => { "!=" => undef },
1928 my( @lost, @cr, @lo );
1929 for my $c (@$open) {
1930 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1931 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1932 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1937 claims_returned => \@cr,
1938 long_overdue => \@lo
1950 __PACKAGE__->register_method(
1951 method => "user_transaction_history",
1952 api_name => "open-ils.actor.user.transactions.history",
1954 notes => <<" NOTES");
1955 Returns a list of billable transactions for a user, optionally by type
1957 __PACKAGE__->register_method(
1958 method => "user_transaction_history",
1959 api_name => "open-ils.actor.user.transactions.history.have_charge",
1961 notes => <<" NOTES");
1962 Returns a list of billable transactions for a user that have an initial charge, optionally by type
1964 __PACKAGE__->register_method(
1965 method => "user_transaction_history",
1966 api_name => "open-ils.actor.user.transactions.history.have_balance",
1969 notes => <<" NOTES");
1970 Returns a list of billable transactions for a user that have a balance, optionally by type
1972 __PACKAGE__->register_method(
1973 method => "user_transaction_history",
1974 api_name => "open-ils.actor.user.transactions.history.still_open",
1976 notes => <<" NOTES");
1977 Returns a list of billable transactions for a user that are not finished
1979 __PACKAGE__->register_method(
1980 method => "user_transaction_history",
1981 api_name => "open-ils.actor.user.transactions.history.have_bill",
1984 notes => <<" NOTES");
1985 Returns a list of billable transactions for a user that has billings
1987 __PACKAGE__->register_method(
1988 method => "user_transaction_history",
1989 api_name => "open-ils.actor.user.transactions.history.ids",
1991 notes => <<" NOTES");
1992 Returns a list of billable transaction ids for a user, optionally by type
1994 __PACKAGE__->register_method(
1995 method => "user_transaction_history",
1996 api_name => "open-ils.actor.user.transactions.history.have_charge.ids",
1998 notes => <<" NOTES");
1999 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
2001 __PACKAGE__->register_method(
2002 method => "user_transaction_history",
2003 api_name => "open-ils.actor.user.transactions.history.have_balance.ids",
2006 notes => <<" NOTES");
2007 Returns a list of billable transaction ids for a user that have a balance, optionally by type
2009 __PACKAGE__->register_method(
2010 method => "user_transaction_history",
2011 api_name => "open-ils.actor.user.transactions.history.still_open.ids",
2013 notes => <<" NOTES");
2014 Returns a list of billable transaction ids for a user that are not finished
2016 __PACKAGE__->register_method(
2017 method => "user_transaction_history",
2018 api_name => "open-ils.actor.user.transactions.history.have_bill.ids",
2021 notes => <<" NOTES");
2022 Returns a list of billable transaction ids for a user that has billings
2026 sub user_transaction_history {
2027 my( $self, $conn, $auth, $userid, $type ) = @_;
2029 # run inside of a transaction to prevent replication delays
2030 my $e = new_editor(xact=>1, authtoken=>$auth);
2031 return $e->die_event unless $e->checkauth;
2033 if( $e->requestor->id ne $userid ) {
2034 return $e->die_event
2035 unless $e->allowed('VIEW_USER_TRANSACTIONS');
2038 my $api = $self->api_name;
2039 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
2041 my @xacts = @{ $e->search_money_billable_transaction(
2042 [ { usr => $userid, @xact_finish },
2044 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
2045 order_by => { mbt => 'xact_start DESC' },
2053 my @mbts = $U->make_mbts( $e, @xacts );
2055 if(defined($type)) {
2056 @mbts = grep { $_->xact_type eq $type } @mbts;
2059 if($api =~ /have_balance/o) {
2060 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
2063 if($api =~ /have_charge/o) {
2064 @mbts = grep { defined($_->last_billing_ts) } @mbts;
2067 if($api =~ /have_bill/o) {
2068 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
2071 if ($api =~ /\.ids/) {
2072 return [map {$_->id} @mbts];
2080 __PACKAGE__->register_method(
2081 method => "user_perms",
2082 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2084 notes => <<" NOTES");
2085 Returns a list of permissions
2088 my( $self, $client, $authtoken, $user ) = @_;
2090 my( $staff, $evt ) = $apputils->checkses($authtoken);
2091 return $evt if $evt;
2093 $user ||= $staff->id;
2095 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2099 return $apputils->simple_scalar_request(
2101 "open-ils.storage.permission.user_perms.atomic",
2105 __PACKAGE__->register_method(
2106 method => "retrieve_perms",
2107 api_name => "open-ils.actor.permissions.retrieve",
2108 notes => <<" NOTES");
2109 Returns a list of permissions
2111 sub retrieve_perms {
2112 my( $self, $client ) = @_;
2113 return $apputils->simple_scalar_request(
2115 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2116 { id => { '!=' => undef } }
2120 __PACKAGE__->register_method(
2121 method => "retrieve_groups",
2122 api_name => "open-ils.actor.groups.retrieve",
2123 notes => <<" NOTES");
2124 Returns a list of user groupss
2126 sub retrieve_groups {
2127 my( $self, $client ) = @_;
2128 return new_editor()->retrieve_all_permission_grp_tree();
2131 __PACKAGE__->register_method(
2132 method => "retrieve_org_address",
2133 api_name => "open-ils.actor.org_unit.address.retrieve",
2134 notes => <<' NOTES');
2135 Returns an org_unit address by ID
2136 @param An org_address ID
2138 sub retrieve_org_address {
2139 my( $self, $client, $id ) = @_;
2140 return $apputils->simple_scalar_request(
2142 "open-ils.cstore.direct.actor.org_address.retrieve",
2147 __PACKAGE__->register_method(
2148 method => "retrieve_groups_tree",
2149 api_name => "open-ils.actor.groups.tree.retrieve",
2150 notes => <<" NOTES");
2151 Returns a list of user groups
2153 sub retrieve_groups_tree {
2154 my( $self, $client ) = @_;
2155 return new_editor()->search_permission_grp_tree(
2160 flesh_fields => { pgt => ["children"] },
2161 order_by => { pgt => 'name'}
2168 __PACKAGE__->register_method(
2169 method => "add_user_to_groups",
2170 api_name => "open-ils.actor.user.set_groups",
2171 notes => <<" NOTES");
2172 Adds a user to one or more permission groups
2175 sub add_user_to_groups {
2176 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2178 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2179 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2180 return $evt if $evt;
2182 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2183 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2184 return $evt if $evt;
2186 $apputils->simplereq(
2188 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2190 for my $group (@$groups) {
2191 my $link = Fieldmapper::permission::usr_grp_map->new;
2193 $link->usr($userid);
2195 my $id = $apputils->simplereq(
2197 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2203 __PACKAGE__->register_method(
2204 method => "get_user_perm_groups",
2205 api_name => "open-ils.actor.user.get_groups",
2206 notes => <<" NOTES");
2207 Retrieve a user's permission groups.
2211 sub get_user_perm_groups {
2212 my( $self, $client, $authtoken, $userid ) = @_;
2214 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2215 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2216 return $evt if $evt;
2218 return $apputils->simplereq(
2220 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2224 __PACKAGE__->register_method(
2225 method => "get_user_work_ous",
2226 api_name => "open-ils.actor.user.get_work_ous",
2227 notes => <<" NOTES");
2228 Retrieve a user's work org units.
2230 __PACKAGE__->register_method(
2231 method => "get_user_work_ous",
2232 api_name => "open-ils.actor.user.get_work_ous.ids",
2233 notes => <<" NOTES");
2234 Retrieve a user's work org units.
2238 sub get_user_work_ous {
2239 my( $self, $client, $auth, $userid ) = @_;
2240 my $e = new_editor(authtoken=>$auth);
2241 return $e->event unless $e->checkauth;
2242 $userid ||= $e->requestor->id;
2244 if($e->requestor->id != $userid) {
2245 my $user = $e->retrieve_actor_user($userid)
2246 or return $e->event;
2247 return $e->event unless $e->allowed('ASSIGN_WORK_ORG_UNIT', $user->home_ou);
2250 return $e->search_permission_usr_work_ou_map({usr => $userid})
2251 unless $self->api_name =~ /.ids$/;
2253 # client just wants a list of org IDs
2254 return $U->get_user_work_ou_ids($e, $userid);
2260 __PACKAGE__->register_method (
2261 method => 'register_workstation',
2262 api_name => 'open-ils.actor.workstation.register.override',
2263 signature => q/@see open-ils.actor.workstation.register/);
2265 __PACKAGE__->register_method (
2266 method => 'register_workstation',
2267 api_name => 'open-ils.actor.workstation.register',
2269 Registers a new workstion in the system
2270 @param authtoken The login session key
2271 @param name The name of the workstation id
2272 @param owner The org unit that owns this workstation
2273 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2274 if the name is already in use.
2277 sub register_workstation {
2278 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2280 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2281 return $e->die_event unless $e->checkauth;
2282 return $e->die_event unless $e->allowed('REGISTER_WORKSTATION', $owner);
2283 my $existing = $e->search_actor_workstation({name => $name})->[0];
2287 if( $self->api_name =~ /override/o ) {
2288 # workstation with the given name exists.
2290 if($owner ne $existing->owning_lib) {
2291 # if necessary, update the owning_lib of the workstation
2293 $logger->info("changing owning lib of workstation ".$existing->id.
2294 " from ".$existing->owning_lib." to $owner");
2295 return $e->die_event unless
2296 $e->allowed('UPDATE_WORKSTATION', $existing->owning_lib);
2298 return $e->die_event unless $e->allowed('UPDATE_WORKSTATION', $owner);
2300 $existing->owning_lib($owner);
2301 return $e->die_event unless $e->update_actor_workstation($existing);
2307 "attempt to register an existing workstation. returning existing ID");
2310 return $existing->id;
2313 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2317 my $ws = Fieldmapper::actor::workstation->new;
2318 $ws->owning_lib($owner);
2320 $e->create_actor_workstation($ws) or return $e->die_event;
2322 return $ws->id; # note: editor sets the id on the new object for us
2325 __PACKAGE__->register_method (
2326 method => 'workstation_list',
2327 api_name => 'open-ils.actor.workstation.list',
2329 Returns a list of workstations registered at the given location
2330 @param authtoken The login session key
2331 @param ids A list of org_unit.id's for the workstation owners
2334 sub workstation_list {
2335 my( $self, $conn, $authtoken, @orgs ) = @_;
2337 my $e = new_editor(authtoken=>$authtoken);
2338 return $e->event unless $e->checkauth;
2343 unless $e->allowed('REGISTER_WORKSTATION', $o);
2344 $results{$o} = $e->search_actor_workstation({owning_lib=>$o});
2355 __PACKAGE__->register_method (
2356 method => 'fetch_patron_note',
2357 api_name => 'open-ils.actor.note.retrieve.all',
2360 Returns a list of notes for a given user
2361 Requestor must have VIEW_USER permission if pub==false and
2362 @param authtoken The login session key
2363 @param args Hash of params including
2364 patronid : the patron's id
2365 pub : true if retrieving only public notes
2369 sub fetch_patron_note {
2370 my( $self, $conn, $authtoken, $args ) = @_;
2371 my $patronid = $$args{patronid};
2373 my($reqr, $evt) = $U->checkses($authtoken);
2374 return $evt if $evt;
2377 ($patron, $evt) = $U->fetch_user($patronid);
2378 return $evt if $evt;
2381 if( $patronid ne $reqr->id ) {
2382 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2383 return $evt if $evt;
2385 return $U->cstorereq(
2386 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2387 { usr => $patronid, pub => 't' } );
2390 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2391 return $evt if $evt;
2393 return $U->cstorereq(
2394 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2397 __PACKAGE__->register_method (
2398 method => 'create_user_note',
2399 api_name => 'open-ils.actor.note.create',
2401 Creates a new note for the given user
2402 @param authtoken The login session key
2403 @param note The note object
2406 sub create_user_note {
2407 my( $self, $conn, $authtoken, $note ) = @_;
2408 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2409 return $e->die_event unless $e->checkauth;
2411 my $user = $e->retrieve_actor_user($note->usr)
2412 or return $e->die_event;
2414 return $e->die_event unless
2415 $e->allowed('UPDATE_USER',$user->home_ou);
2417 $note->creator($e->requestor->id);
2418 $e->create_actor_usr_note($note) or return $e->die_event;
2424 __PACKAGE__->register_method (
2425 method => 'delete_user_note',
2426 api_name => 'open-ils.actor.note.delete',
2428 Deletes a note for the given user
2429 @param authtoken The login session key
2430 @param noteid The note id
2433 sub delete_user_note {
2434 my( $self, $conn, $authtoken, $noteid ) = @_;
2436 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2437 return $e->die_event unless $e->checkauth;
2438 my $note = $e->retrieve_actor_usr_note($noteid)
2439 or return $e->die_event;
2440 my $user = $e->retrieve_actor_user($note->usr)
2441 or return $e->die_event;
2442 return $e->die_event unless
2443 $e->allowed('UPDATE_USER', $user->home_ou);
2445 $e->delete_actor_usr_note($note) or return $e->die_event;
2451 __PACKAGE__->register_method (
2452 method => 'update_user_note',
2453 api_name => 'open-ils.actor.note.update',
2455 @param authtoken The login session key
2456 @param note The note
2460 sub update_user_note {
2461 my( $self, $conn, $auth, $note ) = @_;
2462 my $e = new_editor(authtoken=>$auth, xact=>1);
2463 return $e->event unless $e->checkauth;
2464 my $patron = $e->retrieve_actor_user($note->usr)
2465 or return $e->event;
2466 return $e->event unless
2467 $e->allowed('UPDATE_USER', $patron->home_ou);
2468 $e->update_actor_user_note($note)
2469 or return $e->event;
2477 __PACKAGE__->register_method (
2478 method => 'create_closed_date',
2479 api_name => 'open-ils.actor.org_unit.closed_date.create',
2481 Creates a new closing entry for the given org_unit
2482 @param authtoken The login session key
2483 @param note The closed_date object
2486 sub create_closed_date {
2487 my( $self, $conn, $authtoken, $cd ) = @_;
2489 my( $user, $evt ) = $U->checkses($authtoken);
2490 return $evt if $evt;
2492 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2493 return $evt if $evt;
2495 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2497 my $id = $U->storagereq(
2498 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2499 return $U->DB_UPDATE_FAILED($cd) unless $id;
2504 __PACKAGE__->register_method (
2505 method => 'delete_closed_date',
2506 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2508 Deletes a closing entry for the given org_unit
2509 @param authtoken The login session key
2510 @param noteid The close_date id
2513 sub delete_closed_date {
2514 my( $self, $conn, $authtoken, $cd ) = @_;
2516 my( $user, $evt ) = $U->checkses($authtoken);
2517 return $evt if $evt;
2520 ($cd_obj, $evt) = fetch_closed_date($cd);
2521 return $evt if $evt;
2523 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2524 return $evt if $evt;
2526 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2528 my $stat = $U->storagereq(
2529 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2530 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2535 __PACKAGE__->register_method(
2536 method => 'usrname_exists',
2537 api_name => 'open-ils.actor.username.exists',
2539 Returns 1 if the requested username exists, returns 0 otherwise
2543 sub usrname_exists {
2544 my( $self, $conn, $auth, $usrname ) = @_;
2545 my $e = new_editor(authtoken=>$auth);
2546 return $e->event unless $e->checkauth;
2547 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2548 return $$a[0] if $a and @$a;
2552 __PACKAGE__->register_method(
2553 method => 'barcode_exists',
2554 api_name => 'open-ils.actor.barcode.exists',
2557 Returns 1 if the requested barcode exists, returns 0 otherwise
2561 sub barcode_exists {
2562 my( $self, $conn, $auth, $barcode ) = @_;
2563 my $e = new_editor(authtoken=>$auth);
2564 return $e->event unless $e->checkauth;
2565 my $card = $e->search_actor_card({barcode => $barcode});
2571 #return undef unless @$card;
2572 #return $card->[0]->usr;
2576 __PACKAGE__->register_method(
2577 method => 'retrieve_net_levels',
2578 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2581 sub retrieve_net_levels {
2582 my( $self, $conn, $auth ) = @_;
2583 my $e = new_editor(authtoken=>$auth);
2584 return $e->event unless $e->checkauth;
2585 return $e->retrieve_all_config_net_access_level();
2589 __PACKAGE__->register_method(
2590 method => 'fetch_org_by_shortname',
2591 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2593 sub fetch_org_by_shortname {
2594 my( $self, $conn, $sname ) = @_;
2595 my $e = new_editor();
2596 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2597 return $e->event unless $org;
2602 __PACKAGE__->register_method(
2603 method => 'session_home_lib',
2604 api_name => 'open-ils.actor.session.home_lib',
2607 sub session_home_lib {
2608 my( $self, $conn, $auth ) = @_;
2609 my $e = new_editor(authtoken=>$auth);
2610 return undef unless $e->checkauth;
2611 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2612 return $org->shortname;
2615 __PACKAGE__->register_method(
2616 method => 'session_safe_token',
2617 api_name => 'open-ils.actor.session.safe_token',
2619 Returns a hashed session ID that is safe for export to the world.
2620 This safe token will expire after 1 hour of non-use.
2621 @param auth Active authentication token
2625 sub session_safe_token {
2626 my( $self, $conn, $auth ) = @_;
2627 my $e = new_editor(authtoken=>$auth);
2628 return undef unless $e->checkauth;
2630 my $safe_token = md5_hex($auth);
2632 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2634 # Add more like the following if needed...
2636 "safe-token-home_lib-shortname-$safe_token",
2637 $e->retrieve_actor_org_unit(
2638 $e->requestor->home_ou
2647 __PACKAGE__->register_method(
2648 method => 'safe_token_home_lib',
2649 api_name => 'open-ils.actor.safe_token.home_lib.shortname',
2651 Returns the home library shortname from the session
2652 asscociated with a safe token from generated by
2653 open-ils.actor.session.safe_token.
2654 @param safe_token Active safe token
2658 sub safe_token_home_lib {
2659 my( $self, $conn, $safe_token ) = @_;
2661 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2662 return $cache->get_cache( 'safe-token-home_lib-shortname-'. $safe_token );
2667 __PACKAGE__->register_method(
2668 method => 'slim_tree',
2669 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2672 my $tree = new_editor()->search_actor_org_unit(
2674 {"parent_ou" => undef },
2677 flesh_fields => { aou => ['children'] },
2678 order_by => { aou => 'name'},
2679 select => { aou => ["id","shortname", "name"]},
2684 return trim_tree($tree);
2690 return undef unless $tree;
2692 code => $tree->shortname,
2693 name => $tree->name,
2695 if( $tree->children and @{$tree->children} ) {
2696 $htree->{children} = [];
2697 for my $c (@{$tree->children}) {
2698 push( @{$htree->{children}}, trim_tree($c) );
2706 __PACKAGE__->register_method(
2707 method => "update_penalties",
2708 api_name => "open-ils.actor.user.penalties.update");
2710 sub update_penalties {
2711 my($self, $conn, $auth, $user_id) = @_;
2712 my $e = new_editor(authtoken=>$auth, xact => 1);
2713 return $e->die_event unless $e->checkauth;
2714 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
2715 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2716 my $evt = OpenILS::Utils::Penalty->calculate_penalties($e, $user_id, $e->requestor->ws_ou);
2717 return $evt if $evt;
2723 __PACKAGE__->register_method(
2724 method => "apply_penalty",
2725 api_name => "open-ils.actor.user.penalty.apply");
2728 my($self, $conn, $auth, $penalty) = @_;
2730 my $e = new_editor(authtoken=>$auth, xact => 1);
2731 return $e->die_event unless $e->checkauth;
2733 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2734 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2736 my $ptype = $e->retrieve_config_standing_penalty($penalty->standing_penalty) or return $e->die_event;
2739 (defined $ptype->org_depth) ?
2740 $U->org_unit_ancestor_at_depth($penalty->org_unit, $ptype->org_depth) :
2743 $penalty->org_unit($ctx_org);
2744 $penalty->staff($e->requestor->id);
2745 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
2748 return $penalty->id;
2751 __PACKAGE__->register_method(
2752 method => "remove_penalty",
2753 api_name => "open-ils.actor.user.penalty.remove");
2755 sub remove_penalty {
2756 my($self, $conn, $auth, $penalty) = @_;
2757 my $e = new_editor(authtoken=>$auth, xact => 1);
2758 return $e->die_event unless $e->checkauth;
2759 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2760 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2762 $e->delete_actor_user_standing_penalty($penalty) or return $e->die_event;
2767 __PACKAGE__->register_method(
2768 method => "update_penalty_note",
2769 api_name => "open-ils.actor.user.penalty.note.update");
2771 sub update_penalty_note {
2772 my($self, $conn, $auth, $penalty_ids, $note) = @_;
2773 my $e = new_editor(authtoken=>$auth, xact => 1);
2774 return $e->die_event unless $e->checkauth;
2775 for my $penalty_id (@$penalty_ids) {
2776 my $penalty = $e->search_actor_user_standing_penalty( { id => $penalty_id } )->[0];
2777 if (! $penalty ) { return $e->die_event; }
2778 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2779 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2781 $penalty->note( $note ); $penalty->ischanged( 1 );
2783 $e->update_actor_user_standing_penalty($penalty) or return $e->die_event;
2789 __PACKAGE__->register_method(
2790 method => "ranged_penalty_thresholds",
2791 api_name => "open-ils.actor.grp_penalty_threshold.ranged.retrieve",
2795 sub ranged_penalty_thresholds {
2796 my($self, $conn, $auth, $context_org) = @_;
2797 my $e = new_editor(authtoken=>$auth);
2798 return $e->event unless $e->checkauth;
2799 return $e->event unless $e->allowed('VIEW_GROUP_PENALTY_THRESHOLD', $context_org);
2800 my $list = $e->search_permission_grp_penalty_threshold([
2801 {org_unit => $U->get_org_ancestors($context_org)},
2802 {order_by => {pgpt => 'id'}}
2804 $conn->respond($_) for @$list;
2810 __PACKAGE__->register_method(
2811 method => "user_retrieve_fleshed_by_id",
2813 api_name => "open-ils.actor.user.fleshed.retrieve",);
2815 sub user_retrieve_fleshed_by_id {
2816 my( $self, $client, $auth, $user_id, $fields ) = @_;
2817 my $e = new_editor(authtoken => $auth);
2818 return $e->event unless $e->checkauth;
2820 if( $e->requestor->id != $user_id ) {
2821 return $e->event unless $e->allowed('VIEW_USER');
2827 "standing_penalties",
2831 "stat_cat_entries" ];
2832 return new_flesh_user($user_id, $fields, $e);
2836 sub new_flesh_user {
2839 my $fields = shift || [];
2842 my $fetch_penalties = 0;
2843 if(grep {$_ eq 'standing_penalties'} @$fields) {
2844 $fields = [grep {$_ ne 'standing_penalties'} @$fields];
2845 $fetch_penalties = 1;
2848 my $user = $e->retrieve_actor_user(
2853 "flesh_fields" => { "au" => $fields }
2856 ) or return $e->event;
2859 if( grep { $_ eq 'addresses' } @$fields ) {
2861 $user->addresses([]) unless @{$user->addresses};
2862 # don't expose "replaced" addresses by default
2863 $user->addresses([grep {$_->id >= 0} @{$user->addresses}]);
2865 if( ref $user->billing_address ) {
2866 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2867 push( @{$user->addresses}, $user->billing_address );
2871 if( ref $user->mailing_address ) {
2872 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2873 push( @{$user->addresses}, $user->mailing_address );
2878 if($fetch_penalties) {
2879 # grab the user penalties ranged for this location
2880 $user->standing_penalties(
2881 $e->search_actor_user_standing_penalty([
2884 {stop_date => undef},
2885 {stop_date => {'>' => 'now'}}
2887 org_unit => $U->get_org_ancestors($e->requestor->ws_ou)
2890 flesh_fields => {ausp => ['standing_penalty']}
2897 $user->clear_passwd();
2904 __PACKAGE__->register_method(
2905 method => "user_retrieve_parts",
2906 api_name => "open-ils.actor.user.retrieve.parts",);
2908 sub user_retrieve_parts {
2909 my( $self, $client, $auth, $user_id, $fields ) = @_;
2910 my $e = new_editor(authtoken => $auth);
2911 return $e->event unless $e->checkauth;
2912 if( $e->requestor->id != $user_id ) {
2913 return $e->event unless $e->allowed('VIEW_USER');
2916 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2917 push(@resp, $user->$_()) for(@$fields);
2923 __PACKAGE__->register_method(
2924 method => 'user_opt_in_enabled',
2925 api_name => 'open-ils.actor.user.org_unit_opt_in.enabled',
2927 @return 1 if user opt-in is globally enabled, 0 otherwise.
2930 sub user_opt_in_enabled {
2931 my($self, $conn) = @_;
2932 my $sc = OpenSRF::Utils::SettingsClient->new;
2933 return 1 if lc($sc->config_value(share => user => 'opt_in')) eq 'true';
2938 __PACKAGE__->register_method(
2939 method => 'user_opt_in_at_org',
2940 api_name => 'open-ils.actor.user.org_unit_opt_in.check',
2942 @param $auth The auth token
2943 @param user_id The ID of the user to test
2944 @return 1 if the user has opted in at the specified org,
2945 event on error, and 0 otherwise. /);
2946 sub user_opt_in_at_org {
2947 my($self, $conn, $auth, $user_id) = @_;
2949 # see if we even need to enforce the opt-in value
2950 return 1 unless user_opt_in_enabled($self);
2952 my $e = new_editor(authtoken => $auth);
2953 return $e->event unless $e->checkauth;
2954 my $org_id = $e->requestor->ws_ou;
2956 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2957 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
2959 # user is automatically opted-in at the home org
2960 return 1 if $user->home_ou eq $org_id;
2962 my $vals = $e->search_actor_usr_org_unit_opt_in(
2963 {org_unit=>$org_id, usr=>$user_id},{idlist=>1});
2969 __PACKAGE__->register_method(
2970 method => 'create_user_opt_in_at_org',
2971 api_name => 'open-ils.actor.user.org_unit_opt_in.create',
2973 @param $auth The auth token
2974 @param user_id The ID of the user to test
2975 @return The ID of the newly created object, event on error./);
2977 sub create_user_opt_in_at_org {
2978 my($self, $conn, $auth, $user_id) = @_;
2980 my $e = new_editor(authtoken => $auth, xact=>1);
2981 return $e->die_event unless $e->checkauth;
2982 my $org_id = $e->requestor->ws_ou;
2984 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
2985 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2987 my $opt_in = Fieldmapper::actor::usr_org_unit_opt_in->new;
2989 $opt_in->org_unit($org_id);
2990 $opt_in->usr($user_id);
2991 $opt_in->staff($e->requestor->id);
2992 $opt_in->opt_in_ts('now');
2993 $opt_in->opt_in_ws($e->requestor->wsid);
2995 $opt_in = $e->create_actor_usr_org_unit_opt_in($opt_in)
2996 or return $e->die_event;
3004 __PACKAGE__->register_method (
3005 method => 'retrieve_org_hours',
3006 api_name => 'open-ils.actor.org_unit.hours_of_operation.retrieve',
3008 Returns the hours of operation for a specified org unit
3009 @param authtoken The login session key
3010 @param org_id The org_unit ID
3014 sub retrieve_org_hours {
3015 my($self, $conn, $auth, $org_id) = @_;
3016 my $e = new_editor(authtoken => $auth);
3017 return $e->die_event unless $e->checkauth;
3018 $org_id ||= $e->requestor->ws_ou;
3019 return $e->retrieve_actor_org_unit_hours_of_operation($org_id);
3023 __PACKAGE__->register_method (
3024 method => 'verify_user_password',
3025 api_name => 'open-ils.actor.verify_user_password',
3027 Given a barcode or username and the MD5 encoded password,
3028 returns 1 if the password is correct. Returns 0 otherwise.
3032 sub verify_user_password {
3033 my($self, $conn, $auth, $barcode, $username, $password) = @_;
3034 my $e = new_editor(authtoken => $auth);
3035 return $e->die_event unless $e->checkauth;
3037 my $user_by_barcode;
3038 my $user_by_username;
3040 my $card = $e->search_actor_card([
3041 {barcode => $barcode},
3042 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0] or return 0;
3043 $user_by_barcode = $card->usr;
3044 $user = $user_by_barcode;
3047 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return 0;
3048 $user = $user_by_username;
3050 return 0 if (!$user);
3051 return 0 if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3052 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3053 return 1 if $user->passwd eq $password;
3057 __PACKAGE__->register_method (
3058 method => 'retrieve_usr_id_via_barcode_or_usrname',
3059 api_name => "open-ils.actor.user.retrieve_id_by_barcode_or_username",
3061 Given a barcode or username returns the id for the user or
3066 sub retrieve_usr_id_via_barcode_or_usrname {
3067 my($self, $conn, $auth, $barcode, $username) = @_;
3068 my $e = new_editor(authtoken => $auth);
3069 return $e->die_event unless $e->checkauth;
3070 my $id_as_barcode= OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.actor' => app_settings => 'id_as_barcode');
3072 my $user_by_barcode;
3073 my $user_by_username;
3074 $logger->info("$id_as_barcode is the ID as BARCODE");
3076 my $card = $e->search_actor_card([
3077 {barcode => $barcode},
3078 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0];
3079 if ($id_as_barcode =~ /^t/i) {
3081 $user = $e->retrieve_actor_user($barcode);
3082 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$user);
3084 $user_by_barcode = $card->usr;
3085 $user = $user_by_barcode;
3088 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$card);
3089 $user_by_barcode = $card->usr;
3090 $user = $user_by_barcode;
3095 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return OpenILS::Event->new( 'ACTOR_USR_NOT_FOUND' );
3097 $user = $user_by_username;
3099 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if (!$user);
3100 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3101 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3106 __PACKAGE__->register_method (
3107 method => 'merge_users',
3108 api_name => 'open-ils.actor.user.merge',
3111 Given a list of source users and destination user, transfer all data from the source
3112 to the dest user and delete the source user. All user related data is
3113 transferred, including circulations, holds, bookbags, etc.
3119 my($self, $conn, $auth, $master_id, $user_ids, $options) = @_;
3120 my $e = new_editor(xact => 1, authtoken => $auth);
3121 return $e->die_event unless $e->checkauth;
3123 # disallow the merge if any subordinate accounts are in collections
3124 my $colls = $e->search_money_collections_tracker({usr => $user_ids}, {idlist => 1});
3125 return OpenILS::Event->new('MERGED_USER_IN_COLLECTIONS', payload => $user_ids) if @$colls;
3127 my $master_user = $e->retrieve_actor_user($master_id) or return $e->die_event;
3128 my $del_addrs = ($U->ou_ancestor_setting_value(
3129 $master_user->home_ou, 'circ.user_merge.delete_addresses', $e)) ? 't' : 'f';
3130 my $del_cards = ($U->ou_ancestor_setting_value(
3131 $master_user->home_ou, 'circ.user_merge.delete_cards', $e)) ? 't' : 'f';
3132 my $deactivate_cards = ($U->ou_ancestor_setting_value(
3133 $master_user->home_ou, 'circ.user_merge.deactivate_cards', $e)) ? 't' : 'f';
3135 for my $src_id (@$user_ids) {
3136 my $src_user = $e->retrieve_actor_user($src_id) or return $e->die_event;
3138 return $e->die_event unless $e->allowed('MERGE_USERS', $src_user->home_ou);
3139 if($src_user->home_ou ne $master_user->home_ou) {
3140 return $e->die_event unless $e->allowed('MERGE_USERS', $master_user->home_ou);
3143 return $e->die_event unless
3144 $e->json_query({from => [
3159 __PACKAGE__->register_method (
3160 method => 'approve_user_address',
3161 api_name => 'open-ils.actor.user.pending_address.approve',
3168 sub approve_user_address {
3169 my($self, $conn, $auth, $addr) = @_;
3170 my $e = new_editor(xact => 1, authtoken => $auth);
3171 return $e->die_event unless $e->checkauth;
3173 # if the caller passes an address object, assume they want to
3174 # update it first before approving it
3175 $e->update_actor_user_address($addr) or return $e->die_event;
3177 $addr = $e->retrieve_actor_user_address($addr) or return $e->die_event;
3179 my $user = $e->retrieve_actor_user($addr->usr);
3180 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3181 my $result = $e->json_query({from => ['actor.approve_pending_address', $addr->id]})->[0]
3182 or return $e->die_event;
3184 return [values %$result]->[0];
3188 __PACKAGE__->register_method (
3189 method => 'retrieve_friends',
3190 api_name => 'open-ils.actor.friends.retrieve',
3193 returns { confirmed: [], pending_out: [], pending_in: []}
3194 pending_out are users I'm requesting friendship with
3195 pending_in are users requesting friendship with me
3200 sub retrieve_friends {
3201 my($self, $conn, $auth, $user_id, $options) = @_;
3202 my $e = new_editor(authtoken => $auth);
3203 return $e->event unless $e->checkauth;
3204 $user_id ||= $e->requestor->id;
3206 if($user_id != $e->requestor->id) {
3207 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3208 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3211 return OpenILS::Application::Actor::Friends->retrieve_friends(
3212 $e, $user_id, $options);
3217 __PACKAGE__->register_method (
3218 method => 'apply_friend_perms',
3219 api_name => 'open-ils.actor.friends.perms.apply',
3225 sub apply_friend_perms {
3226 my($self, $conn, $auth, $user_id, $delegate_id, @perms) = @_;
3227 my $e = new_editor(authtoken => $auth, xact => 1);
3228 return $e->event unless $e->checkauth;
3230 if($user_id != $e->requestor->id) {
3231 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3232 return $e->die_event unless $e->allowed('VIEW_USER', $user->home_ou);
3235 for my $perm (@perms) {
3237 OpenILS::Application::Actor::Friends->apply_friend_perm(
3238 $e, $user_id, $delegate_id, $perm);
3239 return $evt if $evt;
3247 __PACKAGE__->register_method (
3248 method => 'update_user_pending_address',
3249 api_name => 'open-ils.actor.user.address.pending.cud'
3252 sub update_user_pending_address {
3253 my($self, $conn, $auth, $addr) = @_;
3254 my $e = new_editor(authtoken => $auth, xact => 1);
3255 return $e->event unless $e->checkauth;
3257 if($addr->usr != $e->requestor->id) {
3258 my $user = $e->retrieve_actor_user($addr->usr) or return $e->die_event;
3259 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3263 $e->create_actor_user_address($addr) or return $e->die_event;
3264 } elsif($addr->isdeleted) {
3265 $e->delete_actor_user_address($addr) or return $e->die_event;
3267 $e->update_actor_user_address($addr) or return $e->die_event;
3275 __PACKAGE__->register_method (
3276 method => 'user_events',
3277 api_name => 'open-ils.actor.user.events.circ',
3280 __PACKAGE__->register_method (
3281 method => 'user_events',
3282 api_name => 'open-ils.actor.user.events.ahr',
3287 my($self, $conn, $auth, $user_id, $filters) = @_;
3288 my $e = new_editor(authtoken => $auth);
3289 return $e->event unless $e->checkauth;
3291 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3292 my $user_field = 'usr';
3295 $filters->{target} = {
3296 select => { $obj_type => ['id'] },
3298 where => {usr => $user_id}
3301 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3302 if($e->requestor->id != $user_id) {
3303 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3306 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3307 my $req = $ses->request('open-ils.trigger.events_by_target',
3308 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3310 while(my $resp = $req->recv) {
3311 my $val = $resp->content;
3312 my $tgt = $val->target;
3314 if($obj_type eq 'circ') {
3315 $tgt->target_copy($e->retrieve_asset_copy($tgt->target_copy));
3317 } elsif($obj_type eq 'ahr') {
3318 $tgt->current_copy($e->retrieve_asset_copy($tgt->current_copy))
3319 if $tgt->current_copy;
3322 $conn->respond($val) if $val;
3329 __PACKAGE__->register_method (
3330 method => 'update_events',
3331 api_name => 'open-ils.actor.user.event.cancel.batch',
3334 __PACKAGE__->register_method (
3335 method => 'update_events',
3336 api_name => 'open-ils.actor.user.event.reset.batch',
3341 my($self, $conn, $auth, $event_ids) = @_;
3342 my $e = new_editor(xact => 1, authtoken => $auth);
3343 return $e->die_event unless $e->checkauth;
3346 for my $id (@$event_ids) {
3348 # do a little dance to determine what user we are ultimately affecting
3349 my $event = $e->retrieve_action_trigger_event([
3352 flesh_fields => {atev => ['event_def'], atevdef => ['hook']}
3354 ]) or return $e->die_event;
3357 if($event->event_def->hook->core_type eq 'circ') {
3358 $user_id = $e->retrieve_action_circulation($event->target)->usr;
3359 } elsif($event->event_def->hook->core_type eq 'ahr') {
3360 $user_id = $e->retrieve_action_hold_request($event->target)->usr;
3365 my $user = $e->retrieve_actor_user($user_id);
3366 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3368 if($self->api_name =~ /cancel/) {
3369 $event->state('invalid');
3370 } elsif($self->api_name =~ /reset/) {
3371 $event->clear_start_time;
3372 $event->clear_update_time;
3373 $event->state('pending');
3376 $e->update_action_trigger_event($event) or return $e->die_event;
3377 $conn->respond({maximum => scalar(@$event_ids), progress => $x++});
3381 return {complete => 1};
3385 __PACKAGE__->register_method (
3386 method => 'really_delete_user',
3387 api_name => 'open-ils.actor.user.delete',
3389 It anonymizes all personally identifiable information in actor.usr. By calling actor.usr_purge_data()
3390 it also purges related data from other tables, sometimes by transferring it to a designated destination user.
3391 The usrname field (along with first_given_name and family_name) is updated to id '-PURGED-' now().
3392 dest_usr_id is only required when deleting a user that performs staff functions.
3396 sub really_delete_user {
3397 my($self, $conn, $auth, $user_id, $dest_user_id) = @_;
3398 my $e = new_editor(authtoken => $auth, xact => 1);
3399 return $e->die_event unless $e->checkauth;
3400 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3401 return $e->die_event unless $e->allowed('DELETE_USER', $user->home_ou);
3402 my $stat = $e->json_query(
3403 {from => ['actor.usr_delete', $user_id, $dest_user_id]})->[0]
3404 or return $e->die_event;