1 package OpenILS::Application::Actor;
2 use base qw/OpenSRF::Application/;
3 use strict; use warnings;
5 $Data::Dumper::Indent = 0;
8 use Digest::MD5 qw(md5_hex);
10 use OpenSRF::EX qw(:try);
14 use OpenILS::Application::AppUtils;
16 use OpenILS::Utils::Fieldmapper;
17 use OpenILS::Application::Search::Actor;
18 use OpenILS::Utils::ModsParser;
19 use OpenSRF::Utils::Logger qw/$logger/;
20 use OpenSRF::Utils qw/:datetime/;
22 use OpenSRF::Utils::Cache;
25 use DateTime::Format::ISO8601;
27 use OpenILS::Application::Actor::Container;
30 OpenILS::Application::Actor::Container->initialize();
33 my $apputils = "OpenILS::Application::AppUtils";
36 sub _d { warn "Patron:\n" . Dumper(shift()); }
41 my $set_user_settings;
44 __PACKAGE__->register_method(
45 method => "set_user_settings",
46 api_name => "open-ils.actor.patron.settings.update",
48 sub set_user_settings {
49 my( $self, $client, $user_session, $uid, $settings ) = @_;
51 $logger->debug("Setting user settings: $user_session, $uid, " . Dumper($settings));
53 my( $staff, $user, $evt ) =
54 $apputils->checkses_requestor( $user_session, $uid, 'UPDATE_USER' );
59 [{ usr => $user->id, name => $_}, {value => $$settings{$_}}] } keys %$settings;
61 $logger->activity("User " . $staff->id . " updating user $uid settings with: " . Dumper($params));
63 return $apputils->simplereq(
65 'open-ils.storage.direct.actor.user_setting.batch.merge', $params );
71 __PACKAGE__->register_method(
72 method => "set_ou_settings",
73 api_name => "open-ils.actor.org_unit.settings.update",
76 my( $self, $client, $user_session, $ouid, $settings ) = @_;
78 my( $staff, $evt ) = $apputils->checkses( $user_session );
80 $evt = $apputils->check_perms( $staff->id, $ouid, 'UPDATE_ORG_UNIT' );
85 map { [{ org_unit => $ouid, name => $_}, {value => $$settings{$_}}] } keys %$settings;
87 $logger->activity("Updating org unit [$ouid] settings with: " . Dumper($params));
89 return $apputils->simplereq(
91 'open-ils.storage.direct.actor.org_unit_setting.merge', @$params );
95 my $fetch_user_settings;
96 my $fetch_ou_settings;
98 __PACKAGE__->register_method(
99 method => "user_settings",
100 api_name => "open-ils.actor.patron.settings.retrieve",
103 my( $self, $client, $user_session, $uid ) = @_;
105 my( $staff, $user, $evt ) =
106 $apputils->checkses_requestor( $user_session, $uid, 'VIEW_USER' );
109 $logger->debug("User " . $staff->id . " fetching user $uid\n");
110 my $s = $apputils->simplereq(
112 'open-ils.storage.direct.actor.user_setting.search.usr.atomic',$uid );
114 return { map { ($_->name,$_->value) } @$s };
119 __PACKAGE__->register_method(
120 method => "ou_settings",
121 api_name => "open-ils.actor.org_unit.settings.retrieve",
124 my( $self, $client, $ouid ) = @_;
126 my $s = $apputils->simplereq(
128 'open-ils.storage.direct.actor.org_unit_setting.search.org_unit.atomic', $ouid);
130 return { map { ($_->name,$_->value) } @$s };
135 __PACKAGE__->register_method(
136 method => "update_patron",
137 api_name => "open-ils.actor.patron.update",);
140 my( $self, $client, $user_session, $patron ) = @_;
142 my $session = $apputils->start_db_session();
145 $logger->info("Creating new patron...") if $patron->isnew;
146 $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
148 my( $user_obj, $evt ) = $U->checkses($user_session);
151 # XXX does this user have permission to add/create users. Granularity?
152 # $new_patron is the patron in progress. $patron is the original patron
153 # passed in with the method. new_patron will change as the components
154 # of patron are added/updated.
158 # unflesh the real items on the patron
159 $patron->card( $patron->card->id ) if(ref($patron->card));
160 $patron->billing_address( $patron->billing_address->id )
161 if(ref($patron->billing_address));
162 $patron->mailing_address( $patron->mailing_address->id )
163 if(ref($patron->mailing_address));
165 # create/update the patron first so we can use his id
166 if($patron->isnew()) {
167 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
169 } else { $new_patron = $patron; }
171 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
174 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
177 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
180 # re-update the patron if anything has happened to him during this process
181 if($new_patron->ischanged()) {
182 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
186 #$session = OpenSRF::AppSession->create("open-ils.storage"); # why did i put this here?
188 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
191 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
194 ($new_patron, $evt) = _create_standing_penalties($session, $user_session, $patron, $new_patron, $user_obj);
197 $apputils->commit_db_session($session);
199 #warn "Patron Update/Create complete\n";
200 return flesh_user($new_patron->id());
206 __PACKAGE__->register_method(
207 method => "user_retrieve_fleshed_by_id",
208 api_name => "open-ils.actor.user.fleshed.retrieve",);
210 sub user_retrieve_fleshed_by_id {
211 my( $self, $client, $user_session, $user_id ) = @_;
213 my( $requestor, $target, $evt ) = $apputils->
214 checkses_requestor( $user_session, $user_id, 'VIEW_USER' );
217 return flesh_user($user_id);
228 $session = OpenSRF::AppSession->create("open-ils.storage");
232 # grab the user with the given id
233 my $ureq = $session->request(
234 "open-ils.storage.direct.actor.user.retrieve", $id);
235 my $user = $ureq->gather(1);
237 if(!$user) { return undef; }
240 my $cards_req = $session->request(
241 "open-ils.storage.direct.actor.card.search.usr.atomic",
243 $user->cards( $cards_req->gather(1) );
245 for my $c(@{$user->cards}) {
246 if($c->id == $user->card || $c->id eq $user->card ) {
247 #warn "Setting my card to " . $c->id . "\n";
252 my $add_req = $session->request(
253 "open-ils.storage.direct.actor.user_address.search.usr.atomic",
255 $user->addresses( $add_req->gather(1) );
257 for my $c(@{$user->addresses}) {
258 if($c->id eq $user->billing_address ) { $user->billing_address($c); }
259 if($c->id eq $user->mailing_address ) { $user->mailing_address($c); }
262 my $stat_req = $session->request(
263 "open-ils.storage.direct.actor.stat_cat_entry_user_map.search.target_usr.atomic",
265 $user->stat_cat_entries($stat_req->gather(1));
267 my $standing_penalties_req = $session->request(
268 "open-ils.storage.direct.actor.user_standing_penalty.search.usr.atomic",
270 $user->standing_penalties($standing_penalties_req->gather(1));
272 if($kill) { $session->disconnect(); }
273 $user->clear_passwd();
279 # clone and clear stuff that would break the database
283 my $new_patron = $patron->clone;
285 # Using the Fieldmapper clone method
286 #my $new_patron = Fieldmapper::actor::user->new();
288 #my $fmap = $Fieldmapper::fieldmap;
289 #no strict; # shallow clone, may be useful in the fieldmapper
291 # (keys %{$fmap->{"Fieldmapper::actor::user"}->{'fields'}}) {
292 # $new_patron->$field( $patron->$field() );
297 $new_patron->clear_billing_address();
298 $new_patron->clear_mailing_address();
299 $new_patron->clear_addresses();
300 $new_patron->clear_card();
301 $new_patron->clear_cards();
302 $new_patron->clear_id();
303 $new_patron->clear_isnew();
304 $new_patron->clear_ischanged();
305 $new_patron->clear_isdeleted();
306 $new_patron->clear_stat_cat_entries();
307 $new_patron->clear_permissions();
308 $new_patron->clear_standing_penalties();
318 my $user_obj = shift;
320 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
321 return (undef, $evt) if $evt;
323 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
325 my $id = $session->request(
326 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
327 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
329 $logger->info("Successfully created new user [$id] in DB");
331 return ( $session->request(
332 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
337 my( $session, $patron, $user_obj) = @_;
339 $logger->info("Updating patron ".$patron->id." in DB");
340 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
341 return (undef, $evt) if $evt;
343 $patron->clear_passwd if ($patron->passwd =~ /^\s+/);
345 my $stat = $session->request(
346 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
347 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
353 sub _add_update_addresses {
357 my $new_patron = shift;
361 my $current_id; # id of the address before creation
363 for my $address (@{$patron->addresses()}) {
365 $address->usr($new_patron->id());
367 if(ref($address) and $address->isnew()) {
369 $current_id = $address->id();
370 ($address, $evt) = _add_address($session,$address);
371 return (undef, $evt) if $evt;
373 if( $patron->billing_address() and
374 $patron->billing_address() == $current_id ) {
375 $new_patron->billing_address($address->id());
376 $new_patron->ischanged(1);
379 if( $patron->mailing_address() and
380 $patron->mailing_address() == $current_id ) {
381 $new_patron->mailing_address($address->id());
382 $new_patron->ischanged(1);
385 } elsif( ref($address) and $address->ischanged() ) {
387 $address->usr($new_patron->id());
388 ($address, $evt) = _update_address($session, $address);
389 return (undef, $evt) if $evt;
391 } elsif( ref($address) and $address->isdeleted() ) {
393 if( $address->id() == $new_patron->mailing_address() ) {
394 $new_patron->clear_mailing_address();
395 ($new_patron, $evt) = _update_patron($session, $new_patron);
396 return (undef, $evt) if $evt;
399 if( $address->id() == $new_patron->billing_address() ) {
400 $new_patron->clear_billing_address();
401 ($new_patron, $evt) = _update_patron($session, $new_patron);
402 return (undef, $evt) if $evt;
405 $evt = _delete_address($session, $address);
406 return (undef, $evt) if $evt;
410 return ( $new_patron, undef );
414 # adds an address to the db and returns the address with new id
416 my($session, $address) = @_;
417 $address->clear_id();
419 $logger->info("Creating new address at street ".$address->street1);
421 # put the address into the database
422 my $id = $session->request(
423 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
424 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
427 return ($address, undef);
431 sub _update_address {
432 my( $session, $address ) = @_;
433 $logger->info("Updating address ".$address->id." in the DB");
435 my $stat = $session->request(
436 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
438 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
439 return ($address, undef);
444 sub _add_update_cards {
448 my $new_patron = shift;
452 my $virtual_id; #id of the card before creation
453 for my $card (@{$patron->cards()}) {
455 $card->usr($new_patron->id());
457 if(ref($card) and $card->isnew()) {
459 $virtual_id = $card->id();
460 ( $card, $evt ) = _add_card($session,$card);
461 return (undef, $evt) if $evt;
463 #if(ref($patron->card)) { $patron->card($patron->card->id); }
464 if($patron->card() == $virtual_id) {
465 $new_patron->card($card->id());
466 $new_patron->ischanged(1);
469 } elsif( ref($card) and $card->ischanged() ) {
470 $card->usr($new_patron->id());
471 $evt = _update_card($session, $card);
472 return (undef, $evt) if $evt;
476 return ( $new_patron, undef );
480 # adds an card to the db and returns the card with new id
482 my( $session, $card ) = @_;
484 $logger->info("Adding new patron card ".$card->barcode);
486 my $id = $session->request(
487 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
488 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
489 $logger->info("Successfully created patron card $id");
492 return ( $card, undef );
496 # returns event on error. returns undef otherwise
498 my( $session, $card ) = @_;
499 $logger->info("Updating patron card ".$card->id);
501 my $stat = $session->request(
502 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
503 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
510 # returns event on error. returns undef otherwise
511 sub _delete_address {
512 my( $session, $address ) = @_;
513 $logger->info("Deleting address ".$address->id." from DB");
515 my $stat = $session->request(
516 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
518 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
524 sub _add_survey_responses {
525 my ($session, $patron, $new_patron) = @_;
527 $logger->info( "Updating survey responses for patron ".$new_patron->id );
529 my $responses = $patron->survey_responses;
533 $_->usr($new_patron->id) for (@$responses);
535 my $evt = $U->simplereq( "open-ils.circ",
536 "open-ils.circ.survey.submit.user_id", $responses );
538 return (undef, $evt) if defined($U->event_code($evt));
542 return ( $new_patron, undef );
546 sub _create_stat_maps {
548 my($session, $user_session, $patron, $new_patron) = @_;
550 my $maps = $patron->stat_cat_entries();
552 for my $map (@$maps) {
554 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
556 if ($map->isdeleted()) {
557 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
559 } elsif ($map->isnew()) {
560 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
565 $map->target_usr($new_patron->id);
568 $logger->info("Updating stat entry with method $method and map $map");
570 my $stat = $session->request($method, $map)->gather(1);
571 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
575 return ($new_patron, undef);
578 sub _create_perm_maps {
580 my($session, $user_session, $patron, $new_patron) = @_;
582 my $maps = $patron->permissions;
584 for my $map (@$maps) {
586 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
587 if ($map->isdeleted()) {
588 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
589 } elsif ($map->isnew()) {
590 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
595 $map->usr($new_patron->id);
597 #warn( "Updating permissions with method $method and session $user_session and map $map" );
598 $logger->info( "Updating permissions with method $method and map $map" );
600 my $stat = $session->request($method, $map)->gather(1);
601 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
605 return ($new_patron, undef);
609 sub _create_standing_penalties {
611 my($session, $user_session, $patron, $new_patron) = @_;
613 my $maps = $patron->standing_penalties;
616 for my $map (@$maps) {
618 if ($map->isdeleted()) {
619 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
620 } elsif ($map->isnew()) {
621 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
627 $map->usr($new_patron->id);
629 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
631 my $stat = $session->request($method, $map)->gather(1);
632 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
635 return ($new_patron, undef);
640 __PACKAGE__->register_method(
641 method => "search_username",
642 api_name => "open-ils.actor.user.search.username",
645 sub search_username {
646 my($self, $client, $username) = @_;
647 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
649 "open-ils.storage.direct.actor.user.search.usrname.atomic",
657 __PACKAGE__->register_method(
658 method => "user_retrieve_by_barcode",
659 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
661 sub user_retrieve_by_barcode {
662 my($self, $client, $user_session, $barcode) = @_;
664 $logger->debug("Searching for user with barcode $barcode");
665 my ($user_obj, $evt) = $apputils->checkses($user_session);
668 my $session = OpenSRF::AppSession->create("open-ils.storage");
670 # find the card with the given barcode
671 my $creq = $session->request(
672 "open-ils.storage.direct.actor.card.search.barcode.atomic",
674 my $card = $creq->gather(1);
676 if(!$card || !$card->[0]) {
677 $session->disconnect();
678 return OpenILS::Event->new( 'USER_NOT_FOUND' );
682 my $user = flesh_user($card->usr(), $session);
683 $session->disconnect();
684 if(!$user) { return OpenILS::Event->new( 'USER_NOT_FOUND' ); }
691 __PACKAGE__->register_method(
692 method => "get_user_by_id",
693 api_name => "open-ils.actor.user.retrieve",);
696 my ($self, $client, $user_session, $id) = @_;
698 my $user_obj = $apputils->check_user_session( $user_session );
700 return $apputils->simple_scalar_request(
702 "open-ils.storage.direct.actor.user.retrieve",
708 __PACKAGE__->register_method(
709 method => "get_org_types",
710 api_name => "open-ils.actor.org_types.retrieve",);
714 my($self, $client) = @_;
716 return $org_types if $org_types;
718 $apputils->simple_scalar_request(
720 "open-ils.storage.direct.actor.org_unit_type.retrieve.all.atomic" );
725 __PACKAGE__->register_method(
726 method => "get_user_profiles",
727 api_name => "open-ils.actor.user.profiles.retrieve",
731 sub get_user_profiles {
732 return $user_profiles if $user_profiles;
734 return $user_profiles =
735 $apputils->simple_scalar_request(
737 "open-ils.storage.direct.actor.profile.retrieve.all.atomic");
742 __PACKAGE__->register_method(
743 method => "get_user_ident_types",
744 api_name => "open-ils.actor.user.ident_types.retrieve",
747 sub get_user_ident_types {
748 return $ident_types if $ident_types;
749 return $ident_types =
750 $apputils->simple_scalar_request(
752 "open-ils.storage.direct.config.identification_type.retrieve.all.atomic" );
758 __PACKAGE__->register_method(
759 method => "get_org_unit",
760 api_name => "open-ils.actor.org_unit.retrieve",
765 my( $self, $client, $user_session, $org_id ) = @_;
767 if(defined($user_session) && !defined($org_id)) {
769 OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
770 if(!defined($org_id)) {
771 $org_id = $user_obj->home_ou;
776 my $home_ou = OpenILS::Application::AppUtils->simple_scalar_request(
778 "open-ils.storage.direct.actor.org_unit.retrieve",
787 __PACKAGE__->register_method(
788 method => "get_org_tree",
789 api_name => "open-ils.actor.org_tree.retrieve",
791 note => "Returns the entire org tree structure",
795 my( $self, $client) = @_;
798 $cache_client = OpenSRF::Utils::Cache->new("global", 0);
800 # see if it's in the cache
801 #warn "Getting ORG Tree\n";
802 my $tree = $cache_client->get_cache('orgtree');
804 #warn "Found orgtree in cache. returning...\n";
808 my $orglist = $apputils->simple_scalar_request(
810 "open-ils.storage.direct.actor.org_unit.retrieve.all.atomic" );
813 #warn "found org list\n";
816 $tree = $self->build_org_tree($orglist);
817 $cache_client->put_cache('orgtree', $tree);
823 # turns an org list into an org tree
826 my( $self, $orglist) = @_;
828 return $orglist unless (
829 ref($orglist) and @$orglist > 1 );
832 $a->ou_type <=> $b->ou_type ||
833 $a->name cmp $b->name } @$orglist;
835 for my $org (@list) {
837 next unless ($org and defined($org->parent_ou));
838 my ($parent) = grep { $_->id == $org->parent_ou } @list;
841 $parent->children([]) unless defined($parent->children);
842 push( @{$parent->children}, $org );
850 __PACKAGE__->register_method(
851 method => "get_org_descendants",
852 api_name => "open-ils.actor.org_tree.descendants.retrieve"
855 # depth is optional. org_unit is the id
856 sub get_org_descendants {
857 my( $self, $client, $org_unit, $depth ) = @_;
858 my $orglist = $apputils->simple_scalar_request(
860 "open-ils.storage.actor.org_unit.descendants.atomic",
862 return $self->build_org_tree($orglist);
866 __PACKAGE__->register_method(
867 method => "get_org_ancestors",
868 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
871 # depth is optional. org_unit is the id
872 sub get_org_ancestors {
873 my( $self, $client, $org_unit, $depth ) = @_;
874 my $orglist = $apputils->simple_scalar_request(
876 "open-ils.storage.actor.org_unit.ancestors.atomic",
878 return $self->build_org_tree($orglist);
882 __PACKAGE__->register_method(
883 method => "get_standings",
884 api_name => "open-ils.actor.standings.retrieve"
889 return $user_standings if $user_standings;
890 return $user_standings =
891 $apputils->simple_scalar_request(
893 "open-ils.storage.direct.config.standing.retrieve.all.atomic" );
898 __PACKAGE__->register_method(
899 method => "get_my_org_path",
900 api_name => "open-ils.actor.org_unit.full_path.retrieve"
903 sub get_my_org_path {
904 my( $self, $client, $user_session, $org_id ) = @_;
905 my $user_obj = $apputils->check_user_session($user_session);
906 if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
908 return $apputils->simple_scalar_request(
910 "open-ils.storage.actor.org_unit.full_path.atomic",
915 __PACKAGE__->register_method(
916 method => "patron_adv_search",
917 api_name => "open-ils.actor.patron.search.advanced" );
919 sub patron_adv_search {
920 my( $self, $client, $staff_login, $search_hash ) = @_;
922 #warn "patron adv with $staff_login and search " .
923 #Dumper($search_hash) . "\n";
925 my $session = OpenSRF::AppSession->create("open-ils.storage");
926 my $req = $session->request(
927 "open-ils.storage.actor.user.crazy_search", $search_hash);
929 my $ans = $req->gather(1);
931 my %hash = map { ($_ =>1) } @$ans;
932 $ans = [ keys %hash ];
934 #warn "Returning @$ans\n";
936 $session->disconnect();
943 sub _verify_password {
944 my($user_session, $password) = @_;
945 my $user_obj = $apputils->check_user_session($user_session);
947 #grab the user with password
948 $user_obj = $apputils->simple_scalar_request(
950 "open-ils.storage.direct.actor.user.retrieve",
953 if($user_obj->passwd eq $password) {
961 __PACKAGE__->register_method(
962 method => "update_password",
963 api_name => "open-ils.actor.user.password.update");
965 __PACKAGE__->register_method(
966 method => "update_password",
967 api_name => "open-ils.actor.user.username.update");
969 __PACKAGE__->register_method(
970 method => "update_password",
971 api_name => "open-ils.actor.user.email.update");
973 sub update_password {
974 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
978 #warn "Updating user with method " .$self->api_name . "\n";
979 my $user_obj = $apputils->check_user_session($user_session);
981 if($self->api_name =~ /password/o) {
983 #make sure they know the current password
984 if(!_verify_password($user_session, md5_hex($current_password))) {
985 return OpenILS::EX->new("USER_WRONG_PASSWORD")->ex;
988 $user_obj->passwd($new_value);
990 elsif($self->api_name =~ /username/o) {
991 my $users = search_username(undef, undef, $new_value);
992 if( $users and $users->[0] ) {
993 return OpenILS::Event->new('USERNAME_EXISTS');
995 $user_obj->usrname($new_value);
998 elsif($self->api_name =~ /email/o) {
999 #warn "Updating email to $new_value\n";
1000 $user_obj->email($new_value);
1003 my $session = $apputils->start_db_session();
1005 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj);
1006 return $evt if $evt;
1008 $apputils->commit_db_session($session);
1010 if($user_obj) { return 1; }
1015 __PACKAGE__->register_method(
1016 method => "check_user_perms",
1017 api_name => "open-ils.actor.user.perm.check",
1018 notes => <<" NOTES");
1019 Takes a login session, user id, an org id, and an array of perm type strings. For each
1020 perm type, if the user does *not* have the given permission it is added
1021 to a list which is returned from the method. If all permissions
1022 are allowed, an empty list is returned
1023 if the logged in user does not match 'user_id', then the logged in user must
1024 have VIEW_PERMISSION priveleges.
1027 sub check_user_perms {
1028 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1030 my( $staff, $evt ) = $apputils->checkses($login_session);
1031 return $evt if $evt;
1033 if($staff->id ne $user_id) {
1034 if( my $evt = $apputils->check_perms(
1035 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1041 for my $perm (@$perm_types) {
1042 if($apputils->check_perms($user_id, $org_id, $perm)) {
1043 push @not_allowed, $perm;
1047 return \@not_allowed
1050 __PACKAGE__->register_method(
1051 method => "check_user_perms2",
1052 api_name => "open-ils.actor.user.perm.check.multi_org",
1054 Checks the permissions on a list of perms and orgs for a user
1055 @param authtoken The login session key
1056 @param user_id The id of the user to check
1057 @param orgs The array of org ids
1058 @param perms The array of permission names
1059 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1060 if the logged in user does not match 'user_id', then the logged in user must
1061 have VIEW_PERMISSION priveleges.
1064 sub check_user_perms2 {
1065 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1067 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1068 $authtoken, $user_id, 'VIEW_PERMISSION' );
1069 return $evt if $evt;
1072 for my $org (@$orgs) {
1073 for my $perm (@$perms) {
1074 if($apputils->check_perms($user_id, $org, $perm)) {
1075 push @not_allowed, [ $org, $perm ];
1080 return \@not_allowed
1084 __PACKAGE__->register_method(
1085 method => 'check_user_perms3',
1086 api_name => 'open-ils.actor.user.perm.highest_org',
1088 Returns the highest org unit id at which a user has a given permission
1089 If the requestor does not match the target user, the requestor must have
1090 'VIEW_PERMISSION' rights at the home org unit of the target user
1091 @param authtoken The login session key
1092 @param userid The id of the user in question
1093 @param perm The permission to check
1094 @return The org unit highest in the org tree within which the user has
1095 the requested permission
1098 sub check_user_perms3 {
1099 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1101 my( $staff, $target, $org, $evt );
1103 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1104 $authtoken, $userid, 'VIEW_PERMISSION' );
1105 return $evt if $evt;
1107 my $tree = $self->get_org_tree();
1108 return _find_highest_perm_org( $perm, $userid, $target->home_ou, $tree );
1112 sub _find_highest_perm_org {
1113 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1114 my $org = $apputils->find_org($org_tree, $start_org );
1118 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1120 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1126 __PACKAGE__->register_method(
1127 method => 'check_user_perms4',
1128 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1130 Returns the highest org unit id at which a user has a given permission
1131 If the requestor does not match the target user, the requestor must have
1132 'VIEW_PERMISSION' rights at the home org unit of the target user
1133 @param authtoken The login session key
1134 @param userid The id of the user in question
1135 @param perms An array of perm names to check
1136 @return An array of orgId's representing the org unit
1137 highest in the org tree within which the user has the requested permission
1138 The arrah of orgId's has matches the order of the perms array
1141 sub check_user_perms4 {
1142 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1144 my( $staff, $target, $org, $evt );
1146 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1147 $authtoken, $userid, 'VIEW_PERMISSION' );
1148 return $evt if $evt;
1151 return [] unless ref($perms);
1152 my $tree = $self->get_org_tree();
1154 for my $p (@$perms) {
1155 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1163 __PACKAGE__->register_method(
1164 method => "user_fines_summary",
1165 api_name => "open-ils.actor.user.fines.summary",
1166 notes => <<" NOTES");
1167 Returns a short summary of the users total open fines, excluding voided fines
1168 Params are login_session, user_id
1169 Returns a 'mous' object.
1172 sub user_fines_summary {
1173 my( $self, $client, $login_session, $user_id ) = @_;
1175 my $user_obj = $apputils->check_user_session($login_session);
1176 if($user_obj->id ne $user_id) {
1177 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1178 return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY");
1182 return $apputils->simple_scalar_request(
1184 "open-ils.storage.direct.money.open_user_summary.search.usr",
1192 __PACKAGE__->register_method(
1193 method => "user_transactions",
1194 api_name => "open-ils.actor.user.transactions",
1195 notes => <<" NOTES");
1196 Returns a list of open user transactions (mbts objects);
1197 Params are login_session, user_id
1198 Optional third parameter is the transactions type. defaults to all
1201 __PACKAGE__->register_method(
1202 method => "user_transactions",
1203 api_name => "open-ils.actor.user.transactions.have_charge",
1204 notes => <<" NOTES");
1205 Returns a list of all open user transactions (mbts objects) that have an initial charge
1206 Params are login_session, user_id
1207 Optional third parameter is the transactions type. defaults to all
1210 __PACKAGE__->register_method(
1211 method => "user_transactions",
1212 api_name => "open-ils.actor.user.transactions.have_balance",
1213 notes => <<" NOTES");
1214 Returns a list of all open user transactions (mbts objects) that have a balance
1215 Params are login_session, user_id
1216 Optional third parameter is the transactions type. defaults to all
1219 __PACKAGE__->register_method(
1220 method => "user_transactions",
1221 api_name => "open-ils.actor.user.transactions.fleshed",
1222 notes => <<" NOTES");
1223 Returns an object/hash of transaction, circ, title where transaction = an open
1224 user transactions (mbts objects), circ is the attached circluation, and title
1225 is the title the circ points to
1226 Params are login_session, user_id
1227 Optional third parameter is the transactions type. defaults to all
1230 __PACKAGE__->register_method(
1231 method => "user_transactions",
1232 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1233 notes => <<" NOTES");
1234 Returns an object/hash of transaction, circ, title where transaction = an open
1235 user transactions that has an initial charge (mbts objects), circ is the
1236 attached circluation, and title is the title the circ points to
1237 Params are login_session, user_id
1238 Optional third parameter is the transactions type. defaults to all
1241 __PACKAGE__->register_method(
1242 method => "user_transactions",
1243 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1244 notes => <<" NOTES");
1245 Returns an object/hash of transaction, circ, title where transaction = an open
1246 user transaction that has a balance (mbts objects), circ is the attached
1247 circluation, and title is the title the circ points to
1248 Params are login_session, user_id
1249 Optional third parameter is the transaction type. defaults to all
1252 __PACKAGE__->register_method(
1253 method => "user_transactions",
1254 api_name => "open-ils.actor.user.transactions.count",
1255 notes => <<" NOTES");
1256 Returns an object/hash of transaction, circ, title where transaction = an open
1257 user transactions (mbts objects), circ is the attached circluation, and title
1258 is the title the circ points to
1259 Params are login_session, user_id
1260 Optional third parameter is the transactions type. defaults to all
1263 __PACKAGE__->register_method(
1264 method => "user_transactions",
1265 api_name => "open-ils.actor.user.transactions.have_charge.count",
1266 notes => <<" NOTES");
1267 Returns an object/hash of transaction, circ, title where transaction = an open
1268 user transactions that has an initial charge (mbts objects), circ is the
1269 attached circluation, and title is the title the circ points to
1270 Params are login_session, user_id
1271 Optional third parameter is the transactions type. defaults to all
1274 __PACKAGE__->register_method(
1275 method => "user_transactions",
1276 api_name => "open-ils.actor.user.transactions.have_balance.count",
1277 notes => <<" NOTES");
1278 Returns an object/hash of transaction, circ, title where transaction = an open
1279 user transaction that has a balance (mbts objects), circ is the attached
1280 circluation, and title is the title the circ points to
1281 Params are login_session, user_id
1282 Optional third parameter is the transaction type. defaults to all
1285 __PACKAGE__->register_method(
1286 method => "user_transactions",
1287 api_name => "open-ils.actor.user.transactions.have_balance.total",
1288 notes => <<" NOTES");
1289 Returns an object/hash of transaction, circ, title where transaction = an open
1290 user transaction that has a balance (mbts objects), circ is the attached
1291 circluation, and title is the title the circ points to
1292 Params are login_session, user_id
1293 Optional third parameter is the transaction type. defaults to all
1298 sub user_transactions {
1299 my( $self, $client, $login_session, $user_id, $type ) = @_;
1301 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1302 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1303 return $evt if $evt;
1305 my $api = $self->api_name();
1309 if(defined($type)) { @xact = (xact_type => $type);
1311 } else { @xact = (); }
1313 if($api =~ /have_charge/o) {
1315 $trans = $apputils->simple_scalar_request(
1317 "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1318 { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1320 } elsif($api =~ /have_balance/o) {
1322 $trans = $apputils->simple_scalar_request(
1324 "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1325 { usr => $user_id, balance_owed => { ">" => 0 }, @xact });
1329 $trans = $apputils->simple_scalar_request(
1331 "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1332 { usr => $user_id, @xact });
1335 if($api =~ /total/o) {
1337 for my $t (@$trans) {
1338 $total += $t->balance_owed;
1341 $logger->debug("Total balance owed by user $user_id: $total");
1345 if($api =~ /count/o) { return scalar @$trans; }
1346 if($api !~ /fleshed/o) { return $trans; }
1348 #warn "API: $api\n";
1351 for my $t (@$trans) {
1353 #warn $t->id . "\n";
1356 if( $t->xact_type ne 'circulation' ) {
1357 push @resp, {transaction => $t};
1361 my $circ = $apputils->simple_scalar_request(
1363 "open-ils.storage.direct.action.circulation.retrieve",
1368 my $title = $apputils->simple_scalar_request(
1370 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1371 $circ->target_copy );
1375 my $u = OpenILS::Utils::ModsParser->new();
1376 $u->start_mods_batch($title->marc());
1377 my $mods = $u->finish_mods_batch();
1379 push @resp, {transaction => $t, circ => $circ, record => $mods };
1387 __PACKAGE__->register_method(
1388 method => "user_transaction_retrieve",
1389 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1391 notes => <<" NOTES");
1392 Returns a fleshedtransaction record
1394 __PACKAGE__->register_method(
1395 method => "user_transaction_retrieve",
1396 api_name => "open-ils.actor.user.transaction.retrieve",
1398 notes => <<" NOTES");
1399 Returns a transaction record
1401 sub user_transaction_retrieve {
1402 my( $self, $client, $login_session, $bill_id ) = @_;
1404 my $trans = $apputils->simple_scalar_request(
1406 "open-ils.storage.direct.money.billable_transaction_summary.retrieve",
1410 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1411 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1412 return $evt if $evt;
1414 my $api = $self->api_name();
1415 if($api !~ /fleshed/o) { return $trans; }
1417 if( $trans->xact_type ne 'circulation' ) {
1418 $logger->debug("Returning non-circ transaction");
1419 return {transaction => $trans};
1422 my $circ = $apputils->simple_scalar_request(
1424 "open-ils.storage.direct.action.circulation.retrieve",
1427 return {transaction => $trans} unless $circ;
1428 $logger->debug("Found the circ transaction");
1430 my $title = $apputils->simple_scalar_request(
1432 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1433 $circ->target_copy );
1435 return {transaction => $trans, circ => $circ } unless $title;
1436 $logger->debug("Found the circ title");
1440 my $u = OpenILS::Utils::ModsParser->new();
1441 $u->start_mods_batch($title->marc());
1442 $mods = $u->finish_mods_batch();
1444 if ($title->id == -1) {
1445 my $copy = $apputils->simple_scalar_request(
1447 "open-ils.storage.direct.asset.copy.retrieve",
1448 $circ->target_copy );
1450 $mods = new Fieldmapper::metabib::virtual_record;
1452 $mods->title($copy->dummy_title);
1453 $mods->author($copy->dummy_author);
1457 $logger->debug("MODSized the circ title");
1459 return {transaction => $trans, circ => $circ, record => $mods };
1463 __PACKAGE__->register_method(
1464 method => "hold_request_count",
1465 api_name => "open-ils.actor.user.hold_requests.count",
1467 notes => <<" NOTES");
1468 Returns hold ready/total counts
1470 sub hold_request_count {
1471 my( $self, $client, $login_session, $userid ) = @_;
1473 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1474 $login_session, $userid, 'VIEW_HOLD' );
1475 return $evt if $evt;
1478 my $holds = $apputils->simple_scalar_request(
1480 "open-ils.storage.direct.action.hold_request.search_where.atomic",
1482 fulfillment_time => {"=" => undef } }
1486 for my $h (@$holds) {
1487 next unless $h->capture_time;
1489 my $copy = $apputils->simple_scalar_request(
1491 "open-ils.storage.direct.asset.copy.retrieve",
1495 if ($copy->status == 8) {
1500 return { total => scalar(@$holds), ready => scalar(@ready) };
1504 __PACKAGE__->register_method(
1505 method => "checkedout_count",
1506 api_name => "open-ils.actor.user.checked_out.count",
1508 notes => <<" NOTES");
1509 Returns a transaction record
1511 sub checkedout_count {
1512 my( $self, $client, $login_session, $userid ) = @_;
1514 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1515 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1516 return $evt if $evt;
1519 my $circs = $apputils->simple_scalar_request(
1521 "open-ils.storage.direct.action.circulation.search_where.atomic",
1523 checkin_time => {"=" => undef } }
1526 my $parser = DateTime::Format::ISO8601->new;
1529 for my $c (@$circs) {
1530 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1531 my $due = $due_dt->epoch;
1538 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1541 __PACKAGE__->register_method(
1542 method => "user_transaction_history",
1543 api_name => "open-ils.actor.user.transactions.history",
1545 notes => <<" NOTES");
1546 Returns a list of billable transaction ids for a user, optionally by type
1548 sub user_transaction_history {
1549 my( $self, $client, $login_session, $user_id, $type ) = @_;
1551 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1552 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1553 return $evt if $evt;
1555 my $api = $self->api_name();
1558 @xact = (xact_type => $type) if(defined($type));
1560 my $trans = $apputils->simple_scalar_request(
1562 "open-ils.storage.direct.money.billable_transaction_summary.search_where.atomic",
1563 { usr => $user_id, @xact }, { order_by => 'xact_start DESC' });
1565 return [ map { $_->id } @$trans ];
1569 __PACKAGE__->register_method(
1570 method => "user_perms",
1571 api_name => "open-ils.actor.permissions.user_perms.retrieve",
1573 notes => <<" NOTES");
1574 Returns a list of permissions
1577 my( $self, $client, $authtoken, $user ) = @_;
1579 my( $staff, $evt ) = $apputils->checkses($authtoken);
1580 return $evt if $evt;
1582 $user ||= $staff->id;
1584 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1588 return $apputils->simple_scalar_request(
1590 "open-ils.storage.permission.user_perms.atomic",
1594 __PACKAGE__->register_method(
1595 method => "retrieve_perms",
1596 api_name => "open-ils.actor.permissions.retrieve",
1597 notes => <<" NOTES");
1598 Returns a list of permissions
1600 sub retrieve_perms {
1601 my( $self, $client ) = @_;
1602 return $apputils->simple_scalar_request(
1604 "open-ils.storage.direct.permission.perm_list.retrieve.all.atomic");
1607 __PACKAGE__->register_method(
1608 method => "retrieve_groups",
1609 api_name => "open-ils.actor.groups.retrieve",
1610 notes => <<" NOTES");
1611 Returns a list of user groupss
1613 sub retrieve_groups {
1614 my( $self, $client ) = @_;
1615 return $apputils->simple_scalar_request(
1617 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1620 __PACKAGE__->register_method(
1621 method => "retrieve_groups_tree",
1622 api_name => "open-ils.actor.groups.tree.retrieve",
1623 notes => <<" NOTES");
1624 Returns a list of user groups
1626 sub retrieve_groups_tree {
1627 my( $self, $client ) = @_;
1628 my $groups = $apputils->simple_scalar_request(
1630 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1631 return $self->build_group_tree($groups);
1635 # turns an org list into an org tree
1636 sub build_group_tree {
1638 my( $self, $grplist) = @_;
1640 return $grplist unless (
1641 ref($grplist) and @$grplist > 1 );
1643 my @list = sort { $a->name cmp $b->name } @$grplist;
1646 for my $grp (@list) {
1648 if ($grp and !defined($grp->parent)) {
1652 my ($parent) = grep { $_->id == $grp->parent} @list;
1654 $parent->children([]) unless defined($parent->children);
1655 push( @{$parent->children}, $grp );
1663 __PACKAGE__->register_method(
1664 method => "add_user_to_groups",
1665 api_name => "open-ils.actor.user.set_groups",
1666 notes => <<" NOTES");
1667 Adds a user to one or more permission groups
1670 sub add_user_to_groups {
1671 my( $self, $client, $authtoken, $userid, $groups ) = @_;
1673 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1674 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
1675 return $evt if $evt;
1677 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1678 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
1679 return $evt if $evt;
1681 $apputils->simplereq(
1683 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
1685 for my $group (@$groups) {
1686 my $link = Fieldmapper::permission::usr_grp_map->new;
1688 $link->usr($userid);
1690 my $id = $apputils->simplereq(
1692 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
1698 __PACKAGE__->register_method(
1699 method => "get_user_perm_groups",
1700 api_name => "open-ils.actor.user.get_groups",
1701 notes => <<" NOTES");
1702 Retrieve a user's permission groups.
1706 sub get_user_perm_groups {
1707 my( $self, $client, $authtoken, $userid ) = @_;
1709 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1710 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
1711 return $evt if $evt;
1713 return $apputils->simplereq(
1715 'open-ils.storage.direct.permission.usr_grp_map.search.usr.atomic', $userid );