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);
13 use OpenILS::Application::AppUtils;
15 use OpenILS::Utils::Fieldmapper;
16 use OpenILS::Utils::ModsParser;
17 use OpenSRF::Utils::Logger qw/$logger/;
18 use OpenSRF::Utils qw/:datetime/;
20 use OpenSRF::Utils::Cache;
24 use DateTime::Format::ISO8601;
25 use OpenILS::Const qw/:const/;
27 use OpenILS::Application::Actor::Container;
28 use OpenILS::Application::Actor::ClosedDates;
30 use OpenILS::Utils::CStoreEditor qw/:funcs/;
32 use OpenILS::Application::Actor::UserGroups;
34 OpenILS::Application::Actor::Container->initialize();
35 OpenILS::Application::Actor::UserGroups->initialize();
36 OpenILS::Application::Actor::ClosedDates->initialize();
39 my $apputils = "OpenILS::Application::AppUtils";
42 sub _d { warn "Patron:\n" . Dumper(shift()); }
47 my $set_user_settings;
50 __PACKAGE__->register_method(
51 method => "set_user_settings",
52 api_name => "open-ils.actor.patron.settings.update",
54 sub set_user_settings {
55 my( $self, $client, $user_session, $uid, $settings ) = @_;
57 $logger->debug("Setting user settings: $user_session, $uid, " . Dumper($settings));
59 my( $staff, $user, $evt ) =
60 $apputils->checkses_requestor( $user_session, $uid, 'UPDATE_USER' );
64 [{ usr => $user->id, name => $_}, {value => $$settings{$_}}] } keys %$settings;
66 $_->[1]->{value} = JSON->perl2JSON($_->[1]->{value}) for @params;
68 $logger->activity("User " . $staff->id . " updating user $uid settings with: " . Dumper(\@params));
70 my $ses = $U->start_db_session();
71 my $stat = $ses->request(
72 'open-ils.storage.direct.actor.user_setting.batch.merge', @params )->gather(1);
73 $U->commit_db_session($ses);
80 __PACKAGE__->register_method(
81 method => "set_ou_settings",
82 api_name => "open-ils.actor.org_unit.settings.update",
85 my( $self, $client, $user_session, $ouid, $settings ) = @_;
87 my( $staff, $evt ) = $apputils->checkses( $user_session );
89 $evt = $apputils->check_perms( $staff->id, $ouid, 'UPDATE_ORG_SETTING' );
93 for my $set (keys %$settings) {
95 my $json = JSON->perl2JSON($$settings{$set});
96 $logger->activity("updating org_unit.setting: $ouid : $set : $json");
99 { org_unit => $ouid, name => $set },
100 { value => $json } );
103 my $ses = $U->start_db_session();
104 my $stat = $ses->request(
105 'open-ils.storage.direct.actor.org_unit_setting.merge', @params )->gather(1);
106 $U->commit_db_session($ses);
112 my $fetch_user_settings;
113 my $fetch_ou_settings;
115 __PACKAGE__->register_method(
116 method => "user_settings",
117 api_name => "open-ils.actor.patron.settings.retrieve",
120 my( $self, $client, $user_session, $uid ) = @_;
122 my( $staff, $user, $evt ) =
123 $apputils->checkses_requestor( $user_session, $uid, 'VIEW_USER' );
126 $logger->debug("User " . $staff->id . " fetching user $uid\n");
127 my $s = $apputils->simplereq(
129 'open-ils.cstore.direct.actor.user_setting.search.atomic', { usr => $uid } );
131 return { map { ( $_->name => JSON->JSON2perl($_->value) ) } @$s };
136 __PACKAGE__->register_method(
137 method => "ou_settings",
138 api_name => "open-ils.actor.org_unit.settings.retrieve",
141 my( $self, $client, $ouid ) = @_;
143 $logger->info("Fetching org unit settings for org $ouid");
145 my $s = $apputils->simplereq(
147 'open-ils.cstore.direct.actor.org_unit_setting.search.atomic', {org_unit => $ouid});
149 return { map { ( $_->name => JSON->JSON2perl($_->value) ) } @$s };
152 __PACKAGE__->register_method (
153 method => "ou_setting_delete",
154 api_name => 'open-ils.actor.org_setting.delete',
156 Deletes a specific org unit setting for a specific location
157 @param authtoken The login session key
158 @param orgid The org unit whose setting we're changing
159 @param setting The name of the setting to delete
160 @return True value on success.
164 sub ou_setting_delete {
165 my( $self, $conn, $authtoken, $orgid, $setting ) = @_;
166 my( $reqr, $evt) = $U->checkses($authtoken);
168 $evt = $U->check_perms($reqr->id, $orgid, 'UPDATE_ORG_SETTING');
171 my $id = $U->cstorereq(
172 'open-ils.cstore.direct.actor.org_unit_setting.id_list',
173 { name => $setting, org_unit => $orgid } );
175 $logger->debug("Retrieved setting $id in org unit setting delete");
177 my $s = $U->cstorereq(
178 'open-ils.cstore.direct.actor.org_unit_setting.delete', $id );
180 $logger->activity("User ".$reqr->id." deleted org unit setting $id") if $s;
186 __PACKAGE__->register_method(
187 method => "update_patron",
188 api_name => "open-ils.actor.patron.update",);
191 my( $self, $client, $user_session, $patron ) = @_;
193 my $session = $apputils->start_db_session();
196 $logger->info("Creating new patron...") if $patron->isnew;
197 $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
199 my( $user_obj, $evt ) = $U->checkses($user_session);
202 # XXX does this user have permission to add/create users. Granularity?
203 # $new_patron is the patron in progress. $patron is the original patron
204 # passed in with the method. new_patron will change as the components
205 # of patron are added/updated.
209 # unflesh the real items on the patron
210 $patron->card( $patron->card->id ) if(ref($patron->card));
211 $patron->billing_address( $patron->billing_address->id )
212 if(ref($patron->billing_address));
213 $patron->mailing_address( $patron->mailing_address->id )
214 if(ref($patron->mailing_address));
216 # create/update the patron first so we can use his id
217 if($patron->isnew()) {
218 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
220 } else { $new_patron = $patron; }
222 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
225 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
228 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
231 # re-update the patron if anything has happened to him during this process
232 if($new_patron->ischanged()) {
233 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
237 #$session = OpenSRF::AppSession->create("open-ils.storage"); # why did i put this here?
239 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
242 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
245 ($new_patron, $evt) = _create_standing_penalties($session, $user_session, $patron, $new_patron, $user_obj);
248 $logger->activity("user ".$user_obj->id." updating/creating user ".$new_patron->id);
249 $apputils->commit_db_session($session);
251 #warn "Patron Update/Create complete\n";
252 return flesh_user($new_patron->id());
258 #__PACKAGE__->register_method(
259 # method => "user_retrieve_fleshed_by_id",
260 # api_name => "open-ils.actor.user.fleshed.retrieve",);
262 #sub user_retrieve_fleshed_by_id {
263 # my( $self, $client, $user_session, $user_id ) = @_;
265 # my( $requestor, $target, $evt ) = $apputils->
266 # checkses_requestor( $user_session, $user_id, 'VIEW_USER' );
267 # return $evt if $evt;
269 # return flesh_user($user_id);
273 # fleshes: card, cards, address, addresses, stat_cat_entries, standing_penalties
283 $session = OpenSRF::AppSession->create("open-ils.storage");
287 # grab the user with the given id
288 my $ureq = $session->request(
289 "open-ils.storage.direct.actor.user.retrieve", $id);
290 my $user = $ureq->gather(1);
292 if(!$user) { return undef; }
295 my $cards_req = $session->request(
296 "open-ils.storage.direct.actor.card.search.usr.atomic",
298 $user->cards( $cards_req->gather(1) );
300 for my $c(@{$user->cards}) {
301 if($c->id == $user->card || $c->id eq $user->card ) {
302 #warn "Setting my card to " . $c->id . "\n";
307 my $add_req = $session->request(
308 "open-ils.storage.direct.actor.user_address.search.usr.atomic",
310 $user->addresses( $add_req->gather(1) );
312 if( @{$user->addresses} ) {
313 if( ! grep { $_->id eq $user->billing_address } @{$user->addresses} ) {
314 my $ba = $session->request(
315 'open-ils.storage.direct.actor.user_address.retrieve',
316 $user->billing_address)->gather(1);
317 push( @{$user->addresses}, $ba );
320 if( ! grep { $_->id eq $user->mailing_address } @{$user->addresses} ) {
321 my $ba = $session->request(
322 'open-ils.storage.direct.actor.user_address.retrieve',
323 $user->mailing_address)->gather(1);
324 push( @{$user->addresses}, $ba );
329 for my $c(@{$user->addresses}) {
330 if($c->id eq $user->billing_address ) { $user->billing_address($c); }
331 if($c->id eq $user->mailing_address ) { $user->mailing_address($c); }
334 my $stat_req = $session->request(
335 "open-ils.storage.direct.actor.stat_cat_entry_user_map.search.target_usr.atomic",
337 $user->stat_cat_entries($stat_req->gather(1));
339 my $standing_penalties_req = $session->request(
340 "open-ils.storage.direct.actor.user_standing_penalty.search.usr.atomic",
342 $user->standing_penalties($standing_penalties_req->gather(1));
344 if($kill) { $session->disconnect(); }
345 $user->clear_passwd();
354 return new_flesh_user($id, [
357 "standing_penalties",
361 "stat_cat_entries" ] );
369 # clone and clear stuff that would break the database
373 my $new_patron = $patron->clone;
375 # Using the Fieldmapper clone method
376 #my $new_patron = Fieldmapper::actor::user->new();
378 #my $fmap = $Fieldmapper::fieldmap;
379 #no strict; # shallow clone, may be useful in the fieldmapper
381 # (keys %{$fmap->{"Fieldmapper::actor::user"}->{'fields'}}) {
382 # $new_patron->$field( $patron->$field() );
387 $new_patron->clear_billing_address();
388 $new_patron->clear_mailing_address();
389 $new_patron->clear_addresses();
390 $new_patron->clear_card();
391 $new_patron->clear_cards();
392 $new_patron->clear_id();
393 $new_patron->clear_isnew();
394 $new_patron->clear_ischanged();
395 $new_patron->clear_isdeleted();
396 $new_patron->clear_stat_cat_entries();
397 $new_patron->clear_permissions();
398 $new_patron->clear_standing_penalties();
408 my $user_obj = shift;
410 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
411 return (undef, $evt) if $evt;
413 my $ex = $session->request(
414 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
416 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
419 $evt = _check_dup_ident($session, $patron);
420 return (undef, $evt) if $evt;
422 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
424 my $id = $session->request(
425 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
426 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
428 $logger->info("Successfully created new user [$id] in DB");
430 return ( $session->request(
431 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
436 my( $session, $patron, $user_obj, $noperm) = @_;
438 $logger->info("Updating patron ".$patron->id." in DB");
443 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
444 return (undef, $evt) if $evt;
447 # We can' check for dup idents on update because some existing
448 # users may already have dup idents
449 #$evt = _check_dup_ident($session, $patron);
450 #return (undef, $evt) if $evt;
453 # update the password by itself to avoid the password protection magic
454 if( $patron->passwd ) {
455 my $s = $session->request(
456 'open-ils.storage.direct.actor.user.remote_update',
457 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
458 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
459 $patron->clear_passwd;
462 if(!$patron->ident_type) {
463 $patron->clear_ident_type;
464 $patron->clear_ident_value;
467 if(!$patron->ident_type2) {
468 $patron->clear_ident_type2;
469 $patron->clear_ident_value2;
472 my $stat = $session->request(
473 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
474 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
479 sub _check_dup_ident {
480 my( $session, $patron ) = @_;
482 return undef unless $patron->ident_value;
485 ident_type => $patron->ident_type,
486 ident_value => $patron->ident_value,
489 $logger->debug("patron update searching for dup ident values: " .
490 $patron->ident_type . ':' . $patron->ident_value);
492 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
494 my $dups = $session->request(
495 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
498 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
505 sub _add_update_addresses {
509 my $new_patron = shift;
513 my $current_id; # id of the address before creation
515 for my $address (@{$patron->addresses()}) {
517 next unless ref $address;
518 $current_id = $address->id();
520 if( $patron->billing_address() and
521 $patron->billing_address() == $current_id ) {
522 $logger->info("setting billing addr to $current_id");
523 $new_patron->billing_address($address->id());
524 $new_patron->ischanged(1);
527 if( $patron->mailing_address() and
528 $patron->mailing_address() == $current_id ) {
529 $new_patron->mailing_address($address->id());
530 $logger->info("setting mailing addr to $current_id");
531 $new_patron->ischanged(1);
535 if($address->isnew()) {
537 $address->usr($new_patron->id());
539 ($address, $evt) = _add_address($session,$address);
540 return (undef, $evt) if $evt;
542 # we need to get the new id
543 if( $patron->billing_address() and
544 $patron->billing_address() == $current_id ) {
545 $new_patron->billing_address($address->id());
546 $logger->info("setting billing addr to $current_id");
547 $new_patron->ischanged(1);
550 if( $patron->mailing_address() and
551 $patron->mailing_address() == $current_id ) {
552 $new_patron->mailing_address($address->id());
553 $logger->info("setting mailing addr to $current_id");
554 $new_patron->ischanged(1);
557 } elsif($address->ischanged() ) {
559 ($address, $evt) = _update_address($session, $address);
560 return (undef, $evt) if $evt;
562 } elsif($address->isdeleted() ) {
564 if( $address->id() == $new_patron->mailing_address() ) {
565 $new_patron->clear_mailing_address();
566 ($new_patron, $evt) = _update_patron($session, $new_patron);
567 return (undef, $evt) if $evt;
570 if( $address->id() == $new_patron->billing_address() ) {
571 $new_patron->clear_billing_address();
572 ($new_patron, $evt) = _update_patron($session, $new_patron);
573 return (undef, $evt) if $evt;
576 $evt = _delete_address($session, $address);
577 return (undef, $evt) if $evt;
581 return ( $new_patron, undef );
585 # adds an address to the db and returns the address with new id
587 my($session, $address) = @_;
588 $address->clear_id();
590 $logger->info("Creating new address at street ".$address->street1);
592 # put the address into the database
593 my $id = $session->request(
594 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
595 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
598 return ($address, undef);
602 sub _update_address {
603 my( $session, $address ) = @_;
605 $logger->info("Updating address ".$address->id." in the DB");
607 my $stat = $session->request(
608 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
610 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
611 return ($address, undef);
616 sub _add_update_cards {
620 my $new_patron = shift;
624 my $virtual_id; #id of the card before creation
625 for my $card (@{$patron->cards()}) {
627 $card->usr($new_patron->id());
629 if(ref($card) and $card->isnew()) {
631 $virtual_id = $card->id();
632 ( $card, $evt ) = _add_card($session,$card);
633 return (undef, $evt) if $evt;
635 #if(ref($patron->card)) { $patron->card($patron->card->id); }
636 if($patron->card() == $virtual_id) {
637 $new_patron->card($card->id());
638 $new_patron->ischanged(1);
641 } elsif( ref($card) and $card->ischanged() ) {
642 $evt = _update_card($session, $card);
643 return (undef, $evt) if $evt;
647 return ( $new_patron, undef );
651 # adds an card to the db and returns the card with new id
653 my( $session, $card ) = @_;
656 $logger->info("Adding new patron card ".$card->barcode);
658 my $id = $session->request(
659 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
660 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
661 $logger->info("Successfully created patron card $id");
664 return ( $card, undef );
668 # returns event on error. returns undef otherwise
670 my( $session, $card ) = @_;
671 $logger->info("Updating patron card ".$card->id);
673 my $stat = $session->request(
674 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
675 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
682 # returns event on error. returns undef otherwise
683 sub _delete_address {
684 my( $session, $address ) = @_;
686 $logger->info("Deleting address ".$address->id." from DB");
688 my $stat = $session->request(
689 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
691 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
697 sub _add_survey_responses {
698 my ($session, $patron, $new_patron) = @_;
700 $logger->info( "Updating survey responses for patron ".$new_patron->id );
702 my $responses = $patron->survey_responses;
706 $_->usr($new_patron->id) for (@$responses);
708 my $evt = $U->simplereq( "open-ils.circ",
709 "open-ils.circ.survey.submit.user_id", $responses );
711 return (undef, $evt) if defined($U->event_code($evt));
715 return ( $new_patron, undef );
719 sub _create_stat_maps {
721 my($session, $user_session, $patron, $new_patron) = @_;
723 my $maps = $patron->stat_cat_entries();
725 for my $map (@$maps) {
727 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
729 if ($map->isdeleted()) {
730 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
732 } elsif ($map->isnew()) {
733 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
738 $map->target_usr($new_patron->id);
741 $logger->info("Updating stat entry with method $method and map $map");
743 my $stat = $session->request($method, $map)->gather(1);
744 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
748 return ($new_patron, undef);
751 sub _create_perm_maps {
753 my($session, $user_session, $patron, $new_patron) = @_;
755 my $maps = $patron->permissions;
757 for my $map (@$maps) {
759 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
760 if ($map->isdeleted()) {
761 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
762 } elsif ($map->isnew()) {
763 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
768 $map->usr($new_patron->id);
770 #warn( "Updating permissions with method $method and session $user_session and map $map" );
771 $logger->info( "Updating permissions with method $method and map $map" );
773 my $stat = $session->request($method, $map)->gather(1);
774 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
778 return ($new_patron, undef);
782 sub _create_standing_penalties {
784 my($session, $user_session, $patron, $new_patron) = @_;
786 my $maps = $patron->standing_penalties;
789 for my $map (@$maps) {
791 if ($map->isdeleted()) {
792 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
793 } elsif ($map->isnew()) {
794 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
800 $map->usr($new_patron->id);
802 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
804 my $stat = $session->request($method, $map)->gather(1);
805 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
808 return ($new_patron, undef);
813 __PACKAGE__->register_method(
814 method => "search_username",
815 api_name => "open-ils.actor.user.search.username",
818 sub search_username {
819 my($self, $client, $username) = @_;
820 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
822 "open-ils.cstore.direct.actor.user.search.atomic",
823 { usrname => $username }
831 __PACKAGE__->register_method(
832 method => "user_retrieve_by_barcode",
833 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
835 sub user_retrieve_by_barcode {
836 my($self, $client, $user_session, $barcode) = @_;
838 $logger->debug("Searching for user with barcode $barcode");
839 my ($user_obj, $evt) = $apputils->checkses($user_session);
842 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
844 "open-ils.cstore.direct.actor.card.search.atomic",
845 { barcode => $barcode }
848 if(!$card || !$card->[0]) {
849 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
853 my $user = flesh_user($card->usr());
855 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
858 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
865 __PACKAGE__->register_method(
866 method => "get_user_by_id",
867 api_name => "open-ils.actor.user.retrieve",);
870 my ($self, $client, $auth, $id) = @_;
871 my $e = new_editor(authtoken=>$auth);
872 return $e->event unless $e->checkauth;
873 my $user = $e->retrieve_actor_user($id)
875 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
881 __PACKAGE__->register_method(
882 method => "get_org_types",
883 api_name => "open-ils.actor.org_types.retrieve",);
887 my($self, $client) = @_;
888 return $org_types if $org_types;
889 return $org_types = new_editor()->retrieve_all_actor_org_unit_type();
894 __PACKAGE__->register_method(
895 method => "get_user_ident_types",
896 api_name => "open-ils.actor.user.ident_types.retrieve",
899 sub get_user_ident_types {
900 return $ident_types if $ident_types;
901 return $ident_types =
902 new_editor()->retrieve_all_config_identification_type();
908 __PACKAGE__->register_method(
909 method => "get_org_unit",
910 api_name => "open-ils.actor.org_unit.retrieve",
914 my( $self, $client, $user_session, $org_id ) = @_;
915 my $e = new_editor(authtoken => $user_session);
917 return $e->event unless $e->checkauth;
918 $org_id = $e->requestor->ws_ou;
920 my $o = $e->retrieve_actor_org_unit($org_id)
925 __PACKAGE__->register_method(
926 method => "search_org_unit",
927 api_name => "open-ils.actor.org_unit_list.search",
930 sub search_org_unit {
932 my( $self, $client, $field, $value ) = @_;
934 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
936 "open-ils.cstore.direct.actor.org_unit.search.atomic",
937 { $field => $value } );
945 __PACKAGE__->register_method(
946 method => "get_org_tree",
947 api_name => "open-ils.actor.org_tree.retrieve",
949 note => "Returns the entire org tree structure",
953 my( $self, $client) = @_;
955 $cache = OpenSRF::Utils::Cache->new("global", 0) unless $cache;
956 my $tree = $cache->get_cache('orgtree');
957 return $tree if $tree;
959 $tree = new_editor()->search_actor_org_unit(
961 {"parent_ou" => undef },
964 flesh_fields => { aou => ['children'] },
965 order_by => { aou => 'name'}
970 $cache->put_cache('orgtree', $tree);
975 # turns an org list into an org tree
978 my( $self, $orglist) = @_;
980 return $orglist unless (
981 ref($orglist) and @$orglist > 1 );
984 $a->ou_type <=> $b->ou_type ||
985 $a->name cmp $b->name } @$orglist;
987 for my $org (@list) {
989 next unless ($org and defined($org->parent_ou));
990 my ($parent) = grep { $_->id == $org->parent_ou } @list;
993 $parent->children([]) unless defined($parent->children);
994 push( @{$parent->children}, $org );
1002 __PACKAGE__->register_method(
1003 method => "get_org_descendants",
1004 api_name => "open-ils.actor.org_tree.descendants.retrieve"
1007 # depth is optional. org_unit is the id
1008 sub get_org_descendants {
1009 my( $self, $client, $org_unit, $depth ) = @_;
1010 my $orglist = $apputils->simple_scalar_request(
1012 "open-ils.storage.actor.org_unit.descendants.atomic",
1013 $org_unit, $depth );
1014 return $self->build_org_tree($orglist);
1018 __PACKAGE__->register_method(
1019 method => "get_org_ancestors",
1020 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
1023 # depth is optional. org_unit is the id
1024 sub get_org_ancestors {
1025 my( $self, $client, $org_unit, $depth ) = @_;
1026 my $orglist = $apputils->simple_scalar_request(
1028 "open-ils.storage.actor.org_unit.ancestors.atomic",
1029 $org_unit, $depth );
1030 return $self->build_org_tree($orglist);
1034 __PACKAGE__->register_method(
1035 method => "get_standings",
1036 api_name => "open-ils.actor.standings.retrieve"
1041 return $user_standings if $user_standings;
1042 return $user_standings =
1043 $apputils->simple_scalar_request(
1045 "open-ils.cstore.direct.config.standing.search.atomic",
1046 { id => { "!=" => undef } }
1052 __PACKAGE__->register_method(
1053 method => "get_my_org_path",
1054 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1057 sub get_my_org_path {
1058 my( $self, $client, $user_session, $org_id ) = @_;
1059 my $user_obj = $apputils->check_user_session($user_session);
1060 if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
1062 return $apputils->simple_scalar_request(
1064 "open-ils.storage.actor.org_unit.full_path.atomic",
1069 __PACKAGE__->register_method(
1070 method => "patron_adv_search",
1071 api_name => "open-ils.actor.patron.search.advanced" );
1072 sub patron_adv_search {
1073 my( $self, $client, $auth, $search_hash, $search_limit, $search_sort, $include_inactive ) = @_;
1074 my $e = new_editor(authtoken=>$auth);
1075 return $e->event unless $e->checkauth;
1076 return $e->event unless $e->allowed('VIEW_USER');
1077 return $U->storagereq(
1078 "open-ils.storage.actor.user.crazy_search",
1079 $search_hash, $search_limit, $search_sort, $include_inactive);
1084 sub _verify_password {
1085 my($user_session, $password) = @_;
1086 my $user_obj = $apputils->check_user_session($user_session);
1088 #grab the user with password
1089 $user_obj = $apputils->simple_scalar_request(
1091 "open-ils.cstore.direct.actor.user.retrieve",
1094 if($user_obj->passwd eq $password) {
1102 __PACKAGE__->register_method(
1103 method => "update_password",
1104 api_name => "open-ils.actor.user.password.update");
1106 __PACKAGE__->register_method(
1107 method => "update_password",
1108 api_name => "open-ils.actor.user.username.update");
1110 __PACKAGE__->register_method(
1111 method => "update_password",
1112 api_name => "open-ils.actor.user.email.update");
1114 sub update_password {
1115 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1119 my $user_obj = $apputils->check_user_session($user_session);
1121 if($self->api_name =~ /password/o) {
1123 #make sure they know the current password
1124 if(!_verify_password($user_session, md5_hex($current_password))) {
1125 return OpenILS::Event->new('INCORRECT_PASSWORD');
1128 $logger->debug("update_password setting new password $new_value");
1129 $user_obj->passwd($new_value);
1131 } elsif($self->api_name =~ /username/o) {
1132 my $users = search_username(undef, undef, $new_value);
1133 if( $users and $users->[0] ) {
1134 return OpenILS::Event->new('USERNAME_EXISTS');
1136 $user_obj->usrname($new_value);
1138 } elsif($self->api_name =~ /email/o) {
1139 #warn "Updating email to $new_value\n";
1140 $user_obj->email($new_value);
1143 my $session = $apputils->start_db_session();
1145 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1146 return $evt if $evt;
1148 $apputils->commit_db_session($session);
1150 if($user_obj) { return 1; }
1155 __PACKAGE__->register_method(
1156 method => "check_user_perms",
1157 api_name => "open-ils.actor.user.perm.check",
1158 notes => <<" NOTES");
1159 Takes a login session, user id, an org id, and an array of perm type strings. For each
1160 perm type, if the user does *not* have the given permission it is added
1161 to a list which is returned from the method. If all permissions
1162 are allowed, an empty list is returned
1163 if the logged in user does not match 'user_id', then the logged in user must
1164 have VIEW_PERMISSION priveleges.
1167 sub check_user_perms {
1168 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1170 my( $staff, $evt ) = $apputils->checkses($login_session);
1171 return $evt if $evt;
1173 if($staff->id ne $user_id) {
1174 if( $evt = $apputils->check_perms(
1175 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1181 for my $perm (@$perm_types) {
1182 if($apputils->check_perms($user_id, $org_id, $perm)) {
1183 push @not_allowed, $perm;
1187 return \@not_allowed
1190 __PACKAGE__->register_method(
1191 method => "check_user_perms2",
1192 api_name => "open-ils.actor.user.perm.check.multi_org",
1194 Checks the permissions on a list of perms and orgs for a user
1195 @param authtoken The login session key
1196 @param user_id The id of the user to check
1197 @param orgs The array of org ids
1198 @param perms The array of permission names
1199 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1200 if the logged in user does not match 'user_id', then the logged in user must
1201 have VIEW_PERMISSION priveleges.
1204 sub check_user_perms2 {
1205 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1207 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1208 $authtoken, $user_id, 'VIEW_PERMISSION' );
1209 return $evt if $evt;
1212 for my $org (@$orgs) {
1213 for my $perm (@$perms) {
1214 if($apputils->check_perms($user_id, $org, $perm)) {
1215 push @not_allowed, [ $org, $perm ];
1220 return \@not_allowed
1224 __PACKAGE__->register_method(
1225 method => 'check_user_perms3',
1226 api_name => 'open-ils.actor.user.perm.highest_org',
1228 Returns the highest org unit id at which a user has a given permission
1229 If the requestor does not match the target user, the requestor must have
1230 'VIEW_PERMISSION' rights at the home org unit of the target user
1231 @param authtoken The login session key
1232 @param userid The id of the user in question
1233 @param perm The permission to check
1234 @return The org unit highest in the org tree within which the user has
1235 the requested permission
1238 sub check_user_perms3 {
1239 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1241 my( $staff, $target, $org, $evt );
1243 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1244 $authtoken, $userid, 'VIEW_PERMISSION' );
1245 return $evt if $evt;
1247 my $tree = $self->get_org_tree();
1248 return _find_highest_perm_org( $perm, $userid, $target->ws_ou, $tree );
1252 sub _find_highest_perm_org {
1253 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1254 my $org = $apputils->find_org($org_tree, $start_org );
1258 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1260 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1266 __PACKAGE__->register_method(
1267 method => 'check_user_perms4',
1268 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1270 Returns the highest org unit id at which a user has a given permission
1271 If the requestor does not match the target user, the requestor must have
1272 'VIEW_PERMISSION' rights at the home org unit of the target user
1273 @param authtoken The login session key
1274 @param userid The id of the user in question
1275 @param perms An array of perm names to check
1276 @return An array of orgId's representing the org unit
1277 highest in the org tree within which the user has the requested permission
1278 The arrah of orgId's has matches the order of the perms array
1281 sub check_user_perms4 {
1282 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1284 my( $staff, $target, $org, $evt );
1286 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1287 $authtoken, $userid, 'VIEW_PERMISSION' );
1288 return $evt if $evt;
1291 return [] unless ref($perms);
1292 my $tree = $self->get_org_tree();
1294 for my $p (@$perms) {
1295 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1303 __PACKAGE__->register_method(
1304 method => "user_fines_summary",
1305 api_name => "open-ils.actor.user.fines.summary",
1306 notes => <<" NOTES");
1307 Returns a short summary of the users total open fines, excluding voided fines
1308 Params are login_session, user_id
1309 Returns a 'mous' object.
1312 sub user_fines_summary {
1313 my( $self, $client, $login_session, $user_id ) = @_;
1315 my $user_obj = $apputils->check_user_session($login_session);
1316 if($user_obj->id ne $user_id) {
1317 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1318 return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY");
1322 return $apputils->simple_scalar_request(
1324 "open-ils.cstore.direct.money.open_user_summary.search",
1325 { usr => $user_id } );
1332 __PACKAGE__->register_method(
1333 method => "user_transactions",
1334 api_name => "open-ils.actor.user.transactions",
1335 notes => <<" NOTES");
1336 Returns a list of open user transactions (mbts objects);
1337 Params are login_session, user_id
1338 Optional third parameter is the transactions type. defaults to all
1341 __PACKAGE__->register_method(
1342 method => "user_transactions",
1343 api_name => "open-ils.actor.user.transactions.have_charge",
1344 notes => <<" NOTES");
1345 Returns a list of all open user transactions (mbts objects) that have an initial charge
1346 Params are login_session, user_id
1347 Optional third parameter is the transactions type. defaults to all
1350 __PACKAGE__->register_method(
1351 method => "user_transactions",
1352 api_name => "open-ils.actor.user.transactions.have_balance",
1353 notes => <<" NOTES");
1354 Returns a list of all open user transactions (mbts objects) that have a balance
1355 Params are login_session, user_id
1356 Optional third parameter is the transactions type. defaults to all
1359 __PACKAGE__->register_method(
1360 method => "user_transactions",
1361 api_name => "open-ils.actor.user.transactions.fleshed",
1362 notes => <<" NOTES");
1363 Returns an object/hash of transaction, circ, title where transaction = an open
1364 user transactions (mbts objects), circ is the attached circluation, and title
1365 is the title the circ points to
1366 Params are login_session, user_id
1367 Optional third parameter is the transactions type. defaults to all
1370 __PACKAGE__->register_method(
1371 method => "user_transactions",
1372 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1373 notes => <<" NOTES");
1374 Returns an object/hash of transaction, circ, title where transaction = an open
1375 user transactions that has an initial charge (mbts objects), circ is the
1376 attached circluation, and title is the title the circ points to
1377 Params are login_session, user_id
1378 Optional third parameter is the transactions type. defaults to all
1381 __PACKAGE__->register_method(
1382 method => "user_transactions",
1383 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1384 notes => <<" NOTES");
1385 Returns an object/hash of transaction, circ, title where transaction = an open
1386 user transaction that has a balance (mbts objects), circ is the attached
1387 circluation, and title is the title the circ points to
1388 Params are login_session, user_id
1389 Optional third parameter is the transaction type. defaults to all
1392 __PACKAGE__->register_method(
1393 method => "user_transactions",
1394 api_name => "open-ils.actor.user.transactions.count",
1395 notes => <<" NOTES");
1396 Returns an object/hash of transaction, circ, title where transaction = an open
1397 user transactions (mbts objects), circ is the attached circluation, and title
1398 is the title the circ points to
1399 Params are login_session, user_id
1400 Optional third parameter is the transactions type. defaults to all
1403 __PACKAGE__->register_method(
1404 method => "user_transactions",
1405 api_name => "open-ils.actor.user.transactions.have_charge.count",
1406 notes => <<" NOTES");
1407 Returns an object/hash of transaction, circ, title where transaction = an open
1408 user transactions that has an initial charge (mbts objects), circ is the
1409 attached circluation, and title is the title the circ points to
1410 Params are login_session, user_id
1411 Optional third parameter is the transactions type. defaults to all
1414 __PACKAGE__->register_method(
1415 method => "user_transactions",
1416 api_name => "open-ils.actor.user.transactions.have_balance.count",
1417 notes => <<" NOTES");
1418 Returns an object/hash of transaction, circ, title where transaction = an open
1419 user transaction that has a balance (mbts objects), circ is the attached
1420 circluation, and title is the title the circ points to
1421 Params are login_session, user_id
1422 Optional third parameter is the transaction type. defaults to all
1425 __PACKAGE__->register_method(
1426 method => "user_transactions",
1427 api_name => "open-ils.actor.user.transactions.have_balance.total",
1428 notes => <<" NOTES");
1429 Returns an object/hash of transaction, circ, title where transaction = an open
1430 user transaction that has a balance (mbts objects), circ is the attached
1431 circluation, and title is the title the circ points to
1432 Params are login_session, user_id
1433 Optional third parameter is the transaction type. defaults to all
1438 sub user_transactions {
1439 my( $self, $client, $login_session, $user_id, $type ) = @_;
1441 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1442 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1443 return $evt if $evt;
1445 my $api = $self->api_name();
1449 if(defined($type)) { @xact = (xact_type => $type);
1451 } else { @xact = (); }
1453 if($api =~ /have_charge/o) {
1455 $trans = $apputils->simple_scalar_request(
1457 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1458 { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1460 } elsif($api =~ /have_balance/o) {
1462 $trans = $apputils->simple_scalar_request(
1464 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1465 { usr => $user_id, balance_owed => { "<>" => 0 }, @xact });
1469 $trans = $apputils->simple_scalar_request(
1471 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1472 { usr => $user_id, @xact });
1475 if($api =~ /total/o) {
1477 for my $t (@$trans) {
1478 $total += $t->balance_owed;
1481 $logger->debug("Total balance owed by user $user_id: $total");
1485 if($api =~ /count/o) { return scalar @$trans; }
1486 if($api !~ /fleshed/o) { return $trans; }
1489 for my $t (@$trans) {
1491 if( $t->xact_type ne 'circulation' ) {
1492 push @resp, {transaction => $t};
1496 my $circ = $apputils->simple_scalar_request(
1498 "open-ils.cstore.direct.action.circulation.retrieve",
1503 my $title = $apputils->simple_scalar_request(
1505 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1506 $circ->target_copy );
1510 my $u = OpenILS::Utils::ModsParser->new();
1511 $u->start_mods_batch($title->marc());
1512 my $mods = $u->finish_mods_batch();
1513 $mods->doc_id($title->id) if $mods;
1515 push @resp, {transaction => $t, circ => $circ, record => $mods };
1523 __PACKAGE__->register_method(
1524 method => "user_transaction_retrieve",
1525 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1527 notes => <<" NOTES");
1528 Returns a fleshedtransaction record
1530 __PACKAGE__->register_method(
1531 method => "user_transaction_retrieve",
1532 api_name => "open-ils.actor.user.transaction.retrieve",
1534 notes => <<" NOTES");
1535 Returns a transaction record
1537 sub user_transaction_retrieve {
1538 my( $self, $client, $login_session, $bill_id ) = @_;
1540 my $trans = $apputils->simple_scalar_request(
1542 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1546 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1547 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1548 return $evt if $evt;
1550 my $api = $self->api_name();
1551 if($api !~ /fleshed/o) { return $trans; }
1553 if( $trans->xact_type ne 'circulation' ) {
1554 $logger->debug("Returning non-circ transaction");
1555 return {transaction => $trans};
1558 my $circ = $apputils->simple_scalar_request(
1560 "open-ils..direct.action.circulation.retrieve",
1563 return {transaction => $trans} unless $circ;
1564 $logger->debug("Found the circ transaction");
1566 my $title = $apputils->simple_scalar_request(
1568 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1569 $circ->target_copy );
1571 return {transaction => $trans, circ => $circ } unless $title;
1572 $logger->debug("Found the circ title");
1576 my $u = OpenILS::Utils::ModsParser->new();
1577 $u->start_mods_batch($title->marc());
1578 $mods = $u->finish_mods_batch();
1580 if ($title->id == OILS_PRECAT_RECORD) {
1581 my $copy = $apputils->simple_scalar_request(
1583 "open-ils.cstore.direct.asset.copy.retrieve",
1584 $circ->target_copy );
1586 $mods = new Fieldmapper::metabib::virtual_record;
1587 $mods->doc_id(OILS_PRECAT_RECORD);
1588 $mods->title($copy->dummy_title);
1589 $mods->author($copy->dummy_author);
1593 $logger->debug("MODSized the circ title");
1595 return {transaction => $trans, circ => $circ, record => $mods };
1599 __PACKAGE__->register_method(
1600 method => "hold_request_count",
1601 api_name => "open-ils.actor.user.hold_requests.count",
1603 notes => <<" NOTES");
1604 Returns hold ready/total counts
1606 sub hold_request_count {
1607 my( $self, $client, $login_session, $userid ) = @_;
1609 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1610 $login_session, $userid, 'VIEW_HOLD' );
1611 return $evt if $evt;
1614 my $holds = $apputils->simple_scalar_request(
1616 "open-ils.cstore.direct.action.hold_request.search.atomic",
1619 fulfillment_time => {"=" => undef },
1620 cancel_time => undef,
1625 for my $h (@$holds) {
1626 next unless $h->capture_time and $h->current_copy;
1628 my $copy = $apputils->simple_scalar_request(
1630 "open-ils.cstore.direct.asset.copy.retrieve",
1634 if ($copy and $copy->status == 8) {
1639 return { total => scalar(@$holds), ready => scalar(@ready) };
1643 __PACKAGE__->register_method(
1644 method => "checkedout_count",
1645 api_name => "open-ils.actor.user.checked_out.count__",
1647 notes => <<" NOTES");
1648 Returns a transaction record
1652 sub checkedout_count {
1653 my( $self, $client, $login_session, $userid ) = @_;
1655 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1656 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1657 return $evt if $evt;
1659 my $circs = $apputils->simple_scalar_request(
1661 "open-ils.cstore.direct.action.circulation.search.atomic",
1662 { usr => $userid, stop_fines => undef }
1663 #{ usr => $userid, checkin_time => {"=" => undef } }
1666 my $parser = DateTime::Format::ISO8601->new;
1669 for my $c (@$circs) {
1670 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1671 my $due = $due_dt->epoch;
1673 if ($due < DateTime->today->epoch) {
1678 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1682 __PACKAGE__->register_method(
1683 method => "checked_out",
1684 api_name => "open-ils.actor.user.checked_out",
1687 Returns a structure of circulations objects sorted by
1688 out, overdue, lost, claims_returned, long_overdue.
1689 A list of IDs are returned of each type.
1690 lost, long_overdue, and claims_returned circ will not
1691 be "finished" (there is an outstanding balance or some
1692 other pending action on the circ).
1694 The .count method also includes a 'total' field which
1695 sums all "open" circs
1699 __PACKAGE__->register_method(
1700 method => "checked_out",
1701 api_name => "open-ils.actor.user.checked_out.count",
1703 signature => q/@see open-ils.actor.user.checked_out/
1707 my( $self, $conn, $auth, $userid ) = @_;
1709 my $e = new_editor(authtoken=>$auth);
1710 return $e->event unless $e->checkauth;
1712 if( $userid ne $e->requestor->id ) {
1713 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1716 my $count = $self->api_name =~ /count/;
1717 return _checked_out( $count, $e, $userid );
1721 my( $iscount, $e, $userid ) = @_;
1723 my $circs = $e->search_action_circulation(
1724 { usr => $userid, stop_fines => undef });
1726 my $parser = DateTime::Format::ISO8601->new;
1728 # split the circs up into overdue and not-overdue circs
1730 for my $c (@$circs) {
1731 if( $c->due_date ) {
1732 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1733 my $due = $due_dt->epoch;
1734 if ($due < DateTime->today->epoch) {
1735 push @overdue, $c->id;
1744 # grab all of the lost, claims-returned, and longoverdue circs
1745 #my $open = $e->search_action_circulation(
1746 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1749 # these items have stop_fines, but no xact_finish, so money
1750 # is owed on them and they have not been checked in
1751 my $open = $e->search_action_circulation(
1754 stop_fines => { '!=' => undef },
1755 xact_finish => undef,
1756 checkin_time => undef,
1761 my( @lost, @cr, @lo );
1762 for my $c (@$open) {
1763 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1764 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1765 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1771 total => @$circs + @lost + @cr + @lo,
1772 out => scalar(@out),
1773 overdue => scalar(@overdue),
1774 lost => scalar(@lost),
1775 claims_returned => scalar(@cr),
1776 long_overdue => scalar(@lo)
1782 overdue => \@overdue,
1784 claims_returned => \@cr,
1785 long_overdue => \@lo
1791 __PACKAGE__->register_method(
1792 method => "checked_in_with_fines",
1793 api_name => "open-ils.actor.user.checked_in_with_fines",
1795 signature => q/@see open-ils.actor.user.checked_out/
1797 sub checked_in_with_fines {
1798 my( $self, $conn, $auth, $userid ) = @_;
1800 my $e = new_editor(authtoken=>$auth);
1801 return $e->event unless $e->checkauth;
1803 if( $userid ne $e->requestor->id ) {
1804 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1807 # money is owed on these items and they are checked in
1808 my $open = $e->search_action_circulation(
1811 xact_finish => undef,
1812 checkin_time => { "!=" => undef },
1817 my( @lost, @cr, @lo );
1818 for my $c (@$open) {
1819 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1820 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1821 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1826 claims_returned => \@cr,
1827 long_overdue => \@lo
1839 __PACKAGE__->register_method(
1840 method => "user_transaction_history",
1841 api_name => "open-ils.actor.user.transactions.history",
1843 notes => <<" NOTES");
1844 Returns a list of billable transaction ids for a user, optionally by type
1846 __PACKAGE__->register_method(
1847 method => "user_transaction_history",
1848 api_name => "open-ils.actor.user.transactions.history.have_charge",
1850 notes => <<" NOTES");
1851 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1853 __PACKAGE__->register_method(
1854 method => "user_transaction_history",
1855 api_name => "open-ils.actor.user.transactions.history.have_balance",
1857 notes => <<" NOTES");
1858 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1862 sub _user_transaction_history {
1863 my( $self, $client, $login_session, $user_id, $type ) = @_;
1865 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1866 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1867 return $evt if $evt;
1869 my $api = $self->api_name();
1874 @xact = (xact_type => $type) if(defined($type));
1875 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1876 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1878 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1880 my $trans = $apputils->simple_scalar_request(
1882 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1883 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1885 return [ map { $_->id } @$trans ];
1890 sub user_transaction_history {
1891 my( $self, $conn, $auth, $userid, $type ) = @_;
1892 my $e = new_editor(authtoken=>$auth);
1893 return $e->event unless $e->checkauth;
1894 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1896 my $api = $self->api_name;
1897 my @xact = (xact_type => $type) if(defined($type));
1898 my @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1899 my @charge = (last_billing_ts => { "!=" => undef }) if $api =~ /have_charge/;
1901 return $e->search_money_billable_transaction_summary(
1903 { usr => $userid, @xact, @charge, @balance },
1904 { order_by => 'xact_start DESC' }
1910 __PACKAGE__->register_method(
1911 method => "user_perms",
1912 api_name => "open-ils.actor.permissions.user_perms.retrieve",
1914 notes => <<" NOTES");
1915 Returns a list of permissions
1918 my( $self, $client, $authtoken, $user ) = @_;
1920 my( $staff, $evt ) = $apputils->checkses($authtoken);
1921 return $evt if $evt;
1923 $user ||= $staff->id;
1925 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1929 return $apputils->simple_scalar_request(
1931 "open-ils.storage.permission.user_perms.atomic",
1935 __PACKAGE__->register_method(
1936 method => "retrieve_perms",
1937 api_name => "open-ils.actor.permissions.retrieve",
1938 notes => <<" NOTES");
1939 Returns a list of permissions
1941 sub retrieve_perms {
1942 my( $self, $client ) = @_;
1943 return $apputils->simple_scalar_request(
1945 "open-ils.cstore.direct.permission.perm_list.search.atomic",
1946 { id => { '!=' => undef } }
1950 __PACKAGE__->register_method(
1951 method => "retrieve_groups",
1952 api_name => "open-ils.actor.groups.retrieve",
1953 notes => <<" NOTES");
1954 Returns a list of user groupss
1956 sub retrieve_groups {
1957 my( $self, $client ) = @_;
1958 return new_editor()->retrieve_all_permission_grp_tree();
1961 __PACKAGE__->register_method(
1962 method => "retrieve_org_address",
1963 api_name => "open-ils.actor.org_unit.address.retrieve",
1964 notes => <<' NOTES');
1965 Returns an org_unit address by ID
1966 @param An org_address ID
1968 sub retrieve_org_address {
1969 my( $self, $client, $id ) = @_;
1970 return $apputils->simple_scalar_request(
1972 "open-ils.cstore.direct.actor.org_address.retrieve",
1977 __PACKAGE__->register_method(
1978 method => "retrieve_groups_tree",
1979 api_name => "open-ils.actor.groups.tree.retrieve",
1980 notes => <<" NOTES");
1981 Returns a list of user groups
1983 sub retrieve_groups_tree {
1984 my( $self, $client ) = @_;
1985 return new_editor()->search_permission_grp_tree(
1990 flesh_fields => { pgt => ["children"] },
1991 order_by => { pgt => 'name'}
1998 # turns an org list into an org tree
2000 sub build_group_tree {
2002 my( $self, $grplist) = @_;
2004 return $grplist unless (
2005 ref($grplist) and @$grplist > 1 );
2007 my @list = sort { $a->name cmp $b->name } @$grplist;
2010 for my $grp (@list) {
2012 if ($grp and !defined($grp->parent)) {
2016 my ($parent) = grep { $_->id == $grp->parent} @list;
2018 $parent->children([]) unless defined($parent->children);
2019 push( @{$parent->children}, $grp );
2027 __PACKAGE__->register_method(
2028 method => "add_user_to_groups",
2029 api_name => "open-ils.actor.user.set_groups",
2030 notes => <<" NOTES");
2031 Adds a user to one or more permission groups
2034 sub add_user_to_groups {
2035 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2037 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2038 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2039 return $evt if $evt;
2041 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2042 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2043 return $evt if $evt;
2045 $apputils->simplereq(
2047 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2049 for my $group (@$groups) {
2050 my $link = Fieldmapper::permission::usr_grp_map->new;
2052 $link->usr($userid);
2054 my $id = $apputils->simplereq(
2056 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2062 __PACKAGE__->register_method(
2063 method => "get_user_perm_groups",
2064 api_name => "open-ils.actor.user.get_groups",
2065 notes => <<" NOTES");
2066 Retrieve a user's permission groups.
2070 sub get_user_perm_groups {
2071 my( $self, $client, $authtoken, $userid ) = @_;
2073 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2074 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2075 return $evt if $evt;
2077 return $apputils->simplereq(
2079 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2084 __PACKAGE__->register_method (
2085 method => 'register_workstation',
2086 api_name => 'open-ils.actor.workstation.register.override',
2087 signature => q/@see open-ils.actor.workstation.register/);
2089 __PACKAGE__->register_method (
2090 method => 'register_workstation',
2091 api_name => 'open-ils.actor.workstation.register',
2093 Registers a new workstion in the system
2094 @param authtoken The login session key
2095 @param name The name of the workstation id
2096 @param owner The org unit that owns this workstation
2097 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2098 if the name is already in use.
2101 sub _register_workstation {
2102 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2103 my( $requestor, $evt ) = $U->checkses($authtoken);
2104 return $evt if $evt;
2105 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2106 return $evt if $evt;
2108 my $ws = $U->cstorereq(
2109 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2110 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2112 $ws = Fieldmapper::actor::workstation->new;
2113 $ws->owning_lib($owner);
2116 my $id = $U->storagereq(
2117 'open-ils.storage.direct.actor.workstation.create', $ws );
2118 return $U->DB_UPDATE_FAILED($ws) unless $id;
2124 sub register_workstation {
2125 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2127 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2128 return $e->event unless $e->checkauth;
2129 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2130 my $existing = $e->search_actor_workstation({name => $name});
2133 if( $self->api_name =~ /override/o ) {
2134 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2135 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2137 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2141 my $ws = Fieldmapper::actor::workstation->new;
2142 $ws->owning_lib($owner);
2144 $e->create_actor_workstation($ws) or return $e->event;
2146 return $ws->id; # note: editor sets the id on the new object for us
2150 __PACKAGE__->register_method (
2151 method => 'fetch_patron_note',
2152 api_name => 'open-ils.actor.note.retrieve.all',
2154 Returns a list of notes for a given user
2155 Requestor must have VIEW_USER permission if pub==false and
2156 @param authtoken The login session key
2157 @param args Hash of params including
2158 patronid : the patron's id
2159 pub : true if retrieving only public notes
2163 sub fetch_patron_note {
2164 my( $self, $conn, $authtoken, $args ) = @_;
2165 my $patronid = $$args{patronid};
2167 my($reqr, $evt) = $U->checkses($authtoken);
2170 ($patron, $evt) = $U->fetch_user($patronid);
2171 return $evt if $evt;
2174 if( $patronid ne $reqr->id ) {
2175 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2176 return $evt if $evt;
2178 return $U->cstorereq(
2179 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2180 { usr => $patronid, pub => 't' } );
2183 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2184 return $evt if $evt;
2186 return $U->cstorereq(
2187 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2190 __PACKAGE__->register_method (
2191 method => 'create_user_note',
2192 api_name => 'open-ils.actor.note.create',
2194 Creates a new note for the given user
2195 @param authtoken The login session key
2196 @param note The note object
2199 sub create_user_note {
2200 my( $self, $conn, $authtoken, $note ) = @_;
2201 my( $reqr, $patron, $evt ) =
2202 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2203 return $evt if $evt;
2204 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2206 $note->creator($reqr->id);
2207 my $id = $U->storagereq(
2208 'open-ils.storage.direct.actor.usr_note.create', $note );
2209 return $U->DB_UPDATE_FAILED($note) unless $id;
2214 __PACKAGE__->register_method (
2215 method => 'delete_user_note',
2216 api_name => 'open-ils.actor.note.delete',
2218 Deletes a note for the given user
2219 @param authtoken The login session key
2220 @param noteid The note id
2223 sub delete_user_note {
2224 my( $self, $conn, $authtoken, $noteid ) = @_;
2226 my $note = $U->cstorereq(
2227 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2228 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2230 my( $reqr, $patron, $evt ) =
2231 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2232 return $evt if $evt;
2233 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2235 my $stat = $U->storagereq(
2236 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2237 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2242 __PACKAGE__->register_method (
2243 method => 'update_user_note',
2244 api_name => 'open-ils.actor.note.update',
2246 @param authtoken The login session key
2247 @param note The note
2251 sub update_user_note {
2252 my( $self, $conn, $auth, $note ) = @_;
2253 my $e = new_editor(authtoken=>$auth, xact=>1);
2254 return $e->event unless $e->checkauth;
2255 my $patron = $e->retrieve_actor_user($note->usr)
2256 or return $e->event;
2257 return $e->event unless
2258 $e->allowed('UPDATE_USER', $patron->home_ou);
2259 $e->update_actor_user_note($note)
2260 or return $e->event;
2268 __PACKAGE__->register_method (
2269 method => 'create_closed_date',
2270 api_name => 'open-ils.actor.org_unit.closed_date.create',
2272 Creates a new closing entry for the given org_unit
2273 @param authtoken The login session key
2274 @param note The closed_date object
2277 sub create_closed_date {
2278 my( $self, $conn, $authtoken, $cd ) = @_;
2280 my( $user, $evt ) = $U->checkses($authtoken);
2281 return $evt if $evt;
2283 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2284 return $evt if $evt;
2286 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2288 my $id = $U->storagereq(
2289 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2290 return $U->DB_UPDATE_FAILED($cd) unless $id;
2295 __PACKAGE__->register_method (
2296 method => 'delete_closed_date',
2297 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2299 Deletes a closing entry for the given org_unit
2300 @param authtoken The login session key
2301 @param noteid The close_date id
2304 sub delete_closed_date {
2305 my( $self, $conn, $authtoken, $cd ) = @_;
2307 my( $user, $evt ) = $U->checkses($authtoken);
2308 return $evt if $evt;
2311 ($cd_obj, $evt) = fetch_closed_date($cd);
2312 return $evt if $evt;
2314 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2315 return $evt if $evt;
2317 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2319 my $stat = $U->storagereq(
2320 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2321 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2326 __PACKAGE__->register_method(
2327 method => 'usrname_exists',
2328 api_name => 'open-ils.actor.username.exists',
2330 Returns 1 if the requested username exists, returns 0 otherwise
2334 sub usrname_exists {
2335 my( $self, $conn, $auth, $usrname ) = @_;
2336 my $e = new_editor(authtoken=>$auth);
2337 return $e->event unless $e->checkauth;
2338 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2339 return $$a[0] if $a and @$a;
2343 __PACKAGE__->register_method(
2344 method => 'barcode_exists',
2345 api_name => 'open-ils.actor.barcode.exists',
2347 Returns 1 if the requested barcode exists, returns 0 otherwise
2351 sub barcode_exists {
2352 my( $self, $conn, $auth, $barcode ) = @_;
2353 my $e = new_editor(authtoken=>$auth);
2354 return $e->event unless $e->checkauth;
2355 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2356 return $$a[0] if $a and @$a;
2361 __PACKAGE__->register_method(
2362 method => 'retrieve_net_levels',
2363 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2366 sub retrieve_net_levels {
2367 my( $self, $conn, $auth ) = @_;
2368 my $e = new_editor(authtoken=>$auth);
2369 return $e->event unless $e->checkauth;
2370 return $e->retrieve_all_config_net_access_level();
2374 __PACKAGE__->register_method(
2375 method => 'fetch_org_by_shortname',
2376 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2378 sub fetch_org_by_shortname {
2379 my( $self, $conn, $sname ) = @_;
2380 my $e = new_editor();
2381 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2382 return $e->event unless $org;
2387 __PACKAGE__->register_method(
2388 method => 'session_home_lib',
2389 api_name => 'open-ils.actor.session.home_lib',
2392 sub session_home_lib {
2393 my( $self, $conn, $auth ) = @_;
2394 my $e = new_editor(authtoken=>$auth);
2395 return undef unless $e->checkauth;
2396 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2397 return $org->shortname;
2402 __PACKAGE__->register_method(
2403 method => 'slim_tree',
2404 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2407 my $tree = new_editor()->search_actor_org_unit(
2409 {"parent_ou" => undef },
2412 flesh_fields => { aou => ['children'] },
2413 order_by => { aou => 'name'},
2414 select => { aou => ["id","shortname", "name"]},
2419 return trim_tree($tree);
2425 return undef unless $tree;
2427 code => $tree->shortname,
2428 name => $tree->name,
2430 if( $tree->children and @{$tree->children} ) {
2431 $htree->{children} = [];
2432 for my $c (@{$tree->children}) {
2433 push( @{$htree->{children}}, trim_tree($c) );
2442 __PACKAGE__->register_method(
2443 method => "user_retrieve_fleshed_by_id",
2444 api_name => "open-ils.actor.user.fleshed.retrieve",);
2446 sub user_retrieve_fleshed_by_id {
2447 my( $self, $client, $auth, $user_id, $fields ) = @_;
2448 my $e = new_editor(authtoken => $auth);
2449 return $e->event unless $e->checkauth;
2450 return $e->event unless $e->allowed('VIEW_USER');
2454 "standing_penalties",
2458 "stat_cat_entries" ];
2459 return new_flesh_user($user_id, $fields, $e);
2463 sub new_flesh_user {
2466 my $fields = shift || [];
2467 my $e = shift || new_editor(xact=>1);
2469 my $user = $e->retrieve_actor_user(
2474 "flesh_fields" => { "au" => $fields }
2477 ) or return $e->event;
2480 if( grep { $_ eq 'addresses' } @$fields ) {
2482 $user->addresses([]) unless @{$user->addresses};
2484 if( ref $user->billing_address ) {
2485 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2486 push( @{$user->addresses}, $user->billing_address );
2490 if( ref $user->mailing_address ) {
2491 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2492 push( @{$user->addresses}, $user->mailing_address );
2497 $user->clear_passwd();