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);
221 # fleshes: card, cards, address, addresses, stat_cat_entries, standing_penalties
229 $session = OpenSRF::AppSession->create("open-ils.storage");
233 # grab the user with the given id
234 my $ureq = $session->request(
235 "open-ils.storage.direct.actor.user.retrieve", $id);
236 my $user = $ureq->gather(1);
238 if(!$user) { return undef; }
241 my $cards_req = $session->request(
242 "open-ils.storage.direct.actor.card.search.usr.atomic",
244 $user->cards( $cards_req->gather(1) );
246 for my $c(@{$user->cards}) {
247 if($c->id == $user->card || $c->id eq $user->card ) {
248 #warn "Setting my card to " . $c->id . "\n";
253 my $add_req = $session->request(
254 "open-ils.storage.direct.actor.user_address.search.usr.atomic",
256 $user->addresses( $add_req->gather(1) );
258 for my $c(@{$user->addresses}) {
259 if($c->id eq $user->billing_address ) { $user->billing_address($c); }
260 if($c->id eq $user->mailing_address ) { $user->mailing_address($c); }
263 my $stat_req = $session->request(
264 "open-ils.storage.direct.actor.stat_cat_entry_user_map.search.target_usr.atomic",
266 $user->stat_cat_entries($stat_req->gather(1));
268 my $standing_penalties_req = $session->request(
269 "open-ils.storage.direct.actor.user_standing_penalty.search.usr.atomic",
271 $user->standing_penalties($standing_penalties_req->gather(1));
273 if($kill) { $session->disconnect(); }
274 $user->clear_passwd();
280 # clone and clear stuff that would break the database
284 my $new_patron = $patron->clone;
286 # Using the Fieldmapper clone method
287 #my $new_patron = Fieldmapper::actor::user->new();
289 #my $fmap = $Fieldmapper::fieldmap;
290 #no strict; # shallow clone, may be useful in the fieldmapper
292 # (keys %{$fmap->{"Fieldmapper::actor::user"}->{'fields'}}) {
293 # $new_patron->$field( $patron->$field() );
298 $new_patron->clear_billing_address();
299 $new_patron->clear_mailing_address();
300 $new_patron->clear_addresses();
301 $new_patron->clear_card();
302 $new_patron->clear_cards();
303 $new_patron->clear_id();
304 $new_patron->clear_isnew();
305 $new_patron->clear_ischanged();
306 $new_patron->clear_isdeleted();
307 $new_patron->clear_stat_cat_entries();
308 $new_patron->clear_permissions();
309 $new_patron->clear_standing_penalties();
319 my $user_obj = shift;
321 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
322 return (undef, $evt) if $evt;
324 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
326 my $id = $session->request(
327 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
328 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
330 $logger->info("Successfully created new user [$id] in DB");
332 return ( $session->request(
333 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
338 my( $session, $patron, $user_obj) = @_;
340 $logger->info("Updating patron ".$patron->id." in DB");
341 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
342 return (undef, $evt) if $evt;
344 $patron->clear_passwd unless $patron->passwd;
346 my $stat = $session->request(
347 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
348 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
354 sub _add_update_addresses {
358 my $new_patron = shift;
362 my $current_id; # id of the address before creation
364 for my $address (@{$patron->addresses()}) {
366 $address->usr($new_patron->id());
368 if(ref($address) and $address->isnew()) {
370 $current_id = $address->id();
371 ($address, $evt) = _add_address($session,$address);
372 return (undef, $evt) if $evt;
374 if( $patron->billing_address() and
375 $patron->billing_address() == $current_id ) {
376 $new_patron->billing_address($address->id());
377 $new_patron->ischanged(1);
380 if( $patron->mailing_address() and
381 $patron->mailing_address() == $current_id ) {
382 $new_patron->mailing_address($address->id());
383 $new_patron->ischanged(1);
386 } elsif( ref($address) and $address->ischanged() ) {
388 $address->usr($new_patron->id());
389 ($address, $evt) = _update_address($session, $address);
390 return (undef, $evt) if $evt;
392 } elsif( ref($address) and $address->isdeleted() ) {
394 if( $address->id() == $new_patron->mailing_address() ) {
395 $new_patron->clear_mailing_address();
396 ($new_patron, $evt) = _update_patron($session, $new_patron);
397 return (undef, $evt) if $evt;
400 if( $address->id() == $new_patron->billing_address() ) {
401 $new_patron->clear_billing_address();
402 ($new_patron, $evt) = _update_patron($session, $new_patron);
403 return (undef, $evt) if $evt;
406 $evt = _delete_address($session, $address);
407 return (undef, $evt) if $evt;
411 return ( $new_patron, undef );
415 # adds an address to the db and returns the address with new id
417 my($session, $address) = @_;
418 $address->clear_id();
420 $logger->info("Creating new address at street ".$address->street1);
422 # put the address into the database
423 my $id = $session->request(
424 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
425 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
428 return ($address, undef);
432 sub _update_address {
433 my( $session, $address ) = @_;
435 $logger->info("Updating address ".$address->id." in the DB");
437 my $stat = $session->request(
438 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
440 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
441 return ($address, undef);
446 sub _add_update_cards {
450 my $new_patron = shift;
454 my $virtual_id; #id of the card before creation
455 for my $card (@{$patron->cards()}) {
457 $card->usr($new_patron->id());
459 if(ref($card) and $card->isnew()) {
461 $virtual_id = $card->id();
462 ( $card, $evt ) = _add_card($session,$card);
463 return (undef, $evt) if $evt;
465 #if(ref($patron->card)) { $patron->card($patron->card->id); }
466 if($patron->card() == $virtual_id) {
467 $new_patron->card($card->id());
468 $new_patron->ischanged(1);
471 } elsif( ref($card) and $card->ischanged() ) {
472 $card->usr($new_patron->id());
473 $evt = _update_card($session, $card);
474 return (undef, $evt) if $evt;
478 return ( $new_patron, undef );
482 # adds an card to the db and returns the card with new id
484 my( $session, $card ) = @_;
487 $logger->info("Adding new patron card ".$card->barcode);
489 my $id = $session->request(
490 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
491 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
492 $logger->info("Successfully created patron card $id");
495 return ( $card, undef );
499 # returns event on error. returns undef otherwise
501 my( $session, $card ) = @_;
502 $logger->info("Updating patron card ".$card->id);
504 my $stat = $session->request(
505 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
506 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
513 # returns event on error. returns undef otherwise
514 sub _delete_address {
515 my( $session, $address ) = @_;
517 $logger->info("Deleting address ".$address->id." from DB");
519 my $stat = $session->request(
520 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
522 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
528 sub _add_survey_responses {
529 my ($session, $patron, $new_patron) = @_;
531 $logger->info( "Updating survey responses for patron ".$new_patron->id );
533 my $responses = $patron->survey_responses;
537 $_->usr($new_patron->id) for (@$responses);
539 my $evt = $U->simplereq( "open-ils.circ",
540 "open-ils.circ.survey.submit.user_id", $responses );
542 return (undef, $evt) if defined($U->event_code($evt));
546 return ( $new_patron, undef );
550 sub _create_stat_maps {
552 my($session, $user_session, $patron, $new_patron) = @_;
554 my $maps = $patron->stat_cat_entries();
556 for my $map (@$maps) {
558 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
560 if ($map->isdeleted()) {
561 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
563 } elsif ($map->isnew()) {
564 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
569 $map->target_usr($new_patron->id);
572 $logger->info("Updating stat entry with method $method and map $map");
574 my $stat = $session->request($method, $map)->gather(1);
575 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
579 return ($new_patron, undef);
582 sub _create_perm_maps {
584 my($session, $user_session, $patron, $new_patron) = @_;
586 my $maps = $patron->permissions;
588 for my $map (@$maps) {
590 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
591 if ($map->isdeleted()) {
592 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
593 } elsif ($map->isnew()) {
594 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
599 $map->usr($new_patron->id);
601 #warn( "Updating permissions with method $method and session $user_session and map $map" );
602 $logger->info( "Updating permissions with method $method and map $map" );
604 my $stat = $session->request($method, $map)->gather(1);
605 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
609 return ($new_patron, undef);
613 sub _create_standing_penalties {
615 my($session, $user_session, $patron, $new_patron) = @_;
617 my $maps = $patron->standing_penalties;
620 for my $map (@$maps) {
622 if ($map->isdeleted()) {
623 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
624 } elsif ($map->isnew()) {
625 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
631 $map->usr($new_patron->id);
633 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
635 my $stat = $session->request($method, $map)->gather(1);
636 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
639 return ($new_patron, undef);
644 __PACKAGE__->register_method(
645 method => "search_username",
646 api_name => "open-ils.actor.user.search.username",
649 sub search_username {
650 my($self, $client, $username) = @_;
651 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
653 "open-ils.storage.direct.actor.user.search.usrname.atomic",
661 __PACKAGE__->register_method(
662 method => "user_retrieve_by_barcode",
663 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
665 sub user_retrieve_by_barcode {
666 my($self, $client, $user_session, $barcode) = @_;
668 $logger->debug("Searching for user with barcode $barcode");
669 my ($user_obj, $evt) = $apputils->checkses($user_session);
672 my $session = OpenSRF::AppSession->create("open-ils.storage");
674 # find the card with the given barcode
675 my $creq = $session->request(
676 "open-ils.storage.direct.actor.card.search.barcode.atomic",
678 my $card = $creq->gather(1);
680 if(!$card || !$card->[0]) {
681 $session->disconnect();
682 return OpenILS::Event->new( 'USER_NOT_FOUND' );
686 my $user = flesh_user($card->usr(), $session);
687 $session->disconnect();
688 if(!$user) { return OpenILS::Event->new( 'USER_NOT_FOUND' ); }
695 __PACKAGE__->register_method(
696 method => "get_user_by_id",
697 api_name => "open-ils.actor.user.retrieve",);
700 my ($self, $client, $user_session, $id) = @_;
702 my $user_obj = $apputils->check_user_session( $user_session );
704 return $apputils->simple_scalar_request(
706 "open-ils.storage.direct.actor.user.retrieve",
712 __PACKAGE__->register_method(
713 method => "get_org_types",
714 api_name => "open-ils.actor.org_types.retrieve",);
718 my($self, $client) = @_;
720 return $org_types if $org_types;
722 $apputils->simple_scalar_request(
724 "open-ils.storage.direct.actor.org_unit_type.retrieve.all.atomic" );
729 __PACKAGE__->register_method(
730 method => "get_user_profiles",
731 api_name => "open-ils.actor.user.profiles.retrieve",
735 sub get_user_profiles {
736 return $user_profiles if $user_profiles;
738 return $user_profiles =
739 $apputils->simple_scalar_request(
741 "open-ils.storage.direct.actor.profile.retrieve.all.atomic");
746 __PACKAGE__->register_method(
747 method => "get_user_ident_types",
748 api_name => "open-ils.actor.user.ident_types.retrieve",
751 sub get_user_ident_types {
752 return $ident_types if $ident_types;
753 return $ident_types =
754 $apputils->simple_scalar_request(
756 "open-ils.storage.direct.config.identification_type.retrieve.all.atomic" );
762 __PACKAGE__->register_method(
763 method => "get_org_unit",
764 api_name => "open-ils.actor.org_unit.retrieve",
769 my( $self, $client, $user_session, $org_id ) = @_;
771 if(defined($user_session) && !defined($org_id)) {
773 OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
774 if(!defined($org_id)) {
775 $org_id = $user_obj->home_ou;
780 my $home_ou = OpenILS::Application::AppUtils->simple_scalar_request(
782 "open-ils.storage.direct.actor.org_unit.retrieve",
791 __PACKAGE__->register_method(
792 method => "get_org_tree",
793 api_name => "open-ils.actor.org_tree.retrieve",
795 note => "Returns the entire org tree structure",
799 my( $self, $client) = @_;
802 $cache_client = OpenSRF::Utils::Cache->new("global", 0);
804 # see if it's in the cache
805 #warn "Getting ORG Tree\n";
806 my $tree = $cache_client->get_cache('orgtree');
808 #warn "Found orgtree in cache. returning...\n";
812 my $orglist = $apputils->simple_scalar_request(
814 "open-ils.storage.direct.actor.org_unit.retrieve.all.atomic" );
817 #warn "found org list\n";
820 $tree = $self->build_org_tree($orglist);
821 $cache_client->put_cache('orgtree', $tree);
827 # turns an org list into an org tree
830 my( $self, $orglist) = @_;
832 return $orglist unless (
833 ref($orglist) and @$orglist > 1 );
836 $a->ou_type <=> $b->ou_type ||
837 $a->name cmp $b->name } @$orglist;
839 for my $org (@list) {
841 next unless ($org and defined($org->parent_ou));
842 my ($parent) = grep { $_->id == $org->parent_ou } @list;
845 $parent->children([]) unless defined($parent->children);
846 push( @{$parent->children}, $org );
854 __PACKAGE__->register_method(
855 method => "get_org_descendants",
856 api_name => "open-ils.actor.org_tree.descendants.retrieve"
859 # depth is optional. org_unit is the id
860 sub get_org_descendants {
861 my( $self, $client, $org_unit, $depth ) = @_;
862 my $orglist = $apputils->simple_scalar_request(
864 "open-ils.storage.actor.org_unit.descendants.atomic",
866 return $self->build_org_tree($orglist);
870 __PACKAGE__->register_method(
871 method => "get_org_ancestors",
872 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
875 # depth is optional. org_unit is the id
876 sub get_org_ancestors {
877 my( $self, $client, $org_unit, $depth ) = @_;
878 my $orglist = $apputils->simple_scalar_request(
880 "open-ils.storage.actor.org_unit.ancestors.atomic",
882 return $self->build_org_tree($orglist);
886 __PACKAGE__->register_method(
887 method => "get_standings",
888 api_name => "open-ils.actor.standings.retrieve"
893 return $user_standings if $user_standings;
894 return $user_standings =
895 $apputils->simple_scalar_request(
897 "open-ils.storage.direct.config.standing.retrieve.all.atomic" );
902 __PACKAGE__->register_method(
903 method => "get_my_org_path",
904 api_name => "open-ils.actor.org_unit.full_path.retrieve"
907 sub get_my_org_path {
908 my( $self, $client, $user_session, $org_id ) = @_;
909 my $user_obj = $apputils->check_user_session($user_session);
910 if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
912 return $apputils->simple_scalar_request(
914 "open-ils.storage.actor.org_unit.full_path.atomic",
919 __PACKAGE__->register_method(
920 method => "patron_adv_search",
921 api_name => "open-ils.actor.patron.search.advanced" );
923 sub patron_adv_search {
924 my( $self, $client, $staff_login, $search_hash ) = @_;
926 #warn "patron adv with $staff_login and search " .
927 #Dumper($search_hash) . "\n";
929 my $session = OpenSRF::AppSession->create("open-ils.storage");
930 my $req = $session->request(
931 "open-ils.storage.actor.user.crazy_search", $search_hash);
933 my $ans = $req->gather(1);
935 my %hash = map { ($_ =>1) } @$ans;
936 $ans = [ keys %hash ];
938 #warn "Returning @$ans\n";
940 $session->disconnect();
947 sub _verify_password {
948 my($user_session, $password) = @_;
949 my $user_obj = $apputils->check_user_session($user_session);
951 #grab the user with password
952 $user_obj = $apputils->simple_scalar_request(
954 "open-ils.storage.direct.actor.user.retrieve",
957 if($user_obj->passwd eq $password) {
965 __PACKAGE__->register_method(
966 method => "update_password",
967 api_name => "open-ils.actor.user.password.update");
969 __PACKAGE__->register_method(
970 method => "update_password",
971 api_name => "open-ils.actor.user.username.update");
973 __PACKAGE__->register_method(
974 method => "update_password",
975 api_name => "open-ils.actor.user.email.update");
977 sub update_password {
978 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
982 #warn "Updating user with method " .$self->api_name . "\n";
983 my $user_obj = $apputils->check_user_session($user_session);
985 if($self->api_name =~ /password/o) {
987 #make sure they know the current password
988 if(!_verify_password($user_session, md5_hex($current_password))) {
989 return OpenILS::EX->new("USER_WRONG_PASSWORD")->ex;
992 $user_obj->passwd($new_value);
994 elsif($self->api_name =~ /username/o) {
995 my $users = search_username(undef, undef, $new_value);
996 if( $users and $users->[0] ) {
997 return OpenILS::Event->new('USERNAME_EXISTS');
999 $user_obj->usrname($new_value);
1002 elsif($self->api_name =~ /email/o) {
1003 #warn "Updating email to $new_value\n";
1004 $user_obj->email($new_value);
1007 my $session = $apputils->start_db_session();
1009 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj);
1010 return $evt if $evt;
1012 $apputils->commit_db_session($session);
1014 if($user_obj) { return 1; }
1019 __PACKAGE__->register_method(
1020 method => "check_user_perms",
1021 api_name => "open-ils.actor.user.perm.check",
1022 notes => <<" NOTES");
1023 Takes a login session, user id, an org id, and an array of perm type strings. For each
1024 perm type, if the user does *not* have the given permission it is added
1025 to a list which is returned from the method. If all permissions
1026 are allowed, an empty list is returned
1027 if the logged in user does not match 'user_id', then the logged in user must
1028 have VIEW_PERMISSION priveleges.
1031 sub check_user_perms {
1032 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1034 my( $staff, $evt ) = $apputils->checkses($login_session);
1035 return $evt if $evt;
1037 if($staff->id ne $user_id) {
1038 if( my $evt = $apputils->check_perms(
1039 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1045 for my $perm (@$perm_types) {
1046 if($apputils->check_perms($user_id, $org_id, $perm)) {
1047 push @not_allowed, $perm;
1051 return \@not_allowed
1054 __PACKAGE__->register_method(
1055 method => "check_user_perms2",
1056 api_name => "open-ils.actor.user.perm.check.multi_org",
1058 Checks the permissions on a list of perms and orgs for a user
1059 @param authtoken The login session key
1060 @param user_id The id of the user to check
1061 @param orgs The array of org ids
1062 @param perms The array of permission names
1063 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1064 if the logged in user does not match 'user_id', then the logged in user must
1065 have VIEW_PERMISSION priveleges.
1068 sub check_user_perms2 {
1069 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1071 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1072 $authtoken, $user_id, 'VIEW_PERMISSION' );
1073 return $evt if $evt;
1076 for my $org (@$orgs) {
1077 for my $perm (@$perms) {
1078 if($apputils->check_perms($user_id, $org, $perm)) {
1079 push @not_allowed, [ $org, $perm ];
1084 return \@not_allowed
1088 __PACKAGE__->register_method(
1089 method => 'check_user_perms3',
1090 api_name => 'open-ils.actor.user.perm.highest_org',
1092 Returns the highest org unit id at which a user has a given permission
1093 If the requestor does not match the target user, the requestor must have
1094 'VIEW_PERMISSION' rights at the home org unit of the target user
1095 @param authtoken The login session key
1096 @param userid The id of the user in question
1097 @param perm The permission to check
1098 @return The org unit highest in the org tree within which the user has
1099 the requested permission
1102 sub check_user_perms3 {
1103 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1105 my( $staff, $target, $org, $evt );
1107 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1108 $authtoken, $userid, 'VIEW_PERMISSION' );
1109 return $evt if $evt;
1111 my $tree = $self->get_org_tree();
1112 return _find_highest_perm_org( $perm, $userid, $target->home_ou, $tree );
1116 sub _find_highest_perm_org {
1117 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1118 my $org = $apputils->find_org($org_tree, $start_org );
1122 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1124 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1130 __PACKAGE__->register_method(
1131 method => 'check_user_perms4',
1132 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1134 Returns the highest org unit id at which a user has a given permission
1135 If the requestor does not match the target user, the requestor must have
1136 'VIEW_PERMISSION' rights at the home org unit of the target user
1137 @param authtoken The login session key
1138 @param userid The id of the user in question
1139 @param perms An array of perm names to check
1140 @return An array of orgId's representing the org unit
1141 highest in the org tree within which the user has the requested permission
1142 The arrah of orgId's has matches the order of the perms array
1145 sub check_user_perms4 {
1146 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1148 my( $staff, $target, $org, $evt );
1150 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1151 $authtoken, $userid, 'VIEW_PERMISSION' );
1152 return $evt if $evt;
1155 return [] unless ref($perms);
1156 my $tree = $self->get_org_tree();
1158 for my $p (@$perms) {
1159 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1167 __PACKAGE__->register_method(
1168 method => "user_fines_summary",
1169 api_name => "open-ils.actor.user.fines.summary",
1170 notes => <<" NOTES");
1171 Returns a short summary of the users total open fines, excluding voided fines
1172 Params are login_session, user_id
1173 Returns a 'mous' object.
1176 sub user_fines_summary {
1177 my( $self, $client, $login_session, $user_id ) = @_;
1179 my $user_obj = $apputils->check_user_session($login_session);
1180 if($user_obj->id ne $user_id) {
1181 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1182 return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY");
1186 return $apputils->simple_scalar_request(
1188 "open-ils.storage.direct.money.open_user_summary.search.usr",
1196 __PACKAGE__->register_method(
1197 method => "user_transactions",
1198 api_name => "open-ils.actor.user.transactions",
1199 notes => <<" NOTES");
1200 Returns a list of open user transactions (mbts objects);
1201 Params are login_session, user_id
1202 Optional third parameter is the transactions type. defaults to all
1205 __PACKAGE__->register_method(
1206 method => "user_transactions",
1207 api_name => "open-ils.actor.user.transactions.have_charge",
1208 notes => <<" NOTES");
1209 Returns a list of all open user transactions (mbts objects) that have an initial charge
1210 Params are login_session, user_id
1211 Optional third parameter is the transactions type. defaults to all
1214 __PACKAGE__->register_method(
1215 method => "user_transactions",
1216 api_name => "open-ils.actor.user.transactions.have_balance",
1217 notes => <<" NOTES");
1218 Returns a list of all open user transactions (mbts objects) that have a balance
1219 Params are login_session, user_id
1220 Optional third parameter is the transactions type. defaults to all
1223 __PACKAGE__->register_method(
1224 method => "user_transactions",
1225 api_name => "open-ils.actor.user.transactions.fleshed",
1226 notes => <<" NOTES");
1227 Returns an object/hash of transaction, circ, title where transaction = an open
1228 user transactions (mbts objects), circ is the attached circluation, and title
1229 is the title the circ points to
1230 Params are login_session, user_id
1231 Optional third parameter is the transactions type. defaults to all
1234 __PACKAGE__->register_method(
1235 method => "user_transactions",
1236 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1237 notes => <<" NOTES");
1238 Returns an object/hash of transaction, circ, title where transaction = an open
1239 user transactions that has an initial charge (mbts objects), circ is the
1240 attached circluation, and title is the title the circ points to
1241 Params are login_session, user_id
1242 Optional third parameter is the transactions type. defaults to all
1245 __PACKAGE__->register_method(
1246 method => "user_transactions",
1247 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1248 notes => <<" NOTES");
1249 Returns an object/hash of transaction, circ, title where transaction = an open
1250 user transaction that has a balance (mbts objects), circ is the attached
1251 circluation, and title is the title the circ points to
1252 Params are login_session, user_id
1253 Optional third parameter is the transaction type. defaults to all
1256 __PACKAGE__->register_method(
1257 method => "user_transactions",
1258 api_name => "open-ils.actor.user.transactions.count",
1259 notes => <<" NOTES");
1260 Returns an object/hash of transaction, circ, title where transaction = an open
1261 user transactions (mbts objects), circ is the attached circluation, and title
1262 is the title the circ points to
1263 Params are login_session, user_id
1264 Optional third parameter is the transactions type. defaults to all
1267 __PACKAGE__->register_method(
1268 method => "user_transactions",
1269 api_name => "open-ils.actor.user.transactions.have_charge.count",
1270 notes => <<" NOTES");
1271 Returns an object/hash of transaction, circ, title where transaction = an open
1272 user transactions that has an initial charge (mbts objects), circ is the
1273 attached circluation, and title is the title the circ points to
1274 Params are login_session, user_id
1275 Optional third parameter is the transactions type. defaults to all
1278 __PACKAGE__->register_method(
1279 method => "user_transactions",
1280 api_name => "open-ils.actor.user.transactions.have_balance.count",
1281 notes => <<" NOTES");
1282 Returns an object/hash of transaction, circ, title where transaction = an open
1283 user transaction that has a balance (mbts objects), circ is the attached
1284 circluation, and title is the title the circ points to
1285 Params are login_session, user_id
1286 Optional third parameter is the transaction type. defaults to all
1289 __PACKAGE__->register_method(
1290 method => "user_transactions",
1291 api_name => "open-ils.actor.user.transactions.have_balance.total",
1292 notes => <<" NOTES");
1293 Returns an object/hash of transaction, circ, title where transaction = an open
1294 user transaction that has a balance (mbts objects), circ is the attached
1295 circluation, and title is the title the circ points to
1296 Params are login_session, user_id
1297 Optional third parameter is the transaction type. defaults to all
1302 sub user_transactions {
1303 my( $self, $client, $login_session, $user_id, $type ) = @_;
1305 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1306 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1307 return $evt if $evt;
1309 my $api = $self->api_name();
1313 if(defined($type)) { @xact = (xact_type => $type);
1315 } else { @xact = (); }
1317 if($api =~ /have_charge/o) {
1319 $trans = $apputils->simple_scalar_request(
1321 "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1322 { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1324 } elsif($api =~ /have_balance/o) {
1326 $trans = $apputils->simple_scalar_request(
1328 "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1329 { usr => $user_id, balance_owed => { ">" => 0 }, @xact });
1333 $trans = $apputils->simple_scalar_request(
1335 "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1336 { usr => $user_id, @xact });
1339 if($api =~ /total/o) {
1341 for my $t (@$trans) {
1342 $total += $t->balance_owed;
1345 $logger->debug("Total balance owed by user $user_id: $total");
1349 if($api =~ /count/o) { return scalar @$trans; }
1350 if($api !~ /fleshed/o) { return $trans; }
1352 #warn "API: $api\n";
1355 for my $t (@$trans) {
1357 #warn $t->id . "\n";
1360 if( $t->xact_type ne 'circulation' ) {
1361 push @resp, {transaction => $t};
1365 my $circ = $apputils->simple_scalar_request(
1367 "open-ils.storage.direct.action.circulation.retrieve",
1372 my $title = $apputils->simple_scalar_request(
1374 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1375 $circ->target_copy );
1379 my $u = OpenILS::Utils::ModsParser->new();
1380 $u->start_mods_batch($title->marc());
1381 my $mods = $u->finish_mods_batch();
1383 push @resp, {transaction => $t, circ => $circ, record => $mods };
1391 __PACKAGE__->register_method(
1392 method => "user_transaction_retrieve",
1393 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1395 notes => <<" NOTES");
1396 Returns a fleshedtransaction record
1398 __PACKAGE__->register_method(
1399 method => "user_transaction_retrieve",
1400 api_name => "open-ils.actor.user.transaction.retrieve",
1402 notes => <<" NOTES");
1403 Returns a transaction record
1405 sub user_transaction_retrieve {
1406 my( $self, $client, $login_session, $bill_id ) = @_;
1408 my $trans = $apputils->simple_scalar_request(
1410 "open-ils.storage.direct.money.billable_transaction_summary.retrieve",
1414 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1415 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1416 return $evt if $evt;
1418 my $api = $self->api_name();
1419 if($api !~ /fleshed/o) { return $trans; }
1421 if( $trans->xact_type ne 'circulation' ) {
1422 $logger->debug("Returning non-circ transaction");
1423 return {transaction => $trans};
1426 my $circ = $apputils->simple_scalar_request(
1428 "open-ils.storage.direct.action.circulation.retrieve",
1431 return {transaction => $trans} unless $circ;
1432 $logger->debug("Found the circ transaction");
1434 my $title = $apputils->simple_scalar_request(
1436 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1437 $circ->target_copy );
1439 return {transaction => $trans, circ => $circ } unless $title;
1440 $logger->debug("Found the circ title");
1444 my $u = OpenILS::Utils::ModsParser->new();
1445 $u->start_mods_batch($title->marc());
1446 $mods = $u->finish_mods_batch();
1448 if ($title->id == -1) {
1449 my $copy = $apputils->simple_scalar_request(
1451 "open-ils.storage.direct.asset.copy.retrieve",
1452 $circ->target_copy );
1454 $mods = new Fieldmapper::metabib::virtual_record;
1456 $mods->title($copy->dummy_title);
1457 $mods->author($copy->dummy_author);
1461 $logger->debug("MODSized the circ title");
1463 return {transaction => $trans, circ => $circ, record => $mods };
1467 __PACKAGE__->register_method(
1468 method => "hold_request_count",
1469 api_name => "open-ils.actor.user.hold_requests.count",
1471 notes => <<" NOTES");
1472 Returns hold ready/total counts
1474 sub hold_request_count {
1475 my( $self, $client, $login_session, $userid ) = @_;
1477 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1478 $login_session, $userid, 'VIEW_HOLD' );
1479 return $evt if $evt;
1482 my $holds = $apputils->simple_scalar_request(
1484 "open-ils.storage.direct.action.hold_request.search_where.atomic",
1486 fulfillment_time => {"=" => undef } }
1490 for my $h (@$holds) {
1491 next unless $h->capture_time;
1493 my $copy = $apputils->simple_scalar_request(
1495 "open-ils.storage.direct.asset.copy.retrieve",
1499 if ($copy->status == 8) {
1504 return { total => scalar(@$holds), ready => scalar(@ready) };
1508 __PACKAGE__->register_method(
1509 method => "checkedout_count",
1510 api_name => "open-ils.actor.user.checked_out.count",
1512 notes => <<" NOTES");
1513 Returns a transaction record
1515 sub checkedout_count {
1516 my( $self, $client, $login_session, $userid ) = @_;
1518 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1519 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1520 return $evt if $evt;
1523 my $circs = $apputils->simple_scalar_request(
1525 "open-ils.storage.direct.action.circulation.search_where.atomic",
1527 checkin_time => {"=" => undef } }
1530 my $parser = DateTime::Format::ISO8601->new;
1533 for my $c (@$circs) {
1534 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1535 my $due = $due_dt->epoch;
1542 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1545 __PACKAGE__->register_method(
1546 method => "user_transaction_history",
1547 api_name => "open-ils.actor.user.transactions.history",
1549 notes => <<" NOTES");
1550 Returns a list of billable transaction ids for a user, optionally by type
1552 sub user_transaction_history {
1553 my( $self, $client, $login_session, $user_id, $type ) = @_;
1555 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1556 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1557 return $evt if $evt;
1559 my $api = $self->api_name();
1562 @xact = (xact_type => $type) if(defined($type));
1564 my $trans = $apputils->simple_scalar_request(
1566 "open-ils.storage.direct.money.billable_transaction_summary.search_where.atomic",
1567 { usr => $user_id, @xact }, { order_by => 'xact_start DESC' });
1569 return [ map { $_->id } @$trans ];
1573 __PACKAGE__->register_method(
1574 method => "user_perms",
1575 api_name => "open-ils.actor.permissions.user_perms.retrieve",
1577 notes => <<" NOTES");
1578 Returns a list of permissions
1581 my( $self, $client, $authtoken, $user ) = @_;
1583 my( $staff, $evt ) = $apputils->checkses($authtoken);
1584 return $evt if $evt;
1586 $user ||= $staff->id;
1588 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1592 return $apputils->simple_scalar_request(
1594 "open-ils.storage.permission.user_perms.atomic",
1598 __PACKAGE__->register_method(
1599 method => "retrieve_perms",
1600 api_name => "open-ils.actor.permissions.retrieve",
1601 notes => <<" NOTES");
1602 Returns a list of permissions
1604 sub retrieve_perms {
1605 my( $self, $client ) = @_;
1606 return $apputils->simple_scalar_request(
1608 "open-ils.storage.direct.permission.perm_list.retrieve.all.atomic");
1611 __PACKAGE__->register_method(
1612 method => "retrieve_groups",
1613 api_name => "open-ils.actor.groups.retrieve",
1614 notes => <<" NOTES");
1615 Returns a list of user groupss
1617 sub retrieve_groups {
1618 my( $self, $client ) = @_;
1619 return $apputils->simple_scalar_request(
1621 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1624 __PACKAGE__->register_method(
1625 method => "retrieve_groups_tree",
1626 api_name => "open-ils.actor.groups.tree.retrieve",
1627 notes => <<" NOTES");
1628 Returns a list of user groups
1630 sub retrieve_groups_tree {
1631 my( $self, $client ) = @_;
1632 my $groups = $apputils->simple_scalar_request(
1634 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1635 return $self->build_group_tree($groups);
1639 # turns an org list into an org tree
1640 sub build_group_tree {
1642 my( $self, $grplist) = @_;
1644 return $grplist unless (
1645 ref($grplist) and @$grplist > 1 );
1647 my @list = sort { $a->name cmp $b->name } @$grplist;
1650 for my $grp (@list) {
1652 if ($grp and !defined($grp->parent)) {
1656 my ($parent) = grep { $_->id == $grp->parent} @list;
1658 $parent->children([]) unless defined($parent->children);
1659 push( @{$parent->children}, $grp );
1667 __PACKAGE__->register_method(
1668 method => "add_user_to_groups",
1669 api_name => "open-ils.actor.user.set_groups",
1670 notes => <<" NOTES");
1671 Adds a user to one or more permission groups
1674 sub add_user_to_groups {
1675 my( $self, $client, $authtoken, $userid, $groups ) = @_;
1677 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1678 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
1679 return $evt if $evt;
1681 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1682 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
1683 return $evt if $evt;
1685 $apputils->simplereq(
1687 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
1689 for my $group (@$groups) {
1690 my $link = Fieldmapper::permission::usr_grp_map->new;
1692 $link->usr($userid);
1694 my $id = $apputils->simplereq(
1696 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
1702 __PACKAGE__->register_method(
1703 method => "get_user_perm_groups",
1704 api_name => "open-ils.actor.user.get_groups",
1705 notes => <<" NOTES");
1706 Retrieve a user's permission groups.
1710 sub get_user_perm_groups {
1711 my( $self, $client, $authtoken, $userid ) = @_;
1713 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1714 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
1715 return $evt if $evt;
1717 return $apputils->simplereq(
1719 'open-ils.storage.direct.permission.usr_grp_map.search.usr.atomic', $userid );
1724 __PACKAGE__->register_method (
1725 method => 'register_workstation',
1726 api_name => 'open-ils.actor.workstation.register',
1728 Registers a new workstion in the system
1729 @param authtoken The login session key
1730 @param name The name of the workstation id
1731 @param owner The org unit that owns this workstation
1732 @return The workstation on success, WORKSTATION_NAME_EXISTS
1733 if the name is already in use.
1736 sub register_workstation {
1737 my( $self, $connection, $authtoken, $name, $owner ) = @_;
1738 my( $requestor, $evt ) = $U->checkses($authtoken);
1739 return $evt if $evt;
1740 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
1741 return $evt if $evt;
1743 my $ws = $U->storagereq(
1744 'open-ils.storage.direct.actor.workstation.search.name', $name );
1745 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
1747 $ws = Fieldmapper::actor::workstation->new;
1748 $ws->owning_lib($owner);
1751 my $id = $U->storagereq(
1752 'open-ils.storage.direct.actor.workstation.create', $ws );
1753 return $U->DB_UPDATE_FAILED($ws) unless $id;