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;
62 [{ usr => $user->id, name => $_}, {value => $$settings{$_}}] } keys %$settings;
64 $logger->activity("User " . $staff->id . " updating user $uid settings with: " . Dumper(\@params));
66 return $apputils->simplereq(
68 'open-ils.storage.direct.actor.user_setting.batch.merge', @params );
74 __PACKAGE__->register_method(
75 method => "set_ou_settings",
76 api_name => "open-ils.actor.org_unit.settings.update",
79 my( $self, $client, $user_session, $ouid, $settings ) = @_;
81 my( $staff, $evt ) = $apputils->checkses( $user_session );
83 $evt = $apputils->check_perms( $staff->id, $ouid, 'UPDATE_ORG_UNIT' );
88 map { [{ org_unit => $ouid, name => $_}, {value => $$settings{$_}}] } keys %$settings;
90 $logger->activity("Updating org unit [$ouid] settings with: " . Dumper($params));
92 return $apputils->simplereq(
94 'open-ils.storage.direct.actor.org_unit_setting.merge', @$params );
98 my $fetch_user_settings;
99 my $fetch_ou_settings;
101 __PACKAGE__->register_method(
102 method => "user_settings",
103 api_name => "open-ils.actor.patron.settings.retrieve",
106 my( $self, $client, $user_session, $uid ) = @_;
108 my( $staff, $user, $evt ) =
109 $apputils->checkses_requestor( $user_session, $uid, 'VIEW_USER' );
112 $logger->debug("User " . $staff->id . " fetching user $uid\n");
113 my $s = $apputils->simplereq(
115 'open-ils.storage.direct.actor.user_setting.search.usr.atomic',$uid );
117 return { map { ($_->name,$_->value) } @$s };
122 __PACKAGE__->register_method(
123 method => "ou_settings",
124 api_name => "open-ils.actor.org_unit.settings.retrieve",
127 my( $self, $client, $ouid ) = @_;
129 my $s = $apputils->simplereq(
131 'open-ils.storage.direct.actor.org_unit_setting.search.org_unit.atomic', $ouid);
133 return { map { ($_->name,$_->value) } @$s };
138 __PACKAGE__->register_method(
139 method => "update_patron",
140 api_name => "open-ils.actor.patron.update",);
143 my( $self, $client, $user_session, $patron ) = @_;
145 my $session = $apputils->start_db_session();
148 $logger->info("Creating new patron...") if $patron->isnew;
149 $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
151 my( $user_obj, $evt ) = $U->checkses($user_session);
154 # XXX does this user have permission to add/create users. Granularity?
155 # $new_patron is the patron in progress. $patron is the original patron
156 # passed in with the method. new_patron will change as the components
157 # of patron are added/updated.
161 # unflesh the real items on the patron
162 $patron->card( $patron->card->id ) if(ref($patron->card));
163 $patron->billing_address( $patron->billing_address->id )
164 if(ref($patron->billing_address));
165 $patron->mailing_address( $patron->mailing_address->id )
166 if(ref($patron->mailing_address));
168 # create/update the patron first so we can use his id
169 if($patron->isnew()) {
170 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
172 } else { $new_patron = $patron; }
174 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
177 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
180 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
183 # re-update the patron if anything has happened to him during this process
184 if($new_patron->ischanged()) {
185 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
189 #$session = OpenSRF::AppSession->create("open-ils.storage"); # why did i put this here?
191 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
194 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
197 ($new_patron, $evt) = _create_standing_penalties($session, $user_session, $patron, $new_patron, $user_obj);
200 $apputils->commit_db_session($session);
202 #warn "Patron Update/Create complete\n";
203 return flesh_user($new_patron->id());
209 __PACKAGE__->register_method(
210 method => "user_retrieve_fleshed_by_id",
211 api_name => "open-ils.actor.user.fleshed.retrieve",);
213 sub user_retrieve_fleshed_by_id {
214 my( $self, $client, $user_session, $user_id ) = @_;
216 my( $requestor, $target, $evt ) = $apputils->
217 checkses_requestor( $user_session, $user_id, 'VIEW_USER' );
220 return flesh_user($user_id);
224 # fleshes: card, cards, address, addresses, stat_cat_entries, standing_penalties
232 $session = OpenSRF::AppSession->create("open-ils.storage");
236 # grab the user with the given id
237 my $ureq = $session->request(
238 "open-ils.storage.direct.actor.user.retrieve", $id);
239 my $user = $ureq->gather(1);
241 if(!$user) { return undef; }
244 my $cards_req = $session->request(
245 "open-ils.storage.direct.actor.card.search.usr.atomic",
247 $user->cards( $cards_req->gather(1) );
249 for my $c(@{$user->cards}) {
250 if($c->id == $user->card || $c->id eq $user->card ) {
251 #warn "Setting my card to " . $c->id . "\n";
256 my $add_req = $session->request(
257 "open-ils.storage.direct.actor.user_address.search.usr.atomic",
259 $user->addresses( $add_req->gather(1) );
261 for my $c(@{$user->addresses}) {
262 if($c->id eq $user->billing_address ) { $user->billing_address($c); }
263 if($c->id eq $user->mailing_address ) { $user->mailing_address($c); }
266 my $stat_req = $session->request(
267 "open-ils.storage.direct.actor.stat_cat_entry_user_map.search.target_usr.atomic",
269 $user->stat_cat_entries($stat_req->gather(1));
271 my $standing_penalties_req = $session->request(
272 "open-ils.storage.direct.actor.user_standing_penalty.search.usr.atomic",
274 $user->standing_penalties($standing_penalties_req->gather(1));
276 if($kill) { $session->disconnect(); }
277 $user->clear_passwd();
283 # clone and clear stuff that would break the database
287 my $new_patron = $patron->clone;
289 # Using the Fieldmapper clone method
290 #my $new_patron = Fieldmapper::actor::user->new();
292 #my $fmap = $Fieldmapper::fieldmap;
293 #no strict; # shallow clone, may be useful in the fieldmapper
295 # (keys %{$fmap->{"Fieldmapper::actor::user"}->{'fields'}}) {
296 # $new_patron->$field( $patron->$field() );
301 $new_patron->clear_billing_address();
302 $new_patron->clear_mailing_address();
303 $new_patron->clear_addresses();
304 $new_patron->clear_card();
305 $new_patron->clear_cards();
306 $new_patron->clear_id();
307 $new_patron->clear_isnew();
308 $new_patron->clear_ischanged();
309 $new_patron->clear_isdeleted();
310 $new_patron->clear_stat_cat_entries();
311 $new_patron->clear_permissions();
312 $new_patron->clear_standing_penalties();
322 my $user_obj = shift;
324 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
325 return (undef, $evt) if $evt;
327 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
329 my $id = $session->request(
330 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
331 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
333 $logger->info("Successfully created new user [$id] in DB");
335 return ( $session->request(
336 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
341 my( $session, $patron, $user_obj) = @_;
343 $logger->info("Updating patron ".$patron->id." in DB");
344 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
345 return (undef, $evt) if $evt;
347 $patron->clear_passwd unless $patron->passwd;
349 my $stat = $session->request(
350 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
351 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
357 sub _add_update_addresses {
361 my $new_patron = shift;
365 my $current_id; # id of the address before creation
367 for my $address (@{$patron->addresses()}) {
369 $address->usr($new_patron->id());
371 if(ref($address) and $address->isnew()) {
373 $current_id = $address->id();
374 ($address, $evt) = _add_address($session,$address);
375 return (undef, $evt) if $evt;
377 if( $patron->billing_address() and
378 $patron->billing_address() == $current_id ) {
379 $new_patron->billing_address($address->id());
380 $new_patron->ischanged(1);
383 if( $patron->mailing_address() and
384 $patron->mailing_address() == $current_id ) {
385 $new_patron->mailing_address($address->id());
386 $new_patron->ischanged(1);
389 } elsif( ref($address) and $address->ischanged() ) {
391 $address->usr($new_patron->id());
392 ($address, $evt) = _update_address($session, $address);
393 return (undef, $evt) if $evt;
395 } elsif( ref($address) and $address->isdeleted() ) {
397 if( $address->id() == $new_patron->mailing_address() ) {
398 $new_patron->clear_mailing_address();
399 ($new_patron, $evt) = _update_patron($session, $new_patron);
400 return (undef, $evt) if $evt;
403 if( $address->id() == $new_patron->billing_address() ) {
404 $new_patron->clear_billing_address();
405 ($new_patron, $evt) = _update_patron($session, $new_patron);
406 return (undef, $evt) if $evt;
409 $evt = _delete_address($session, $address);
410 return (undef, $evt) if $evt;
414 return ( $new_patron, undef );
418 # adds an address to the db and returns the address with new id
420 my($session, $address) = @_;
421 $address->clear_id();
423 $logger->info("Creating new address at street ".$address->street1);
425 # put the address into the database
426 my $id = $session->request(
427 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
428 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
431 return ($address, undef);
435 sub _update_address {
436 my( $session, $address ) = @_;
438 $logger->info("Updating address ".$address->id." in the DB");
440 my $stat = $session->request(
441 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
443 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
444 return ($address, undef);
449 sub _add_update_cards {
453 my $new_patron = shift;
457 my $virtual_id; #id of the card before creation
458 for my $card (@{$patron->cards()}) {
460 $card->usr($new_patron->id());
462 if(ref($card) and $card->isnew()) {
464 $virtual_id = $card->id();
465 ( $card, $evt ) = _add_card($session,$card);
466 return (undef, $evt) if $evt;
468 #if(ref($patron->card)) { $patron->card($patron->card->id); }
469 if($patron->card() == $virtual_id) {
470 $new_patron->card($card->id());
471 $new_patron->ischanged(1);
474 } elsif( ref($card) and $card->ischanged() ) {
475 $card->usr($new_patron->id());
476 $evt = _update_card($session, $card);
477 return (undef, $evt) if $evt;
481 return ( $new_patron, undef );
485 # adds an card to the db and returns the card with new id
487 my( $session, $card ) = @_;
490 $logger->info("Adding new patron card ".$card->barcode);
492 my $id = $session->request(
493 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
494 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
495 $logger->info("Successfully created patron card $id");
498 return ( $card, undef );
502 # returns event on error. returns undef otherwise
504 my( $session, $card ) = @_;
505 $logger->info("Updating patron card ".$card->id);
507 my $stat = $session->request(
508 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
509 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
516 # returns event on error. returns undef otherwise
517 sub _delete_address {
518 my( $session, $address ) = @_;
520 $logger->info("Deleting address ".$address->id." from DB");
522 my $stat = $session->request(
523 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
525 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
531 sub _add_survey_responses {
532 my ($session, $patron, $new_patron) = @_;
534 $logger->info( "Updating survey responses for patron ".$new_patron->id );
536 my $responses = $patron->survey_responses;
540 $_->usr($new_patron->id) for (@$responses);
542 my $evt = $U->simplereq( "open-ils.circ",
543 "open-ils.circ.survey.submit.user_id", $responses );
545 return (undef, $evt) if defined($U->event_code($evt));
549 return ( $new_patron, undef );
553 sub _create_stat_maps {
555 my($session, $user_session, $patron, $new_patron) = @_;
557 my $maps = $patron->stat_cat_entries();
559 for my $map (@$maps) {
561 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
563 if ($map->isdeleted()) {
564 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
566 } elsif ($map->isnew()) {
567 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
572 $map->target_usr($new_patron->id);
575 $logger->info("Updating stat entry with method $method and map $map");
577 my $stat = $session->request($method, $map)->gather(1);
578 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
582 return ($new_patron, undef);
585 sub _create_perm_maps {
587 my($session, $user_session, $patron, $new_patron) = @_;
589 my $maps = $patron->permissions;
591 for my $map (@$maps) {
593 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
594 if ($map->isdeleted()) {
595 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
596 } elsif ($map->isnew()) {
597 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
602 $map->usr($new_patron->id);
604 #warn( "Updating permissions with method $method and session $user_session and map $map" );
605 $logger->info( "Updating permissions with method $method and map $map" );
607 my $stat = $session->request($method, $map)->gather(1);
608 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
612 return ($new_patron, undef);
616 sub _create_standing_penalties {
618 my($session, $user_session, $patron, $new_patron) = @_;
620 my $maps = $patron->standing_penalties;
623 for my $map (@$maps) {
625 if ($map->isdeleted()) {
626 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
627 } elsif ($map->isnew()) {
628 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
634 $map->usr($new_patron->id);
636 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
638 my $stat = $session->request($method, $map)->gather(1);
639 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
642 return ($new_patron, undef);
647 __PACKAGE__->register_method(
648 method => "search_username",
649 api_name => "open-ils.actor.user.search.username",
652 sub search_username {
653 my($self, $client, $username) = @_;
654 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
656 "open-ils.storage.direct.actor.user.search.usrname.atomic",
664 __PACKAGE__->register_method(
665 method => "user_retrieve_by_barcode",
666 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
668 sub user_retrieve_by_barcode {
669 my($self, $client, $user_session, $barcode) = @_;
671 $logger->debug("Searching for user with barcode $barcode");
672 my ($user_obj, $evt) = $apputils->checkses($user_session);
675 my $session = OpenSRF::AppSession->create("open-ils.storage");
677 # find the card with the given barcode
678 my $creq = $session->request(
679 "open-ils.storage.direct.actor.card.search.barcode.atomic",
681 my $card = $creq->gather(1);
683 if(!$card || !$card->[0]) {
684 $session->disconnect();
685 return OpenILS::Event->new( 'USER_NOT_FOUND' );
689 my $user = flesh_user($card->usr(), $session);
690 $session->disconnect();
691 if(!$user) { return OpenILS::Event->new( 'USER_NOT_FOUND' ); }
698 __PACKAGE__->register_method(
699 method => "get_user_by_id",
700 api_name => "open-ils.actor.user.retrieve",);
703 my ($self, $client, $user_session, $id) = @_;
705 my $user_obj = $apputils->check_user_session( $user_session );
707 return $apputils->simple_scalar_request(
709 "open-ils.storage.direct.actor.user.retrieve",
715 __PACKAGE__->register_method(
716 method => "get_org_types",
717 api_name => "open-ils.actor.org_types.retrieve",);
721 my($self, $client) = @_;
723 return $org_types if $org_types;
725 $apputils->simple_scalar_request(
727 "open-ils.storage.direct.actor.org_unit_type.retrieve.all.atomic" );
732 __PACKAGE__->register_method(
733 method => "get_user_profiles",
734 api_name => "open-ils.actor.user.profiles.retrieve",
738 sub get_user_profiles {
739 return $user_profiles if $user_profiles;
741 return $user_profiles =
742 $apputils->simple_scalar_request(
744 "open-ils.storage.direct.actor.profile.retrieve.all.atomic");
749 __PACKAGE__->register_method(
750 method => "get_user_ident_types",
751 api_name => "open-ils.actor.user.ident_types.retrieve",
754 sub get_user_ident_types {
755 return $ident_types if $ident_types;
756 return $ident_types =
757 $apputils->simple_scalar_request(
759 "open-ils.storage.direct.config.identification_type.retrieve.all.atomic" );
765 __PACKAGE__->register_method(
766 method => "get_org_unit",
767 api_name => "open-ils.actor.org_unit.retrieve",
772 my( $self, $client, $user_session, $org_id ) = @_;
774 if(defined($user_session) && !defined($org_id)) {
776 OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
777 if(!defined($org_id)) {
778 $org_id = $user_obj->home_ou;
783 my $home_ou = OpenILS::Application::AppUtils->simple_scalar_request(
785 "open-ils.storage.direct.actor.org_unit.retrieve",
791 __PACKAGE__->register_method(
792 method => "search_org_unit",
793 api_name => "open-ils.actor.org_unit_list.search",
796 sub search_org_unit {
798 my( $self, $client, $field, $value ) = @_;
800 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
802 "open-ils.storage.direct.actor.org_unit.search.$field.atomic",
811 __PACKAGE__->register_method(
812 method => "get_org_tree",
813 api_name => "open-ils.actor.org_tree.retrieve",
815 note => "Returns the entire org tree structure",
819 my( $self, $client) = @_;
822 $cache_client = OpenSRF::Utils::Cache->new("global", 0);
824 # see if it's in the cache
825 #warn "Getting ORG Tree\n";
826 my $tree = $cache_client->get_cache('orgtree');
828 #warn "Found orgtree in cache. returning...\n";
832 my $orglist = $apputils->simple_scalar_request(
834 "open-ils.storage.direct.actor.org_unit.retrieve.all.atomic" );
837 #warn "found org list\n";
840 $tree = $self->build_org_tree($orglist);
841 $cache_client->put_cache('orgtree', $tree);
847 # turns an org list into an org tree
850 my( $self, $orglist) = @_;
852 return $orglist unless (
853 ref($orglist) and @$orglist > 1 );
856 $a->ou_type <=> $b->ou_type ||
857 $a->name cmp $b->name } @$orglist;
859 for my $org (@list) {
861 next unless ($org and defined($org->parent_ou));
862 my ($parent) = grep { $_->id == $org->parent_ou } @list;
865 $parent->children([]) unless defined($parent->children);
866 push( @{$parent->children}, $org );
874 __PACKAGE__->register_method(
875 method => "get_org_descendants",
876 api_name => "open-ils.actor.org_tree.descendants.retrieve"
879 # depth is optional. org_unit is the id
880 sub get_org_descendants {
881 my( $self, $client, $org_unit, $depth ) = @_;
882 my $orglist = $apputils->simple_scalar_request(
884 "open-ils.storage.actor.org_unit.descendants.atomic",
886 return $self->build_org_tree($orglist);
890 __PACKAGE__->register_method(
891 method => "get_org_ancestors",
892 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
895 # depth is optional. org_unit is the id
896 sub get_org_ancestors {
897 my( $self, $client, $org_unit, $depth ) = @_;
898 my $orglist = $apputils->simple_scalar_request(
900 "open-ils.storage.actor.org_unit.ancestors.atomic",
902 return $self->build_org_tree($orglist);
906 __PACKAGE__->register_method(
907 method => "get_standings",
908 api_name => "open-ils.actor.standings.retrieve"
913 return $user_standings if $user_standings;
914 return $user_standings =
915 $apputils->simple_scalar_request(
917 "open-ils.storage.direct.config.standing.retrieve.all.atomic" );
922 __PACKAGE__->register_method(
923 method => "get_my_org_path",
924 api_name => "open-ils.actor.org_unit.full_path.retrieve"
927 sub get_my_org_path {
928 my( $self, $client, $user_session, $org_id ) = @_;
929 my $user_obj = $apputils->check_user_session($user_session);
930 if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
932 return $apputils->simple_scalar_request(
934 "open-ils.storage.actor.org_unit.full_path.atomic",
939 __PACKAGE__->register_method(
940 method => "patron_adv_search",
941 api_name => "open-ils.actor.patron.search.advanced" );
943 sub patron_adv_search {
944 my( $self, $client, $staff_login, $search_hash ) = @_;
946 #warn "patron adv with $staff_login and search " .
947 #Dumper($search_hash) . "\n";
949 my $session = OpenSRF::AppSession->create("open-ils.storage");
950 my $req = $session->request(
951 "open-ils.storage.actor.user.crazy_search", $search_hash);
953 my $ans = $req->gather(1);
955 my %hash = map { ($_ =>1) } @$ans;
956 $ans = [ keys %hash ];
958 #warn "Returning @$ans\n";
960 $session->disconnect();
967 sub _verify_password {
968 my($user_session, $password) = @_;
969 my $user_obj = $apputils->check_user_session($user_session);
971 #grab the user with password
972 $user_obj = $apputils->simple_scalar_request(
974 "open-ils.storage.direct.actor.user.retrieve",
977 if($user_obj->passwd eq $password) {
985 __PACKAGE__->register_method(
986 method => "update_password",
987 api_name => "open-ils.actor.user.password.update");
989 __PACKAGE__->register_method(
990 method => "update_password",
991 api_name => "open-ils.actor.user.username.update");
993 __PACKAGE__->register_method(
994 method => "update_password",
995 api_name => "open-ils.actor.user.email.update");
997 sub update_password {
998 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1002 #warn "Updating user with method " .$self->api_name . "\n";
1003 my $user_obj = $apputils->check_user_session($user_session);
1005 if($self->api_name =~ /password/o) {
1007 #make sure they know the current password
1008 if(!_verify_password($user_session, md5_hex($current_password))) {
1009 return OpenILS::EX->new("USER_WRONG_PASSWORD")->ex;
1012 $user_obj->passwd($new_value);
1014 elsif($self->api_name =~ /username/o) {
1015 my $users = search_username(undef, undef, $new_value);
1016 if( $users and $users->[0] ) {
1017 return OpenILS::Event->new('USERNAME_EXISTS');
1019 $user_obj->usrname($new_value);
1022 elsif($self->api_name =~ /email/o) {
1023 #warn "Updating email to $new_value\n";
1024 $user_obj->email($new_value);
1027 my $session = $apputils->start_db_session();
1029 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj);
1030 return $evt if $evt;
1032 $apputils->commit_db_session($session);
1034 if($user_obj) { return 1; }
1039 __PACKAGE__->register_method(
1040 method => "check_user_perms",
1041 api_name => "open-ils.actor.user.perm.check",
1042 notes => <<" NOTES");
1043 Takes a login session, user id, an org id, and an array of perm type strings. For each
1044 perm type, if the user does *not* have the given permission it is added
1045 to a list which is returned from the method. If all permissions
1046 are allowed, an empty list is returned
1047 if the logged in user does not match 'user_id', then the logged in user must
1048 have VIEW_PERMISSION priveleges.
1051 sub check_user_perms {
1052 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1054 my( $staff, $evt ) = $apputils->checkses($login_session);
1055 return $evt if $evt;
1057 if($staff->id ne $user_id) {
1058 if( my $evt = $apputils->check_perms(
1059 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1065 for my $perm (@$perm_types) {
1066 if($apputils->check_perms($user_id, $org_id, $perm)) {
1067 push @not_allowed, $perm;
1071 return \@not_allowed
1074 __PACKAGE__->register_method(
1075 method => "check_user_perms2",
1076 api_name => "open-ils.actor.user.perm.check.multi_org",
1078 Checks the permissions on a list of perms and orgs for a user
1079 @param authtoken The login session key
1080 @param user_id The id of the user to check
1081 @param orgs The array of org ids
1082 @param perms The array of permission names
1083 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1084 if the logged in user does not match 'user_id', then the logged in user must
1085 have VIEW_PERMISSION priveleges.
1088 sub check_user_perms2 {
1089 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1091 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1092 $authtoken, $user_id, 'VIEW_PERMISSION' );
1093 return $evt if $evt;
1096 for my $org (@$orgs) {
1097 for my $perm (@$perms) {
1098 if($apputils->check_perms($user_id, $org, $perm)) {
1099 push @not_allowed, [ $org, $perm ];
1104 return \@not_allowed
1108 __PACKAGE__->register_method(
1109 method => 'check_user_perms3',
1110 api_name => 'open-ils.actor.user.perm.highest_org',
1112 Returns the highest org unit id at which a user has a given permission
1113 If the requestor does not match the target user, the requestor must have
1114 'VIEW_PERMISSION' rights at the home org unit of the target user
1115 @param authtoken The login session key
1116 @param userid The id of the user in question
1117 @param perm The permission to check
1118 @return The org unit highest in the org tree within which the user has
1119 the requested permission
1122 sub check_user_perms3 {
1123 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1125 my( $staff, $target, $org, $evt );
1127 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1128 $authtoken, $userid, 'VIEW_PERMISSION' );
1129 return $evt if $evt;
1131 my $tree = $self->get_org_tree();
1132 return _find_highest_perm_org( $perm, $userid, $target->home_ou, $tree );
1136 sub _find_highest_perm_org {
1137 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1138 my $org = $apputils->find_org($org_tree, $start_org );
1142 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1144 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1150 __PACKAGE__->register_method(
1151 method => 'check_user_perms4',
1152 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1154 Returns the highest org unit id at which a user has a given permission
1155 If the requestor does not match the target user, the requestor must have
1156 'VIEW_PERMISSION' rights at the home org unit of the target user
1157 @param authtoken The login session key
1158 @param userid The id of the user in question
1159 @param perms An array of perm names to check
1160 @return An array of orgId's representing the org unit
1161 highest in the org tree within which the user has the requested permission
1162 The arrah of orgId's has matches the order of the perms array
1165 sub check_user_perms4 {
1166 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1168 my( $staff, $target, $org, $evt );
1170 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1171 $authtoken, $userid, 'VIEW_PERMISSION' );
1172 return $evt if $evt;
1175 return [] unless ref($perms);
1176 my $tree = $self->get_org_tree();
1178 for my $p (@$perms) {
1179 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1187 __PACKAGE__->register_method(
1188 method => "user_fines_summary",
1189 api_name => "open-ils.actor.user.fines.summary",
1190 notes => <<" NOTES");
1191 Returns a short summary of the users total open fines, excluding voided fines
1192 Params are login_session, user_id
1193 Returns a 'mous' object.
1196 sub user_fines_summary {
1197 my( $self, $client, $login_session, $user_id ) = @_;
1199 my $user_obj = $apputils->check_user_session($login_session);
1200 if($user_obj->id ne $user_id) {
1201 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1202 return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY");
1206 return $apputils->simple_scalar_request(
1208 "open-ils.storage.direct.money.open_user_summary.search.usr",
1216 __PACKAGE__->register_method(
1217 method => "user_transactions",
1218 api_name => "open-ils.actor.user.transactions",
1219 notes => <<" NOTES");
1220 Returns a list of open user transactions (mbts objects);
1221 Params are login_session, user_id
1222 Optional third parameter is the transactions type. defaults to all
1225 __PACKAGE__->register_method(
1226 method => "user_transactions",
1227 api_name => "open-ils.actor.user.transactions.have_charge",
1228 notes => <<" NOTES");
1229 Returns a list of all open user transactions (mbts objects) that have an initial charge
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_balance",
1237 notes => <<" NOTES");
1238 Returns a list of all open user transactions (mbts objects) that have a balance
1239 Params are login_session, user_id
1240 Optional third parameter is the transactions type. defaults to all
1243 __PACKAGE__->register_method(
1244 method => "user_transactions",
1245 api_name => "open-ils.actor.user.transactions.fleshed",
1246 notes => <<" NOTES");
1247 Returns an object/hash of transaction, circ, title where transaction = an open
1248 user transactions (mbts objects), circ is the attached circluation, and title
1249 is the title the circ points to
1250 Params are login_session, user_id
1251 Optional third parameter is the transactions type. defaults to all
1254 __PACKAGE__->register_method(
1255 method => "user_transactions",
1256 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1257 notes => <<" NOTES");
1258 Returns an object/hash of transaction, circ, title where transaction = an open
1259 user transactions that has an initial charge (mbts objects), circ is the
1260 attached circluation, and title is the title the circ points to
1261 Params are login_session, user_id
1262 Optional third parameter is the transactions type. defaults to all
1265 __PACKAGE__->register_method(
1266 method => "user_transactions",
1267 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1268 notes => <<" NOTES");
1269 Returns an object/hash of transaction, circ, title where transaction = an open
1270 user transaction that has a balance (mbts objects), circ is the attached
1271 circluation, and title is the title the circ points to
1272 Params are login_session, user_id
1273 Optional third parameter is the transaction type. defaults to all
1276 __PACKAGE__->register_method(
1277 method => "user_transactions",
1278 api_name => "open-ils.actor.user.transactions.count",
1279 notes => <<" NOTES");
1280 Returns an object/hash of transaction, circ, title where transaction = an open
1281 user transactions (mbts objects), circ is the attached circluation, and title
1282 is the title the circ points to
1283 Params are login_session, user_id
1284 Optional third parameter is the transactions type. defaults to all
1287 __PACKAGE__->register_method(
1288 method => "user_transactions",
1289 api_name => "open-ils.actor.user.transactions.have_charge.count",
1290 notes => <<" NOTES");
1291 Returns an object/hash of transaction, circ, title where transaction = an open
1292 user transactions that has an initial charge (mbts objects), circ is the
1293 attached circluation, and title is the title the circ points to
1294 Params are login_session, user_id
1295 Optional third parameter is the transactions type. defaults to all
1298 __PACKAGE__->register_method(
1299 method => "user_transactions",
1300 api_name => "open-ils.actor.user.transactions.have_balance.count",
1301 notes => <<" NOTES");
1302 Returns an object/hash of transaction, circ, title where transaction = an open
1303 user transaction that has a balance (mbts objects), circ is the attached
1304 circluation, and title is the title the circ points to
1305 Params are login_session, user_id
1306 Optional third parameter is the transaction type. defaults to all
1309 __PACKAGE__->register_method(
1310 method => "user_transactions",
1311 api_name => "open-ils.actor.user.transactions.have_balance.total",
1312 notes => <<" NOTES");
1313 Returns an object/hash of transaction, circ, title where transaction = an open
1314 user transaction that has a balance (mbts objects), circ is the attached
1315 circluation, and title is the title the circ points to
1316 Params are login_session, user_id
1317 Optional third parameter is the transaction type. defaults to all
1322 sub user_transactions {
1323 my( $self, $client, $login_session, $user_id, $type ) = @_;
1325 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1326 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1327 return $evt if $evt;
1329 my $api = $self->api_name();
1333 if(defined($type)) { @xact = (xact_type => $type);
1335 } else { @xact = (); }
1337 if($api =~ /have_charge/o) {
1339 $trans = $apputils->simple_scalar_request(
1341 "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1342 { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1344 } elsif($api =~ /have_balance/o) {
1346 $trans = $apputils->simple_scalar_request(
1348 "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1349 { usr => $user_id, balance_owed => { ">" => 0 }, @xact });
1353 $trans = $apputils->simple_scalar_request(
1355 "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1356 { usr => $user_id, @xact });
1359 if($api =~ /total/o) {
1361 for my $t (@$trans) {
1362 $total += $t->balance_owed;
1365 $logger->debug("Total balance owed by user $user_id: $total");
1369 if($api =~ /count/o) { return scalar @$trans; }
1370 if($api !~ /fleshed/o) { return $trans; }
1372 #warn "API: $api\n";
1375 for my $t (@$trans) {
1377 #warn $t->id . "\n";
1380 if( $t->xact_type ne 'circulation' ) {
1381 push @resp, {transaction => $t};
1385 my $circ = $apputils->simple_scalar_request(
1387 "open-ils.storage.direct.action.circulation.retrieve",
1392 my $title = $apputils->simple_scalar_request(
1394 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1395 $circ->target_copy );
1399 my $u = OpenILS::Utils::ModsParser->new();
1400 $u->start_mods_batch($title->marc());
1401 my $mods = $u->finish_mods_batch();
1403 push @resp, {transaction => $t, circ => $circ, record => $mods };
1411 __PACKAGE__->register_method(
1412 method => "user_transaction_retrieve",
1413 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1415 notes => <<" NOTES");
1416 Returns a fleshedtransaction record
1418 __PACKAGE__->register_method(
1419 method => "user_transaction_retrieve",
1420 api_name => "open-ils.actor.user.transaction.retrieve",
1422 notes => <<" NOTES");
1423 Returns a transaction record
1425 sub user_transaction_retrieve {
1426 my( $self, $client, $login_session, $bill_id ) = @_;
1428 my $trans = $apputils->simple_scalar_request(
1430 "open-ils.storage.direct.money.billable_transaction_summary.retrieve",
1434 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1435 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1436 return $evt if $evt;
1438 my $api = $self->api_name();
1439 if($api !~ /fleshed/o) { return $trans; }
1441 if( $trans->xact_type ne 'circulation' ) {
1442 $logger->debug("Returning non-circ transaction");
1443 return {transaction => $trans};
1446 my $circ = $apputils->simple_scalar_request(
1448 "open-ils.storage.direct.action.circulation.retrieve",
1451 return {transaction => $trans} unless $circ;
1452 $logger->debug("Found the circ transaction");
1454 my $title = $apputils->simple_scalar_request(
1456 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1457 $circ->target_copy );
1459 return {transaction => $trans, circ => $circ } unless $title;
1460 $logger->debug("Found the circ title");
1464 my $u = OpenILS::Utils::ModsParser->new();
1465 $u->start_mods_batch($title->marc());
1466 $mods = $u->finish_mods_batch();
1468 if ($title->id == -1) {
1469 my $copy = $apputils->simple_scalar_request(
1471 "open-ils.storage.direct.asset.copy.retrieve",
1472 $circ->target_copy );
1474 $mods = new Fieldmapper::metabib::virtual_record;
1476 $mods->title($copy->dummy_title);
1477 $mods->author($copy->dummy_author);
1481 $logger->debug("MODSized the circ title");
1483 return {transaction => $trans, circ => $circ, record => $mods };
1487 __PACKAGE__->register_method(
1488 method => "hold_request_count",
1489 api_name => "open-ils.actor.user.hold_requests.count",
1491 notes => <<" NOTES");
1492 Returns hold ready/total counts
1494 sub hold_request_count {
1495 my( $self, $client, $login_session, $userid ) = @_;
1497 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1498 $login_session, $userid, 'VIEW_HOLD' );
1499 return $evt if $evt;
1502 my $holds = $apputils->simple_scalar_request(
1504 "open-ils.storage.direct.action.hold_request.search_where.atomic",
1506 fulfillment_time => {"=" => undef } }
1510 for my $h (@$holds) {
1511 next unless $h->capture_time;
1513 my $copy = $apputils->simple_scalar_request(
1515 "open-ils.storage.direct.asset.copy.retrieve",
1519 if ($copy->status == 8) {
1524 return { total => scalar(@$holds), ready => scalar(@ready) };
1528 __PACKAGE__->register_method(
1529 method => "checkedout_count",
1530 api_name => "open-ils.actor.user.checked_out.count",
1532 notes => <<" NOTES");
1533 Returns a transaction record
1535 sub checkedout_count {
1536 my( $self, $client, $login_session, $userid ) = @_;
1538 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1539 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1540 return $evt if $evt;
1543 my $circs = $apputils->simple_scalar_request(
1545 "open-ils.storage.direct.action.circulation.search_where.atomic",
1547 checkin_time => {"=" => undef } }
1550 my $parser = DateTime::Format::ISO8601->new;
1553 for my $c (@$circs) {
1554 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1555 my $due = $due_dt->epoch;
1562 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1565 __PACKAGE__->register_method(
1566 method => "user_transaction_history",
1567 api_name => "open-ils.actor.user.transactions.history",
1569 notes => <<" NOTES");
1570 Returns a list of billable transaction ids for a user, optionally by type
1572 sub user_transaction_history {
1573 my( $self, $client, $login_session, $user_id, $type ) = @_;
1575 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1576 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1577 return $evt if $evt;
1579 my $api = $self->api_name();
1582 @xact = (xact_type => $type) if(defined($type));
1584 my $trans = $apputils->simple_scalar_request(
1586 "open-ils.storage.direct.money.billable_transaction_summary.search_where.atomic",
1587 { usr => $user_id, @xact }, { order_by => 'xact_start DESC' });
1589 return [ map { $_->id } @$trans ];
1593 __PACKAGE__->register_method(
1594 method => "user_perms",
1595 api_name => "open-ils.actor.permissions.user_perms.retrieve",
1597 notes => <<" NOTES");
1598 Returns a list of permissions
1601 my( $self, $client, $authtoken, $user ) = @_;
1603 my( $staff, $evt ) = $apputils->checkses($authtoken);
1604 return $evt if $evt;
1606 $user ||= $staff->id;
1608 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1612 return $apputils->simple_scalar_request(
1614 "open-ils.storage.permission.user_perms.atomic",
1618 __PACKAGE__->register_method(
1619 method => "retrieve_perms",
1620 api_name => "open-ils.actor.permissions.retrieve",
1621 notes => <<" NOTES");
1622 Returns a list of permissions
1624 sub retrieve_perms {
1625 my( $self, $client ) = @_;
1626 return $apputils->simple_scalar_request(
1628 "open-ils.storage.direct.permission.perm_list.retrieve.all.atomic");
1631 __PACKAGE__->register_method(
1632 method => "retrieve_groups",
1633 api_name => "open-ils.actor.groups.retrieve",
1634 notes => <<" NOTES");
1635 Returns a list of user groupss
1637 sub retrieve_groups {
1638 my( $self, $client ) = @_;
1639 return $apputils->simple_scalar_request(
1641 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1644 __PACKAGE__->register_method(
1645 method => "retrieve_groups_tree",
1646 api_name => "open-ils.actor.groups.tree.retrieve",
1647 notes => <<" NOTES");
1648 Returns a list of user groups
1650 sub retrieve_groups_tree {
1651 my( $self, $client ) = @_;
1652 my $groups = $apputils->simple_scalar_request(
1654 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1655 return $self->build_group_tree($groups);
1659 # turns an org list into an org tree
1660 sub build_group_tree {
1662 my( $self, $grplist) = @_;
1664 return $grplist unless (
1665 ref($grplist) and @$grplist > 1 );
1667 my @list = sort { $a->name cmp $b->name } @$grplist;
1670 for my $grp (@list) {
1672 if ($grp and !defined($grp->parent)) {
1676 my ($parent) = grep { $_->id == $grp->parent} @list;
1678 $parent->children([]) unless defined($parent->children);
1679 push( @{$parent->children}, $grp );
1687 __PACKAGE__->register_method(
1688 method => "add_user_to_groups",
1689 api_name => "open-ils.actor.user.set_groups",
1690 notes => <<" NOTES");
1691 Adds a user to one or more permission groups
1694 sub add_user_to_groups {
1695 my( $self, $client, $authtoken, $userid, $groups ) = @_;
1697 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1698 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
1699 return $evt if $evt;
1701 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1702 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
1703 return $evt if $evt;
1705 $apputils->simplereq(
1707 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
1709 for my $group (@$groups) {
1710 my $link = Fieldmapper::permission::usr_grp_map->new;
1712 $link->usr($userid);
1714 my $id = $apputils->simplereq(
1716 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
1722 __PACKAGE__->register_method(
1723 method => "get_user_perm_groups",
1724 api_name => "open-ils.actor.user.get_groups",
1725 notes => <<" NOTES");
1726 Retrieve a user's permission groups.
1730 sub get_user_perm_groups {
1731 my( $self, $client, $authtoken, $userid ) = @_;
1733 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1734 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
1735 return $evt if $evt;
1737 return $apputils->simplereq(
1739 'open-ils.storage.direct.permission.usr_grp_map.search.usr.atomic', $userid );
1744 __PACKAGE__->register_method (
1745 method => 'register_workstation',
1746 api_name => 'open-ils.actor.workstation.register',
1748 Registers a new workstion in the system
1749 @param authtoken The login session key
1750 @param name The name of the workstation id
1751 @param owner The org unit that owns this workstation
1752 @return The workstation id on success, WORKSTATION_NAME_EXISTS
1753 if the name is already in use.
1756 sub register_workstation {
1757 my( $self, $connection, $authtoken, $name, $owner ) = @_;
1758 my( $requestor, $evt ) = $U->checkses($authtoken);
1759 return $evt if $evt;
1760 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
1761 return $evt if $evt;
1763 my $ws = $U->storagereq(
1764 'open-ils.storage.direct.actor.workstation.search.name', $name );
1765 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
1767 $ws = Fieldmapper::actor::workstation->new;
1768 $ws->owning_lib($owner);
1771 my $id = $U->storagereq(
1772 'open-ils.storage.direct.actor.workstation.create', $ws );
1773 return $U->DB_UPDATE_FAILED($ws) unless $id;