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 transaction ids 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 transaction ids 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 transaction ids 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 transaction ids 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 transaction ids for a user that has billings
1988 sub user_transaction_history {
1989 my( $self, $conn, $auth, $userid, $type ) = @_;
1991 # run inside of a transaction to prevent replication delays
1992 my $e = new_editor(xact=>1, authtoken=>$auth);
1993 return $e->die_event unless $e->checkauth;
1995 if( $e->requestor->id ne $userid ) {
1996 return $e->die_event
1997 unless $e->allowed('VIEW_USER_TRANSACTIONS');
2000 my $api = $self->api_name;
2001 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
2003 my @xacts = @{ $e->search_money_billable_transaction(
2004 [ { usr => $userid, @xact_finish },
2006 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
2007 order_by => { mbt => 'xact_start DESC' },
2015 my @mbts = $U->make_mbts( $e, @xacts );
2017 if(defined($type)) {
2018 @mbts = grep { $_->xact_type eq $type } @mbts;
2021 if($api =~ /have_balance/o) {
2022 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
2025 if($api =~ /have_charge/o) {
2026 @mbts = grep { defined($_->last_billing_ts) } @mbts;
2029 if($api =~ /have_bill/o) {
2030 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
2038 __PACKAGE__->register_method(
2039 method => "user_perms",
2040 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2042 notes => <<" NOTES");
2043 Returns a list of permissions
2046 my( $self, $client, $authtoken, $user ) = @_;
2048 my( $staff, $evt ) = $apputils->checkses($authtoken);
2049 return $evt if $evt;
2051 $user ||= $staff->id;
2053 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2057 return $apputils->simple_scalar_request(
2059 "open-ils.storage.permission.user_perms.atomic",
2063 __PACKAGE__->register_method(
2064 method => "retrieve_perms",
2065 api_name => "open-ils.actor.permissions.retrieve",
2066 notes => <<" NOTES");
2067 Returns a list of permissions
2069 sub retrieve_perms {
2070 my( $self, $client ) = @_;
2071 return $apputils->simple_scalar_request(
2073 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2074 { id => { '!=' => undef } }
2078 __PACKAGE__->register_method(
2079 method => "retrieve_groups",
2080 api_name => "open-ils.actor.groups.retrieve",
2081 notes => <<" NOTES");
2082 Returns a list of user groupss
2084 sub retrieve_groups {
2085 my( $self, $client ) = @_;
2086 return new_editor()->retrieve_all_permission_grp_tree();
2089 __PACKAGE__->register_method(
2090 method => "retrieve_org_address",
2091 api_name => "open-ils.actor.org_unit.address.retrieve",
2092 notes => <<' NOTES');
2093 Returns an org_unit address by ID
2094 @param An org_address ID
2096 sub retrieve_org_address {
2097 my( $self, $client, $id ) = @_;
2098 return $apputils->simple_scalar_request(
2100 "open-ils.cstore.direct.actor.org_address.retrieve",
2105 __PACKAGE__->register_method(
2106 method => "retrieve_groups_tree",
2107 api_name => "open-ils.actor.groups.tree.retrieve",
2108 notes => <<" NOTES");
2109 Returns a list of user groups
2111 sub retrieve_groups_tree {
2112 my( $self, $client ) = @_;
2113 return new_editor()->search_permission_grp_tree(
2118 flesh_fields => { pgt => ["children"] },
2119 order_by => { pgt => 'name'}
2126 __PACKAGE__->register_method(
2127 method => "add_user_to_groups",
2128 api_name => "open-ils.actor.user.set_groups",
2129 notes => <<" NOTES");
2130 Adds a user to one or more permission groups
2133 sub add_user_to_groups {
2134 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2136 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2137 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2138 return $evt if $evt;
2140 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2141 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2142 return $evt if $evt;
2144 $apputils->simplereq(
2146 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2148 for my $group (@$groups) {
2149 my $link = Fieldmapper::permission::usr_grp_map->new;
2151 $link->usr($userid);
2153 my $id = $apputils->simplereq(
2155 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2161 __PACKAGE__->register_method(
2162 method => "get_user_perm_groups",
2163 api_name => "open-ils.actor.user.get_groups",
2164 notes => <<" NOTES");
2165 Retrieve a user's permission groups.
2169 sub get_user_perm_groups {
2170 my( $self, $client, $authtoken, $userid ) = @_;
2172 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2173 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2174 return $evt if $evt;
2176 return $apputils->simplereq(
2178 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2182 __PACKAGE__->register_method(
2183 method => "get_user_work_ous",
2184 api_name => "open-ils.actor.user.get_work_ous",
2185 notes => <<" NOTES");
2186 Retrieve a user's work org units.
2188 __PACKAGE__->register_method(
2189 method => "get_user_work_ous",
2190 api_name => "open-ils.actor.user.get_work_ous.ids",
2191 notes => <<" NOTES");
2192 Retrieve a user's work org units.
2196 sub get_user_work_ous {
2197 my( $self, $client, $auth, $userid ) = @_;
2198 my $e = new_editor(authtoken=>$auth);
2199 return $e->event unless $e->checkauth;
2200 $userid ||= $e->requestor->id;
2202 if($e->requestor->id != $userid) {
2203 my $user = $e->retrieve_actor_user($userid)
2204 or return $e->event;
2205 return $e->event unless $e->allowed('ASSIGN_WORK_ORG_UNIT', $user->home_ou);
2208 return $e->search_permission_usr_work_ou_map({usr => $userid})
2209 unless $self->api_name =~ /.ids$/;
2211 # client just wants a list of org IDs
2212 return $U->get_user_work_ou_ids($e, $userid);
2218 __PACKAGE__->register_method (
2219 method => 'register_workstation',
2220 api_name => 'open-ils.actor.workstation.register.override',
2221 signature => q/@see open-ils.actor.workstation.register/);
2223 __PACKAGE__->register_method (
2224 method => 'register_workstation',
2225 api_name => 'open-ils.actor.workstation.register',
2227 Registers a new workstion in the system
2228 @param authtoken The login session key
2229 @param name The name of the workstation id
2230 @param owner The org unit that owns this workstation
2231 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2232 if the name is already in use.
2235 sub register_workstation {
2236 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2238 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2239 return $e->die_event unless $e->checkauth;
2240 return $e->die_event unless $e->allowed('REGISTER_WORKSTATION', $owner);
2241 my $existing = $e->search_actor_workstation({name => $name})->[0];
2245 if( $self->api_name =~ /override/o ) {
2246 # workstation with the given name exists.
2248 if($owner ne $existing->owning_lib) {
2249 # if necessary, update the owning_lib of the workstation
2251 $logger->info("changing owning lib of workstation ".$existing->id.
2252 " from ".$existing->owning_lib." to $owner");
2253 return $e->die_event unless
2254 $e->allowed('UPDATE_WORKSTATION', $existing->owning_lib);
2256 return $e->die_event unless $e->allowed('UPDATE_WORKSTATION', $owner);
2258 $existing->owning_lib($owner);
2259 return $e->die_event unless $e->update_actor_workstation($existing);
2265 "attempt to register an existing workstation. returning existing ID");
2268 return $existing->id;
2271 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2275 my $ws = Fieldmapper::actor::workstation->new;
2276 $ws->owning_lib($owner);
2278 $e->create_actor_workstation($ws) or return $e->die_event;
2280 return $ws->id; # note: editor sets the id on the new object for us
2283 __PACKAGE__->register_method (
2284 method => 'workstation_list',
2285 api_name => 'open-ils.actor.workstation.list',
2287 Returns a list of workstations registered at the given location
2288 @param authtoken The login session key
2289 @param ids A list of org_unit.id's for the workstation owners
2292 sub workstation_list {
2293 my( $self, $conn, $authtoken, @orgs ) = @_;
2295 my $e = new_editor(authtoken=>$authtoken);
2296 return $e->event unless $e->checkauth;
2301 unless $e->allowed('REGISTER_WORKSTATION', $o);
2302 $results{$o} = $e->search_actor_workstation({owning_lib=>$o});
2313 __PACKAGE__->register_method (
2314 method => 'fetch_patron_note',
2315 api_name => 'open-ils.actor.note.retrieve.all',
2318 Returns a list of notes for a given user
2319 Requestor must have VIEW_USER permission if pub==false and
2320 @param authtoken The login session key
2321 @param args Hash of params including
2322 patronid : the patron's id
2323 pub : true if retrieving only public notes
2327 sub fetch_patron_note {
2328 my( $self, $conn, $authtoken, $args ) = @_;
2329 my $patronid = $$args{patronid};
2331 my($reqr, $evt) = $U->checkses($authtoken);
2332 return $evt if $evt;
2335 ($patron, $evt) = $U->fetch_user($patronid);
2336 return $evt if $evt;
2339 if( $patronid ne $reqr->id ) {
2340 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2341 return $evt if $evt;
2343 return $U->cstorereq(
2344 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2345 { usr => $patronid, pub => 't' } );
2348 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2349 return $evt if $evt;
2351 return $U->cstorereq(
2352 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2355 __PACKAGE__->register_method (
2356 method => 'create_user_note',
2357 api_name => 'open-ils.actor.note.create',
2359 Creates a new note for the given user
2360 @param authtoken The login session key
2361 @param note The note object
2364 sub create_user_note {
2365 my( $self, $conn, $authtoken, $note ) = @_;
2366 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2367 return $e->die_event unless $e->checkauth;
2369 my $user = $e->retrieve_actor_user($note->usr)
2370 or return $e->die_event;
2372 return $e->die_event unless
2373 $e->allowed('UPDATE_USER',$user->home_ou);
2375 $note->creator($e->requestor->id);
2376 $e->create_actor_usr_note($note) or return $e->die_event;
2382 __PACKAGE__->register_method (
2383 method => 'delete_user_note',
2384 api_name => 'open-ils.actor.note.delete',
2386 Deletes a note for the given user
2387 @param authtoken The login session key
2388 @param noteid The note id
2391 sub delete_user_note {
2392 my( $self, $conn, $authtoken, $noteid ) = @_;
2394 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2395 return $e->die_event unless $e->checkauth;
2396 my $note = $e->retrieve_actor_usr_note($noteid)
2397 or return $e->die_event;
2398 my $user = $e->retrieve_actor_user($note->usr)
2399 or return $e->die_event;
2400 return $e->die_event unless
2401 $e->allowed('UPDATE_USER', $user->home_ou);
2403 $e->delete_actor_usr_note($note) or return $e->die_event;
2409 __PACKAGE__->register_method (
2410 method => 'update_user_note',
2411 api_name => 'open-ils.actor.note.update',
2413 @param authtoken The login session key
2414 @param note The note
2418 sub update_user_note {
2419 my( $self, $conn, $auth, $note ) = @_;
2420 my $e = new_editor(authtoken=>$auth, xact=>1);
2421 return $e->event unless $e->checkauth;
2422 my $patron = $e->retrieve_actor_user($note->usr)
2423 or return $e->event;
2424 return $e->event unless
2425 $e->allowed('UPDATE_USER', $patron->home_ou);
2426 $e->update_actor_user_note($note)
2427 or return $e->event;
2435 __PACKAGE__->register_method (
2436 method => 'create_closed_date',
2437 api_name => 'open-ils.actor.org_unit.closed_date.create',
2439 Creates a new closing entry for the given org_unit
2440 @param authtoken The login session key
2441 @param note The closed_date object
2444 sub create_closed_date {
2445 my( $self, $conn, $authtoken, $cd ) = @_;
2447 my( $user, $evt ) = $U->checkses($authtoken);
2448 return $evt if $evt;
2450 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2451 return $evt if $evt;
2453 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2455 my $id = $U->storagereq(
2456 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2457 return $U->DB_UPDATE_FAILED($cd) unless $id;
2462 __PACKAGE__->register_method (
2463 method => 'delete_closed_date',
2464 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2466 Deletes a closing entry for the given org_unit
2467 @param authtoken The login session key
2468 @param noteid The close_date id
2471 sub delete_closed_date {
2472 my( $self, $conn, $authtoken, $cd ) = @_;
2474 my( $user, $evt ) = $U->checkses($authtoken);
2475 return $evt if $evt;
2478 ($cd_obj, $evt) = fetch_closed_date($cd);
2479 return $evt if $evt;
2481 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2482 return $evt if $evt;
2484 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2486 my $stat = $U->storagereq(
2487 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2488 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2493 __PACKAGE__->register_method(
2494 method => 'usrname_exists',
2495 api_name => 'open-ils.actor.username.exists',
2497 Returns 1 if the requested username exists, returns 0 otherwise
2501 sub usrname_exists {
2502 my( $self, $conn, $auth, $usrname ) = @_;
2503 my $e = new_editor(authtoken=>$auth);
2504 return $e->event unless $e->checkauth;
2505 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2506 return $$a[0] if $a and @$a;
2510 __PACKAGE__->register_method(
2511 method => 'barcode_exists',
2512 api_name => 'open-ils.actor.barcode.exists',
2515 Returns 1 if the requested barcode exists, returns 0 otherwise
2519 sub barcode_exists {
2520 my( $self, $conn, $auth, $barcode ) = @_;
2521 my $e = new_editor(authtoken=>$auth);
2522 return $e->event unless $e->checkauth;
2523 my $card = $e->search_actor_card({barcode => $barcode});
2529 #return undef unless @$card;
2530 #return $card->[0]->usr;
2534 __PACKAGE__->register_method(
2535 method => 'retrieve_net_levels',
2536 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2539 sub retrieve_net_levels {
2540 my( $self, $conn, $auth ) = @_;
2541 my $e = new_editor(authtoken=>$auth);
2542 return $e->event unless $e->checkauth;
2543 return $e->retrieve_all_config_net_access_level();
2547 __PACKAGE__->register_method(
2548 method => 'fetch_org_by_shortname',
2549 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2551 sub fetch_org_by_shortname {
2552 my( $self, $conn, $sname ) = @_;
2553 my $e = new_editor();
2554 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2555 return $e->event unless $org;
2560 __PACKAGE__->register_method(
2561 method => 'session_home_lib',
2562 api_name => 'open-ils.actor.session.home_lib',
2565 sub session_home_lib {
2566 my( $self, $conn, $auth ) = @_;
2567 my $e = new_editor(authtoken=>$auth);
2568 return undef unless $e->checkauth;
2569 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2570 return $org->shortname;
2573 __PACKAGE__->register_method(
2574 method => 'session_safe_token',
2575 api_name => 'open-ils.actor.session.safe_token',
2577 Returns a hashed session ID that is safe for export to the world.
2578 This safe token will expire after 1 hour of non-use.
2579 @param auth Active authentication token
2583 sub session_safe_token {
2584 my( $self, $conn, $auth ) = @_;
2585 my $e = new_editor(authtoken=>$auth);
2586 return undef unless $e->checkauth;
2588 my $safe_token = md5_hex($auth);
2590 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2592 # Add more like the following if needed...
2594 "safe-token-home_lib-shortname-$safe_token",
2595 $e->retrieve_actor_org_unit(
2596 $e->requestor->home_ou
2605 __PACKAGE__->register_method(
2606 method => 'safe_token_home_lib',
2607 api_name => 'open-ils.actor.safe_token.home_lib.shortname',
2609 Returns the home library shortname from the session
2610 asscociated with a safe token from generated by
2611 open-ils.actor.session.safe_token.
2612 @param safe_token Active safe token
2616 sub safe_token_home_lib {
2617 my( $self, $conn, $safe_token ) = @_;
2619 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2620 return $cache->get_cache( 'safe-token-home_lib-shortname-'. $safe_token );
2625 __PACKAGE__->register_method(
2626 method => 'slim_tree',
2627 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2630 my $tree = new_editor()->search_actor_org_unit(
2632 {"parent_ou" => undef },
2635 flesh_fields => { aou => ['children'] },
2636 order_by => { aou => 'name'},
2637 select => { aou => ["id","shortname", "name"]},
2642 return trim_tree($tree);
2648 return undef unless $tree;
2650 code => $tree->shortname,
2651 name => $tree->name,
2653 if( $tree->children and @{$tree->children} ) {
2654 $htree->{children} = [];
2655 for my $c (@{$tree->children}) {
2656 push( @{$htree->{children}}, trim_tree($c) );
2664 __PACKAGE__->register_method(
2665 method => "update_penalties",
2666 api_name => "open-ils.actor.user.penalties.update");
2668 sub update_penalties {
2669 my($self, $conn, $auth, $user_id) = @_;
2670 my $e = new_editor(authtoken=>$auth, xact => 1);
2671 return $e->die_event unless $e->checkauth;
2672 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
2673 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2674 my $evt = OpenILS::Utils::Penalty->calculate_penalties($e, $user_id, $e->requestor->ws_ou);
2675 return $evt if $evt;
2681 __PACKAGE__->register_method(
2682 method => "apply_penalty",
2683 api_name => "open-ils.actor.user.penalty.apply");
2686 my($self, $conn, $auth, $penalty) = @_;
2688 my $e = new_editor(authtoken=>$auth, xact => 1);
2689 return $e->die_event unless $e->checkauth;
2691 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2692 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2694 my $ptype = $e->retrieve_config_standing_penalty($penalty->standing_penalty) or return $e->die_event;
2697 (defined $ptype->org_depth) ?
2698 $U->org_unit_ancestor_at_depth($penalty->org_unit, $ptype->org_depth) :
2701 $penalty->org_unit($ctx_org);
2702 $penalty->staff($e->requestor->id);
2703 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
2706 return $penalty->id;
2709 __PACKAGE__->register_method(
2710 method => "remove_penalty",
2711 api_name => "open-ils.actor.user.penalty.remove");
2713 sub remove_penalty {
2714 my($self, $conn, $auth, $penalty) = @_;
2715 my $e = new_editor(authtoken=>$auth, xact => 1);
2716 return $e->die_event unless $e->checkauth;
2717 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2718 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2720 $e->delete_actor_user_standing_penalty($penalty) or return $e->die_event;
2725 __PACKAGE__->register_method(
2726 method => "update_penalty_note",
2727 api_name => "open-ils.actor.user.penalty.note.update");
2729 sub update_penalty_note {
2730 my($self, $conn, $auth, $penalty_ids, $note) = @_;
2731 my $e = new_editor(authtoken=>$auth, xact => 1);
2732 return $e->die_event unless $e->checkauth;
2733 for my $penalty_id (@$penalty_ids) {
2734 my $penalty = $e->search_actor_user_standing_penalty( { id => $penalty_id } )->[0];
2735 if (! $penalty ) { return $e->die_event; }
2736 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2737 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2739 $penalty->note( $note ); $penalty->ischanged( 1 );
2741 $e->update_actor_user_standing_penalty($penalty) or return $e->die_event;
2747 __PACKAGE__->register_method(
2748 method => "ranged_penalty_thresholds",
2749 api_name => "open-ils.actor.grp_penalty_threshold.ranged.retrieve",
2753 sub ranged_penalty_thresholds {
2754 my($self, $conn, $auth, $context_org) = @_;
2755 my $e = new_editor(authtoken=>$auth);
2756 return $e->event unless $e->checkauth;
2757 return $e->event unless $e->allowed('VIEW_GROUP_PENALTY_THRESHOLD', $context_org);
2758 my $list = $e->search_permission_grp_penalty_threshold([
2759 {org_unit => $U->get_org_ancestors($context_org)},
2760 {order_by => {pgpt => 'id'}}
2762 $conn->respond($_) for @$list;
2768 __PACKAGE__->register_method(
2769 method => "user_retrieve_fleshed_by_id",
2771 api_name => "open-ils.actor.user.fleshed.retrieve",);
2773 sub user_retrieve_fleshed_by_id {
2774 my( $self, $client, $auth, $user_id, $fields ) = @_;
2775 my $e = new_editor(authtoken => $auth);
2776 return $e->event unless $e->checkauth;
2778 if( $e->requestor->id != $user_id ) {
2779 return $e->event unless $e->allowed('VIEW_USER');
2785 "standing_penalties",
2789 "stat_cat_entries" ];
2790 return new_flesh_user($user_id, $fields, $e);
2794 sub new_flesh_user {
2797 my $fields = shift || [];
2800 my $fetch_penalties = 0;
2801 if(grep {$_ eq 'standing_penalties'} @$fields) {
2802 $fields = [grep {$_ ne 'standing_penalties'} @$fields];
2803 $fetch_penalties = 1;
2806 my $user = $e->retrieve_actor_user(
2811 "flesh_fields" => { "au" => $fields }
2814 ) or return $e->event;
2817 if( grep { $_ eq 'addresses' } @$fields ) {
2819 $user->addresses([]) unless @{$user->addresses};
2820 # don't expose "replaced" addresses by default
2821 $user->addresses([grep {$_->id >= 0} @{$user->addresses}]);
2823 if( ref $user->billing_address ) {
2824 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2825 push( @{$user->addresses}, $user->billing_address );
2829 if( ref $user->mailing_address ) {
2830 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2831 push( @{$user->addresses}, $user->mailing_address );
2836 if($fetch_penalties) {
2837 # grab the user penalties ranged for this location
2838 $user->standing_penalties(
2839 $e->search_actor_user_standing_penalty([
2842 {stop_date => undef},
2843 {stop_date => {'>' => 'now'}}
2845 org_unit => $U->get_org_ancestors($e->requestor->ws_ou)
2848 flesh_fields => {ausp => ['standing_penalty']}
2855 $user->clear_passwd();
2862 __PACKAGE__->register_method(
2863 method => "user_retrieve_parts",
2864 api_name => "open-ils.actor.user.retrieve.parts",);
2866 sub user_retrieve_parts {
2867 my( $self, $client, $auth, $user_id, $fields ) = @_;
2868 my $e = new_editor(authtoken => $auth);
2869 return $e->event unless $e->checkauth;
2870 if( $e->requestor->id != $user_id ) {
2871 return $e->event unless $e->allowed('VIEW_USER');
2874 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2875 push(@resp, $user->$_()) for(@$fields);
2881 __PACKAGE__->register_method(
2882 method => 'user_opt_in_enabled',
2883 api_name => 'open-ils.actor.user.org_unit_opt_in.enabled',
2885 @return 1 if user opt-in is globally enabled, 0 otherwise.
2888 sub user_opt_in_enabled {
2889 my($self, $conn) = @_;
2890 my $sc = OpenSRF::Utils::SettingsClient->new;
2891 return 1 if lc($sc->config_value(share => user => 'opt_in')) eq 'true';
2896 __PACKAGE__->register_method(
2897 method => 'user_opt_in_at_org',
2898 api_name => 'open-ils.actor.user.org_unit_opt_in.check',
2900 @param $auth The auth token
2901 @param user_id The ID of the user to test
2902 @return 1 if the user has opted in at the specified org,
2903 event on error, and 0 otherwise. /);
2904 sub user_opt_in_at_org {
2905 my($self, $conn, $auth, $user_id) = @_;
2907 # see if we even need to enforce the opt-in value
2908 return 1 unless user_opt_in_enabled($self);
2910 my $e = new_editor(authtoken => $auth);
2911 return $e->event unless $e->checkauth;
2912 my $org_id = $e->requestor->ws_ou;
2914 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2915 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
2917 # user is automatically opted-in at the home org
2918 return 1 if $user->home_ou eq $org_id;
2920 my $vals = $e->search_actor_usr_org_unit_opt_in(
2921 {org_unit=>$org_id, usr=>$user_id},{idlist=>1});
2927 __PACKAGE__->register_method(
2928 method => 'create_user_opt_in_at_org',
2929 api_name => 'open-ils.actor.user.org_unit_opt_in.create',
2931 @param $auth The auth token
2932 @param user_id The ID of the user to test
2933 @return The ID of the newly created object, event on error./);
2935 sub create_user_opt_in_at_org {
2936 my($self, $conn, $auth, $user_id) = @_;
2938 my $e = new_editor(authtoken => $auth, xact=>1);
2939 return $e->die_event unless $e->checkauth;
2940 my $org_id = $e->requestor->ws_ou;
2942 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
2943 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2945 my $opt_in = Fieldmapper::actor::usr_org_unit_opt_in->new;
2947 $opt_in->org_unit($org_id);
2948 $opt_in->usr($user_id);
2949 $opt_in->staff($e->requestor->id);
2950 $opt_in->opt_in_ts('now');
2951 $opt_in->opt_in_ws($e->requestor->wsid);
2953 $opt_in = $e->create_actor_usr_org_unit_opt_in($opt_in)
2954 or return $e->die_event;
2962 __PACKAGE__->register_method (
2963 method => 'retrieve_org_hours',
2964 api_name => 'open-ils.actor.org_unit.hours_of_operation.retrieve',
2966 Returns the hours of operation for a specified org unit
2967 @param authtoken The login session key
2968 @param org_id The org_unit ID
2972 sub retrieve_org_hours {
2973 my($self, $conn, $auth, $org_id) = @_;
2974 my $e = new_editor(authtoken => $auth);
2975 return $e->die_event unless $e->checkauth;
2976 $org_id ||= $e->requestor->ws_ou;
2977 return $e->retrieve_actor_org_unit_hours_of_operation($org_id);
2981 __PACKAGE__->register_method (
2982 method => 'verify_user_password',
2983 api_name => 'open-ils.actor.verify_user_password',
2985 Given a barcode or username and the MD5 encoded password,
2986 returns 1 if the password is correct. Returns 0 otherwise.
2990 sub verify_user_password {
2991 my($self, $conn, $auth, $barcode, $username, $password) = @_;
2992 my $e = new_editor(authtoken => $auth);
2993 return $e->die_event unless $e->checkauth;
2995 my $user_by_barcode;
2996 my $user_by_username;
2998 my $card = $e->search_actor_card([
2999 {barcode => $barcode},
3000 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0] or return 0;
3001 $user_by_barcode = $card->usr;
3002 $user = $user_by_barcode;
3005 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return 0;
3006 $user = $user_by_username;
3008 return 0 if (!$user);
3009 return 0 if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3010 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3011 return 1 if $user->passwd eq $password;
3015 __PACKAGE__->register_method (
3016 method => 'retrieve_usr_id_via_barcode_or_usrname',
3017 api_name => "open-ils.actor.user.retrieve_id_by_barcode_or_username",
3019 Given a barcode or username returns the id for the user or
3024 sub retrieve_usr_id_via_barcode_or_usrname {
3025 my($self, $conn, $auth, $barcode, $username) = @_;
3026 my $e = new_editor(authtoken => $auth);
3027 return $e->die_event unless $e->checkauth;
3028 my $id_as_barcode= OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.actor' => app_settings => 'id_as_barcode');
3030 my $user_by_barcode;
3031 my $user_by_username;
3032 $logger->info("$id_as_barcode is the ID as BARCODE");
3034 my $card = $e->search_actor_card([
3035 {barcode => $barcode},
3036 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0];
3037 if ($id_as_barcode =~ /^t/i) {
3039 $user = $e->retrieve_actor_user($barcode);
3040 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$user);
3042 $user_by_barcode = $card->usr;
3043 $user = $user_by_barcode;
3046 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$card);
3047 $user_by_barcode = $card->usr;
3048 $user = $user_by_barcode;
3053 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return OpenILS::Event->new( 'ACTOR_USR_NOT_FOUND' );
3055 $user = $user_by_username;
3057 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if (!$user);
3058 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3059 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3064 __PACKAGE__->register_method (
3065 method => 'merge_users',
3066 api_name => 'open-ils.actor.user.merge',
3069 Given a list of source users and destination user, transfer all data from the source
3070 to the dest user and delete the source user. All user related data is
3071 transferred, including circulations, holds, bookbags, etc.
3077 my($self, $conn, $auth, $master_id, $user_ids, $options) = @_;
3078 my $e = new_editor(xact => 1, authtoken => $auth);
3079 return $e->die_event unless $e->checkauth;
3081 # disallow the merge if any subordinate accounts are in collections
3082 my $colls = $e->search_money_collections_tracker({usr => $user_ids}, {idlist => 1});
3083 return OpenILS::Event->new('MERGED_USER_IN_COLLECTIONS', payload => $user_ids) if @$colls;
3085 my $master_user = $e->retrieve_actor_user($master_id) or return $e->die_event;
3086 my $del_addrs = ($U->ou_ancestor_setting_value(
3087 $master_user->home_ou, 'circ.user_merge.delete_addresses', $e)) ? 't' : 'f';
3088 my $del_cards = ($U->ou_ancestor_setting_value(
3089 $master_user->home_ou, 'circ.user_merge.delete_cards', $e)) ? 't' : 'f';
3090 my $deactivate_cards = ($U->ou_ancestor_setting_value(
3091 $master_user->home_ou, 'circ.user_merge.deactivate_cards', $e)) ? 't' : 'f';
3093 for my $src_id (@$user_ids) {
3094 my $src_user = $e->retrieve_actor_user($src_id) or return $e->die_event;
3096 return $e->die_event unless $e->allowed('MERGE_USERS', $src_user->home_ou);
3097 if($src_user->home_ou ne $master_user->home_ou) {
3098 return $e->die_event unless $e->allowed('MERGE_USERS', $master_user->home_ou);
3101 return $e->die_event unless
3102 $e->json_query({from => [
3117 __PACKAGE__->register_method (
3118 method => 'approve_user_address',
3119 api_name => 'open-ils.actor.user.pending_address.approve',
3126 sub approve_user_address {
3127 my($self, $conn, $auth, $addr) = @_;
3128 my $e = new_editor(xact => 1, authtoken => $auth);
3129 return $e->die_event unless $e->checkauth;
3131 # if the caller passes an address object, assume they want to
3132 # update it first before approving it
3133 $e->update_actor_user_address($addr) or return $e->die_event;
3135 $addr = $e->retrieve_actor_user_address($addr) or return $e->die_event;
3137 my $user = $e->retrieve_actor_user($addr->usr);
3138 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3139 my $result = $e->json_query({from => ['actor.approve_pending_address', $addr->id]})->[0]
3140 or return $e->die_event;
3142 return [values %$result]->[0];
3146 __PACKAGE__->register_method (
3147 method => 'retrieve_friends',
3148 api_name => 'open-ils.actor.friends.retrieve',
3151 returns { confirmed: [], pending_out: [], pending_in: []}
3152 pending_out are users I'm requesting friendship with
3153 pending_in are users requesting friendship with me
3158 sub retrieve_friends {
3159 my($self, $conn, $auth, $user_id, $options) = @_;
3160 my $e = new_editor(authtoken => $auth);
3161 return $e->event unless $e->checkauth;
3162 $user_id ||= $e->requestor->id;
3164 if($user_id != $e->requestor->id) {
3165 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3166 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3169 return OpenILS::Application::Actor::Friends->retrieve_friends(
3170 $e, $user_id, $options);
3175 __PACKAGE__->register_method (
3176 method => 'apply_friend_perms',
3177 api_name => 'open-ils.actor.friends.perms.apply',
3183 sub apply_friend_perms {
3184 my($self, $conn, $auth, $user_id, $delegate_id, @perms) = @_;
3185 my $e = new_editor(authtoken => $auth, xact => 1);
3186 return $e->event unless $e->checkauth;
3188 if($user_id != $e->requestor->id) {
3189 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3190 return $e->die_event unless $e->allowed('VIEW_USER', $user->home_ou);
3193 for my $perm (@perms) {
3195 OpenILS::Application::Actor::Friends->apply_friend_perm(
3196 $e, $user_id, $delegate_id, $perm);
3197 return $evt if $evt;
3205 __PACKAGE__->register_method (
3206 method => 'update_user_pending_address',
3207 api_name => 'open-ils.actor.user.address.pending.cud'
3210 sub update_user_pending_address {
3211 my($self, $conn, $auth, $addr) = @_;
3212 my $e = new_editor(authtoken => $auth, xact => 1);
3213 return $e->event unless $e->checkauth;
3215 if($addr->usr != $e->requestor->id) {
3216 my $user = $e->retrieve_actor_user($addr->usr) or return $e->die_event;
3217 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3221 $e->create_actor_user_address($addr) or return $e->die_event;
3222 } elsif($addr->isdeleted) {
3223 $e->delete_actor_user_address($addr) or return $e->die_event;
3225 $e->update_actor_user_address($addr) or return $e->die_event;
3233 __PACKAGE__->register_method (
3234 method => 'user_events',
3235 api_name => 'open-ils.actor.user.events.circ',
3238 __PACKAGE__->register_method (
3239 method => 'user_events',
3240 api_name => 'open-ils.actor.user.events.ahr',
3245 my($self, $conn, $auth, $user_id, $filters) = @_;
3246 my $e = new_editor(authtoken => $auth);
3247 return $e->event unless $e->checkauth;
3249 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3250 my $user_field = 'usr';
3253 $filters->{target} = {
3254 select => { $obj_type => ['id'] },
3256 where => {usr => $user_id}
3259 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3260 if($e->requestor->id != $user_id) {
3261 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3264 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3265 my $req = $ses->request('open-ils.trigger.events_by_target',
3266 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3268 while(my $resp = $req->recv) {
3269 my $val = $resp->content;
3270 my $tgt = $val->target;
3272 if($obj_type eq 'circ') {
3273 $tgt->target_copy($e->retrieve_asset_copy($tgt->target_copy));
3275 } elsif($obj_type eq 'ahr') {
3276 $tgt->current_copy($e->retrieve_asset_copy($tgt->current_copy))
3277 if $tgt->current_copy;
3280 $conn->respond($val) if $val;
3287 __PACKAGE__->register_method (
3288 method => 'update_events',
3289 api_name => 'open-ils.actor.user.event.cancel.batch',
3292 __PACKAGE__->register_method (
3293 method => 'update_events',
3294 api_name => 'open-ils.actor.user.event.reset.batch',
3299 my($self, $conn, $auth, $event_ids) = @_;
3300 my $e = new_editor(xact => 1, authtoken => $auth);
3301 return $e->die_event unless $e->checkauth;
3304 for my $id (@$event_ids) {
3306 # do a little dance to determine what user we are ultimately affecting
3307 my $event = $e->retrieve_action_trigger_event([
3310 flesh_fields => {atev => ['event_def'], atevdef => ['hook']}
3312 ]) or return $e->die_event;
3315 if($event->event_def->hook->core_type eq 'circ') {
3316 $user_id = $e->retrieve_action_circulation($event->target)->usr;
3317 } elsif($event->event_def->hook->core_type eq 'ahr') {
3318 $user_id = $e->retrieve_action_hold_request($event->target)->usr;
3323 my $user = $e->retrieve_actor_user($user_id);
3324 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3326 if($self->api_name =~ /cancel/) {
3327 $event->state('invalid');
3328 } elsif($self->api_name =~ /reset/) {
3329 $event->clear_start_time;
3330 $event->clear_update_time;
3331 $event->state('pending');
3334 $e->update_action_trigger_event($event) or return $e->die_event;
3335 $conn->respond({maximum => scalar(@$event_ids), progress => $x++});
3339 return {complete => 1};
3343 __PACKAGE__->register_method (
3344 method => 'really_delete_user',
3345 api_name => 'open-ils.actor.user.delete',
3347 It anonymizes all personally identifiable information in actor.usr. By calling actor.usr_purge_data()
3348 it also purges related data from other tables, sometimes by transferring it to a designated destination user.
3349 The usrname field (along with first_given_name and family_name) is updated to id '-PURGED-' now().
3350 dest_usr_id is only required when deleting a user that performs staff functions.
3354 sub really_delete_user {
3355 my($self, $conn, $auth, $user_id, $dest_user_id) = @_;
3356 my $e = new_editor(authtoken => $auth, xact => 1);
3357 return $e->die_event unless $e->checkauth;
3358 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3359 return $e->die_event unless $e->allowed('DELETE_USER', $user->home_ou);
3360 my $stat = $e->json_query(
3361 {from => ['actor.usr_delete', $user_id, $dest_user_id]})->[0]
3362 or return $e->die_event;