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 my $stat = $session->request(
468 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
469 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
474 sub _check_dup_ident {
475 my( $session, $patron ) = @_;
477 return undef unless $patron->ident_value;
480 ident_type => $patron->ident_type,
481 ident_value => $patron->ident_value,
484 $logger->debug("patron update searching for dup ident values: " .
485 $patron->ident_type . ':' . $patron->ident_value);
487 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
489 my $dups = $session->request(
490 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
493 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
500 sub _add_update_addresses {
504 my $new_patron = shift;
508 my $current_id; # id of the address before creation
510 for my $address (@{$patron->addresses()}) {
512 next unless ref $address;
513 $current_id = $address->id();
515 if( $patron->billing_address() and
516 $patron->billing_address() == $current_id ) {
517 $logger->info("setting billing addr to $current_id");
518 $new_patron->billing_address($address->id());
519 $new_patron->ischanged(1);
522 if( $patron->mailing_address() and
523 $patron->mailing_address() == $current_id ) {
524 $new_patron->mailing_address($address->id());
525 $logger->info("setting mailing addr to $current_id");
526 $new_patron->ischanged(1);
530 if($address->isnew()) {
532 $address->usr($new_patron->id());
534 ($address, $evt) = _add_address($session,$address);
535 return (undef, $evt) if $evt;
537 # we need to get the new id
538 if( $patron->billing_address() and
539 $patron->billing_address() == $current_id ) {
540 $new_patron->billing_address($address->id());
541 $logger->info("setting billing addr to $current_id");
542 $new_patron->ischanged(1);
545 if( $patron->mailing_address() and
546 $patron->mailing_address() == $current_id ) {
547 $new_patron->mailing_address($address->id());
548 $logger->info("setting mailing addr to $current_id");
549 $new_patron->ischanged(1);
552 } elsif($address->ischanged() ) {
554 ($address, $evt) = _update_address($session, $address);
555 return (undef, $evt) if $evt;
557 } elsif($address->isdeleted() ) {
559 if( $address->id() == $new_patron->mailing_address() ) {
560 $new_patron->clear_mailing_address();
561 ($new_patron, $evt) = _update_patron($session, $new_patron);
562 return (undef, $evt) if $evt;
565 if( $address->id() == $new_patron->billing_address() ) {
566 $new_patron->clear_billing_address();
567 ($new_patron, $evt) = _update_patron($session, $new_patron);
568 return (undef, $evt) if $evt;
571 $evt = _delete_address($session, $address);
572 return (undef, $evt) if $evt;
576 return ( $new_patron, undef );
580 # adds an address to the db and returns the address with new id
582 my($session, $address) = @_;
583 $address->clear_id();
585 $logger->info("Creating new address at street ".$address->street1);
587 # put the address into the database
588 my $id = $session->request(
589 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
590 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
593 return ($address, undef);
597 sub _update_address {
598 my( $session, $address ) = @_;
600 $logger->info("Updating address ".$address->id." in the DB");
602 my $stat = $session->request(
603 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
605 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
606 return ($address, undef);
611 sub _add_update_cards {
615 my $new_patron = shift;
619 my $virtual_id; #id of the card before creation
620 for my $card (@{$patron->cards()}) {
622 $card->usr($new_patron->id());
624 if(ref($card) and $card->isnew()) {
626 $virtual_id = $card->id();
627 ( $card, $evt ) = _add_card($session,$card);
628 return (undef, $evt) if $evt;
630 #if(ref($patron->card)) { $patron->card($patron->card->id); }
631 if($patron->card() == $virtual_id) {
632 $new_patron->card($card->id());
633 $new_patron->ischanged(1);
636 } elsif( ref($card) and $card->ischanged() ) {
637 $evt = _update_card($session, $card);
638 return (undef, $evt) if $evt;
642 return ( $new_patron, undef );
646 # adds an card to the db and returns the card with new id
648 my( $session, $card ) = @_;
651 $logger->info("Adding new patron card ".$card->barcode);
653 my $id = $session->request(
654 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
655 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
656 $logger->info("Successfully created patron card $id");
659 return ( $card, undef );
663 # returns event on error. returns undef otherwise
665 my( $session, $card ) = @_;
666 $logger->info("Updating patron card ".$card->id);
668 my $stat = $session->request(
669 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
670 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
677 # returns event on error. returns undef otherwise
678 sub _delete_address {
679 my( $session, $address ) = @_;
681 $logger->info("Deleting address ".$address->id." from DB");
683 my $stat = $session->request(
684 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
686 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
692 sub _add_survey_responses {
693 my ($session, $patron, $new_patron) = @_;
695 $logger->info( "Updating survey responses for patron ".$new_patron->id );
697 my $responses = $patron->survey_responses;
701 $_->usr($new_patron->id) for (@$responses);
703 my $evt = $U->simplereq( "open-ils.circ",
704 "open-ils.circ.survey.submit.user_id", $responses );
706 return (undef, $evt) if defined($U->event_code($evt));
710 return ( $new_patron, undef );
714 sub _create_stat_maps {
716 my($session, $user_session, $patron, $new_patron) = @_;
718 my $maps = $patron->stat_cat_entries();
720 for my $map (@$maps) {
722 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
724 if ($map->isdeleted()) {
725 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
727 } elsif ($map->isnew()) {
728 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
733 $map->target_usr($new_patron->id);
736 $logger->info("Updating stat entry with method $method and map $map");
738 my $stat = $session->request($method, $map)->gather(1);
739 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
743 return ($new_patron, undef);
746 sub _create_perm_maps {
748 my($session, $user_session, $patron, $new_patron) = @_;
750 my $maps = $patron->permissions;
752 for my $map (@$maps) {
754 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
755 if ($map->isdeleted()) {
756 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
757 } elsif ($map->isnew()) {
758 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
763 $map->usr($new_patron->id);
765 #warn( "Updating permissions with method $method and session $user_session and map $map" );
766 $logger->info( "Updating permissions with method $method and map $map" );
768 my $stat = $session->request($method, $map)->gather(1);
769 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
773 return ($new_patron, undef);
777 sub _create_standing_penalties {
779 my($session, $user_session, $patron, $new_patron) = @_;
781 my $maps = $patron->standing_penalties;
784 for my $map (@$maps) {
786 if ($map->isdeleted()) {
787 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
788 } elsif ($map->isnew()) {
789 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
795 $map->usr($new_patron->id);
797 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
799 my $stat = $session->request($method, $map)->gather(1);
800 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
803 return ($new_patron, undef);
808 __PACKAGE__->register_method(
809 method => "search_username",
810 api_name => "open-ils.actor.user.search.username",
813 sub search_username {
814 my($self, $client, $username) = @_;
815 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
817 "open-ils.cstore.direct.actor.user.search.atomic",
818 { usrname => $username }
826 __PACKAGE__->register_method(
827 method => "user_retrieve_by_barcode",
828 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
830 sub user_retrieve_by_barcode {
831 my($self, $client, $user_session, $barcode) = @_;
833 $logger->debug("Searching for user with barcode $barcode");
834 my ($user_obj, $evt) = $apputils->checkses($user_session);
837 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
839 "open-ils.cstore.direct.actor.card.search.atomic",
840 { barcode => $barcode }
843 if(!$card || !$card->[0]) {
844 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
848 my $user = flesh_user($card->usr());
850 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
853 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
860 __PACKAGE__->register_method(
861 method => "get_user_by_id",
862 api_name => "open-ils.actor.user.retrieve",);
865 my ($self, $client, $auth, $id) = @_;
866 my $e = new_editor(authtoken=>$auth);
867 return $e->event unless $e->checkauth;
868 my $user = $e->retrieve_actor_user($id)
870 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
876 __PACKAGE__->register_method(
877 method => "get_org_types",
878 api_name => "open-ils.actor.org_types.retrieve",);
882 my($self, $client) = @_;
883 return $org_types if $org_types;
884 return $org_types = new_editor()->retrieve_all_actor_org_unit_type();
889 __PACKAGE__->register_method(
890 method => "get_user_ident_types",
891 api_name => "open-ils.actor.user.ident_types.retrieve",
894 sub get_user_ident_types {
895 return $ident_types if $ident_types;
896 return $ident_types =
897 new_editor()->retrieve_all_config_identification_type();
903 __PACKAGE__->register_method(
904 method => "get_org_unit",
905 api_name => "open-ils.actor.org_unit.retrieve",
909 my( $self, $client, $user_session, $org_id ) = @_;
910 my $e = new_editor(authtoken => $user_session);
912 return $e->event unless $e->checkauth;
913 $org_id = $e->requestor->ws_ou;
915 my $o = $e->retrieve_actor_org_unit($org_id)
920 __PACKAGE__->register_method(
921 method => "search_org_unit",
922 api_name => "open-ils.actor.org_unit_list.search",
925 sub search_org_unit {
927 my( $self, $client, $field, $value ) = @_;
929 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
931 "open-ils.cstore.direct.actor.org_unit.search.atomic",
932 { $field => $value } );
940 __PACKAGE__->register_method(
941 method => "get_org_tree",
942 api_name => "open-ils.actor.org_tree.retrieve",
944 note => "Returns the entire org tree structure",
948 my( $self, $client) = @_;
950 $cache = OpenSRF::Utils::Cache->new("global", 0) unless $cache;
951 my $tree = $cache->get_cache('orgtree');
952 return $tree if $tree;
954 $tree = new_editor()->search_actor_org_unit(
956 {"parent_ou" => undef },
959 flesh_fields => { aou => ['children'] },
960 order_by => { aou => 'name'}
965 $cache->put_cache('orgtree', $tree);
970 # turns an org list into an org tree
973 my( $self, $orglist) = @_;
975 return $orglist unless (
976 ref($orglist) and @$orglist > 1 );
979 $a->ou_type <=> $b->ou_type ||
980 $a->name cmp $b->name } @$orglist;
982 for my $org (@list) {
984 next unless ($org and defined($org->parent_ou));
985 my ($parent) = grep { $_->id == $org->parent_ou } @list;
988 $parent->children([]) unless defined($parent->children);
989 push( @{$parent->children}, $org );
997 __PACKAGE__->register_method(
998 method => "get_org_descendants",
999 api_name => "open-ils.actor.org_tree.descendants.retrieve"
1002 # depth is optional. org_unit is the id
1003 sub get_org_descendants {
1004 my( $self, $client, $org_unit, $depth ) = @_;
1005 my $orglist = $apputils->simple_scalar_request(
1007 "open-ils.storage.actor.org_unit.descendants.atomic",
1008 $org_unit, $depth );
1009 return $self->build_org_tree($orglist);
1013 __PACKAGE__->register_method(
1014 method => "get_org_ancestors",
1015 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
1018 # depth is optional. org_unit is the id
1019 sub get_org_ancestors {
1020 my( $self, $client, $org_unit, $depth ) = @_;
1021 my $orglist = $apputils->simple_scalar_request(
1023 "open-ils.storage.actor.org_unit.ancestors.atomic",
1024 $org_unit, $depth );
1025 return $self->build_org_tree($orglist);
1029 __PACKAGE__->register_method(
1030 method => "get_standings",
1031 api_name => "open-ils.actor.standings.retrieve"
1036 return $user_standings if $user_standings;
1037 return $user_standings =
1038 $apputils->simple_scalar_request(
1040 "open-ils.cstore.direct.config.standing.search.atomic",
1041 { id => { "!=" => undef } }
1047 __PACKAGE__->register_method(
1048 method => "get_my_org_path",
1049 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1052 sub get_my_org_path {
1053 my( $self, $client, $user_session, $org_id ) = @_;
1054 my $user_obj = $apputils->check_user_session($user_session);
1055 if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
1057 return $apputils->simple_scalar_request(
1059 "open-ils.storage.actor.org_unit.full_path.atomic",
1064 __PACKAGE__->register_method(
1065 method => "patron_adv_search",
1066 api_name => "open-ils.actor.patron.search.advanced" );
1067 sub patron_adv_search {
1068 my( $self, $client, $auth, $search_hash, $search_limit, $search_sort, $include_inactive ) = @_;
1069 my $e = new_editor(authtoken=>$auth);
1070 return $e->event unless $e->checkauth;
1071 return $e->event unless $e->allowed('VIEW_USER');
1072 return $U->storagereq(
1073 "open-ils.storage.actor.user.crazy_search",
1074 $search_hash, $search_limit, $search_sort, $include_inactive);
1079 sub _verify_password {
1080 my($user_session, $password) = @_;
1081 my $user_obj = $apputils->check_user_session($user_session);
1083 #grab the user with password
1084 $user_obj = $apputils->simple_scalar_request(
1086 "open-ils.cstore.direct.actor.user.retrieve",
1089 if($user_obj->passwd eq $password) {
1097 __PACKAGE__->register_method(
1098 method => "update_password",
1099 api_name => "open-ils.actor.user.password.update");
1101 __PACKAGE__->register_method(
1102 method => "update_password",
1103 api_name => "open-ils.actor.user.username.update");
1105 __PACKAGE__->register_method(
1106 method => "update_password",
1107 api_name => "open-ils.actor.user.email.update");
1109 sub update_password {
1110 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1114 my $user_obj = $apputils->check_user_session($user_session);
1116 if($self->api_name =~ /password/o) {
1118 #make sure they know the current password
1119 if(!_verify_password($user_session, md5_hex($current_password))) {
1120 return OpenILS::Event->new('INCORRECT_PASSWORD');
1123 $logger->debug("update_password setting new password $new_value");
1124 $user_obj->passwd($new_value);
1126 } elsif($self->api_name =~ /username/o) {
1127 my $users = search_username(undef, undef, $new_value);
1128 if( $users and $users->[0] ) {
1129 return OpenILS::Event->new('USERNAME_EXISTS');
1131 $user_obj->usrname($new_value);
1133 } elsif($self->api_name =~ /email/o) {
1134 #warn "Updating email to $new_value\n";
1135 $user_obj->email($new_value);
1138 my $session = $apputils->start_db_session();
1140 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1141 return $evt if $evt;
1143 $apputils->commit_db_session($session);
1145 if($user_obj) { return 1; }
1150 __PACKAGE__->register_method(
1151 method => "check_user_perms",
1152 api_name => "open-ils.actor.user.perm.check",
1153 notes => <<" NOTES");
1154 Takes a login session, user id, an org id, and an array of perm type strings. For each
1155 perm type, if the user does *not* have the given permission it is added
1156 to a list which is returned from the method. If all permissions
1157 are allowed, an empty list is returned
1158 if the logged in user does not match 'user_id', then the logged in user must
1159 have VIEW_PERMISSION priveleges.
1162 sub check_user_perms {
1163 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1165 my( $staff, $evt ) = $apputils->checkses($login_session);
1166 return $evt if $evt;
1168 if($staff->id ne $user_id) {
1169 if( $evt = $apputils->check_perms(
1170 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1176 for my $perm (@$perm_types) {
1177 if($apputils->check_perms($user_id, $org_id, $perm)) {
1178 push @not_allowed, $perm;
1182 return \@not_allowed
1185 __PACKAGE__->register_method(
1186 method => "check_user_perms2",
1187 api_name => "open-ils.actor.user.perm.check.multi_org",
1189 Checks the permissions on a list of perms and orgs for a user
1190 @param authtoken The login session key
1191 @param user_id The id of the user to check
1192 @param orgs The array of org ids
1193 @param perms The array of permission names
1194 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1195 if the logged in user does not match 'user_id', then the logged in user must
1196 have VIEW_PERMISSION priveleges.
1199 sub check_user_perms2 {
1200 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1202 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1203 $authtoken, $user_id, 'VIEW_PERMISSION' );
1204 return $evt if $evt;
1207 for my $org (@$orgs) {
1208 for my $perm (@$perms) {
1209 if($apputils->check_perms($user_id, $org, $perm)) {
1210 push @not_allowed, [ $org, $perm ];
1215 return \@not_allowed
1219 __PACKAGE__->register_method(
1220 method => 'check_user_perms3',
1221 api_name => 'open-ils.actor.user.perm.highest_org',
1223 Returns the highest org unit id at which a user has a given permission
1224 If the requestor does not match the target user, the requestor must have
1225 'VIEW_PERMISSION' rights at the home org unit of the target user
1226 @param authtoken The login session key
1227 @param userid The id of the user in question
1228 @param perm The permission to check
1229 @return The org unit highest in the org tree within which the user has
1230 the requested permission
1233 sub check_user_perms3 {
1234 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1236 my( $staff, $target, $org, $evt );
1238 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1239 $authtoken, $userid, 'VIEW_PERMISSION' );
1240 return $evt if $evt;
1242 my $tree = $self->get_org_tree();
1243 return _find_highest_perm_org( $perm, $userid, $target->ws_ou, $tree );
1247 sub _find_highest_perm_org {
1248 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1249 my $org = $apputils->find_org($org_tree, $start_org );
1253 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1255 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1261 __PACKAGE__->register_method(
1262 method => 'check_user_perms4',
1263 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1265 Returns the highest org unit id at which a user has a given permission
1266 If the requestor does not match the target user, the requestor must have
1267 'VIEW_PERMISSION' rights at the home org unit of the target user
1268 @param authtoken The login session key
1269 @param userid The id of the user in question
1270 @param perms An array of perm names to check
1271 @return An array of orgId's representing the org unit
1272 highest in the org tree within which the user has the requested permission
1273 The arrah of orgId's has matches the order of the perms array
1276 sub check_user_perms4 {
1277 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1279 my( $staff, $target, $org, $evt );
1281 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1282 $authtoken, $userid, 'VIEW_PERMISSION' );
1283 return $evt if $evt;
1286 return [] unless ref($perms);
1287 my $tree = $self->get_org_tree();
1289 for my $p (@$perms) {
1290 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1298 __PACKAGE__->register_method(
1299 method => "user_fines_summary",
1300 api_name => "open-ils.actor.user.fines.summary",
1301 notes => <<" NOTES");
1302 Returns a short summary of the users total open fines, excluding voided fines
1303 Params are login_session, user_id
1304 Returns a 'mous' object.
1307 sub user_fines_summary {
1308 my( $self, $client, $login_session, $user_id ) = @_;
1310 my $user_obj = $apputils->check_user_session($login_session);
1311 if($user_obj->id ne $user_id) {
1312 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1313 return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY");
1317 return $apputils->simple_scalar_request(
1319 "open-ils.cstore.direct.money.open_user_summary.search",
1320 { usr => $user_id } );
1327 __PACKAGE__->register_method(
1328 method => "user_transactions",
1329 api_name => "open-ils.actor.user.transactions",
1330 notes => <<" NOTES");
1331 Returns a list of open user transactions (mbts objects);
1332 Params are login_session, user_id
1333 Optional third parameter is the transactions type. defaults to all
1336 __PACKAGE__->register_method(
1337 method => "user_transactions",
1338 api_name => "open-ils.actor.user.transactions.have_charge",
1339 notes => <<" NOTES");
1340 Returns a list of all open user transactions (mbts objects) that have an initial charge
1341 Params are login_session, user_id
1342 Optional third parameter is the transactions type. defaults to all
1345 __PACKAGE__->register_method(
1346 method => "user_transactions",
1347 api_name => "open-ils.actor.user.transactions.have_balance",
1348 notes => <<" NOTES");
1349 Returns a list of all open user transactions (mbts objects) that have a balance
1350 Params are login_session, user_id
1351 Optional third parameter is the transactions type. defaults to all
1354 __PACKAGE__->register_method(
1355 method => "user_transactions",
1356 api_name => "open-ils.actor.user.transactions.fleshed",
1357 notes => <<" NOTES");
1358 Returns an object/hash of transaction, circ, title where transaction = an open
1359 user transactions (mbts objects), circ is the attached circluation, and title
1360 is the title the circ points to
1361 Params are login_session, user_id
1362 Optional third parameter is the transactions type. defaults to all
1365 __PACKAGE__->register_method(
1366 method => "user_transactions",
1367 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1368 notes => <<" NOTES");
1369 Returns an object/hash of transaction, circ, title where transaction = an open
1370 user transactions that has an initial charge (mbts objects), circ is the
1371 attached circluation, and title is the title the circ points to
1372 Params are login_session, user_id
1373 Optional third parameter is the transactions type. defaults to all
1376 __PACKAGE__->register_method(
1377 method => "user_transactions",
1378 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1379 notes => <<" NOTES");
1380 Returns an object/hash of transaction, circ, title where transaction = an open
1381 user transaction that has a balance (mbts objects), circ is the attached
1382 circluation, and title is the title the circ points to
1383 Params are login_session, user_id
1384 Optional third parameter is the transaction type. defaults to all
1387 __PACKAGE__->register_method(
1388 method => "user_transactions",
1389 api_name => "open-ils.actor.user.transactions.count",
1390 notes => <<" NOTES");
1391 Returns an object/hash of transaction, circ, title where transaction = an open
1392 user transactions (mbts objects), circ is the attached circluation, and title
1393 is the title the circ points to
1394 Params are login_session, user_id
1395 Optional third parameter is the transactions type. defaults to all
1398 __PACKAGE__->register_method(
1399 method => "user_transactions",
1400 api_name => "open-ils.actor.user.transactions.have_charge.count",
1401 notes => <<" NOTES");
1402 Returns an object/hash of transaction, circ, title where transaction = an open
1403 user transactions that has an initial charge (mbts objects), circ is the
1404 attached circluation, and title is the title the circ points to
1405 Params are login_session, user_id
1406 Optional third parameter is the transactions type. defaults to all
1409 __PACKAGE__->register_method(
1410 method => "user_transactions",
1411 api_name => "open-ils.actor.user.transactions.have_balance.count",
1412 notes => <<" NOTES");
1413 Returns an object/hash of transaction, circ, title where transaction = an open
1414 user transaction that has a balance (mbts objects), circ is the attached
1415 circluation, and title is the title the circ points to
1416 Params are login_session, user_id
1417 Optional third parameter is the transaction type. defaults to all
1420 __PACKAGE__->register_method(
1421 method => "user_transactions",
1422 api_name => "open-ils.actor.user.transactions.have_balance.total",
1423 notes => <<" NOTES");
1424 Returns an object/hash of transaction, circ, title where transaction = an open
1425 user transaction that has a balance (mbts objects), circ is the attached
1426 circluation, and title is the title the circ points to
1427 Params are login_session, user_id
1428 Optional third parameter is the transaction type. defaults to all
1433 sub user_transactions {
1434 my( $self, $client, $login_session, $user_id, $type ) = @_;
1436 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1437 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1438 return $evt if $evt;
1440 my $api = $self->api_name();
1444 if(defined($type)) { @xact = (xact_type => $type);
1446 } else { @xact = (); }
1448 if($api =~ /have_charge/o) {
1450 $trans = $apputils->simple_scalar_request(
1452 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1453 { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1455 } elsif($api =~ /have_balance/o) {
1457 $trans = $apputils->simple_scalar_request(
1459 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1460 { usr => $user_id, balance_owed => { "<>" => 0 }, @xact });
1464 $trans = $apputils->simple_scalar_request(
1466 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1467 { usr => $user_id, @xact });
1470 if($api =~ /total/o) {
1472 for my $t (@$trans) {
1473 $total += $t->balance_owed;
1476 $logger->debug("Total balance owed by user $user_id: $total");
1480 if($api =~ /count/o) { return scalar @$trans; }
1481 if($api !~ /fleshed/o) { return $trans; }
1484 for my $t (@$trans) {
1486 if( $t->xact_type ne 'circulation' ) {
1487 push @resp, {transaction => $t};
1491 my $circ = $apputils->simple_scalar_request(
1493 "open-ils.cstore.direct.action.circulation.retrieve",
1498 my $title = $apputils->simple_scalar_request(
1500 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1501 $circ->target_copy );
1505 my $u = OpenILS::Utils::ModsParser->new();
1506 $u->start_mods_batch($title->marc());
1507 my $mods = $u->finish_mods_batch();
1508 $mods->doc_id($title->id) if $mods;
1510 push @resp, {transaction => $t, circ => $circ, record => $mods };
1518 __PACKAGE__->register_method(
1519 method => "user_transaction_retrieve",
1520 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1522 notes => <<" NOTES");
1523 Returns a fleshedtransaction record
1525 __PACKAGE__->register_method(
1526 method => "user_transaction_retrieve",
1527 api_name => "open-ils.actor.user.transaction.retrieve",
1529 notes => <<" NOTES");
1530 Returns a transaction record
1532 sub user_transaction_retrieve {
1533 my( $self, $client, $login_session, $bill_id ) = @_;
1535 my $trans = $apputils->simple_scalar_request(
1537 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1541 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1542 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1543 return $evt if $evt;
1545 my $api = $self->api_name();
1546 if($api !~ /fleshed/o) { return $trans; }
1548 if( $trans->xact_type ne 'circulation' ) {
1549 $logger->debug("Returning non-circ transaction");
1550 return {transaction => $trans};
1553 my $circ = $apputils->simple_scalar_request(
1555 "open-ils..direct.action.circulation.retrieve",
1558 return {transaction => $trans} unless $circ;
1559 $logger->debug("Found the circ transaction");
1561 my $title = $apputils->simple_scalar_request(
1563 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1564 $circ->target_copy );
1566 return {transaction => $trans, circ => $circ } unless $title;
1567 $logger->debug("Found the circ title");
1571 my $u = OpenILS::Utils::ModsParser->new();
1572 $u->start_mods_batch($title->marc());
1573 $mods = $u->finish_mods_batch();
1575 if ($title->id == OILS_PRECAT_RECORD) {
1576 my $copy = $apputils->simple_scalar_request(
1578 "open-ils.cstore.direct.asset.copy.retrieve",
1579 $circ->target_copy );
1581 $mods = new Fieldmapper::metabib::virtual_record;
1582 $mods->doc_id(OILS_PRECAT_RECORD);
1583 $mods->title($copy->dummy_title);
1584 $mods->author($copy->dummy_author);
1588 $logger->debug("MODSized the circ title");
1590 return {transaction => $trans, circ => $circ, record => $mods };
1594 __PACKAGE__->register_method(
1595 method => "hold_request_count",
1596 api_name => "open-ils.actor.user.hold_requests.count",
1598 notes => <<" NOTES");
1599 Returns hold ready/total counts
1601 sub hold_request_count {
1602 my( $self, $client, $login_session, $userid ) = @_;
1604 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1605 $login_session, $userid, 'VIEW_HOLD' );
1606 return $evt if $evt;
1609 my $holds = $apputils->simple_scalar_request(
1611 "open-ils.cstore.direct.action.hold_request.search.atomic",
1614 fulfillment_time => {"=" => undef },
1615 cancel_time => undef,
1620 for my $h (@$holds) {
1621 next unless $h->capture_time and $h->current_copy;
1623 my $copy = $apputils->simple_scalar_request(
1625 "open-ils.cstore.direct.asset.copy.retrieve",
1629 if ($copy and $copy->status == 8) {
1634 return { total => scalar(@$holds), ready => scalar(@ready) };
1638 __PACKAGE__->register_method(
1639 method => "checkedout_count",
1640 api_name => "open-ils.actor.user.checked_out.count__",
1642 notes => <<" NOTES");
1643 Returns a transaction record
1647 sub checkedout_count {
1648 my( $self, $client, $login_session, $userid ) = @_;
1650 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1651 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1652 return $evt if $evt;
1654 my $circs = $apputils->simple_scalar_request(
1656 "open-ils.cstore.direct.action.circulation.search.atomic",
1657 { usr => $userid, stop_fines => undef }
1658 #{ usr => $userid, checkin_time => {"=" => undef } }
1661 my $parser = DateTime::Format::ISO8601->new;
1664 for my $c (@$circs) {
1665 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1666 my $due = $due_dt->epoch;
1668 if ($due < DateTime->today->epoch) {
1673 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1677 __PACKAGE__->register_method(
1678 method => "checked_out",
1679 api_name => "open-ils.actor.user.checked_out",
1682 Returns a structure of circulations objects sorted by
1683 out, overdue, lost, claims_returned, long_overdue.
1684 A list of IDs are returned of each type.
1685 lost, long_overdue, and claims_returned circ will not
1686 be "finished" (there is an outstanding balance or some
1687 other pending action on the circ).
1689 The .count method also includes a 'total' field which
1690 sums all "open" circs
1694 __PACKAGE__->register_method(
1695 method => "checked_out",
1696 api_name => "open-ils.actor.user.checked_out.count",
1698 signature => q/@see open-ils.actor.user.checked_out/
1702 my( $self, $conn, $auth, $userid ) = @_;
1704 my $e = new_editor(authtoken=>$auth);
1705 return $e->event unless $e->checkauth;
1707 if( $userid ne $e->requestor->id ) {
1708 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1711 my $count = $self->api_name =~ /count/;
1712 return _checked_out( $count, $e, $userid );
1716 my( $iscount, $e, $userid ) = @_;
1718 my $circs = $e->search_action_circulation(
1719 { usr => $userid, stop_fines => undef });
1721 my $parser = DateTime::Format::ISO8601->new;
1723 # split the circs up into overdue and not-overdue circs
1725 for my $c (@$circs) {
1726 if( $c->due_date ) {
1727 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1728 my $due = $due_dt->epoch;
1729 if ($due < DateTime->today->epoch) {
1730 push @overdue, $c->id;
1739 # grab all of the lost, claims-returned, and longoverdue circs
1740 #my $open = $e->search_action_circulation(
1741 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1744 # these items have stop_fines, but no xact_finish, so money
1745 # is owed on them and they have not been checked in
1746 my $open = $e->search_action_circulation(
1749 stop_fines => { '!=' => undef },
1750 xact_finish => undef,
1751 checkin_time => undef,
1756 my( @lost, @cr, @lo );
1757 for my $c (@$open) {
1758 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1759 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1760 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1766 total => @$circs + @lost + @cr + @lo,
1767 out => scalar(@out),
1768 overdue => scalar(@overdue),
1769 lost => scalar(@lost),
1770 claims_returned => scalar(@cr),
1771 long_overdue => scalar(@lo)
1777 overdue => \@overdue,
1779 claims_returned => \@cr,
1780 long_overdue => \@lo
1786 __PACKAGE__->register_method(
1787 method => "checked_in_with_fines",
1788 api_name => "open-ils.actor.user.checked_in_with_fines",
1790 signature => q/@see open-ils.actor.user.checked_out/
1792 sub checked_in_with_fines {
1793 my( $self, $conn, $auth, $userid ) = @_;
1795 my $e = new_editor(authtoken=>$auth);
1796 return $e->event unless $e->checkauth;
1798 if( $userid ne $e->requestor->id ) {
1799 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1802 # money is owed on these items and they are checked in
1803 my $open = $e->search_action_circulation(
1806 xact_finish => undef,
1807 checkin_time => { "!=" => undef },
1812 my( @lost, @cr, @lo );
1813 for my $c (@$open) {
1814 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1815 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1816 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1821 claims_returned => \@cr,
1822 long_overdue => \@lo
1834 __PACKAGE__->register_method(
1835 method => "user_transaction_history",
1836 api_name => "open-ils.actor.user.transactions.history",
1838 notes => <<" NOTES");
1839 Returns a list of billable transaction ids for a user, optionally by type
1841 __PACKAGE__->register_method(
1842 method => "user_transaction_history",
1843 api_name => "open-ils.actor.user.transactions.history.have_charge",
1845 notes => <<" NOTES");
1846 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1848 __PACKAGE__->register_method(
1849 method => "user_transaction_history",
1850 api_name => "open-ils.actor.user.transactions.history.have_balance",
1852 notes => <<" NOTES");
1853 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1857 sub _user_transaction_history {
1858 my( $self, $client, $login_session, $user_id, $type ) = @_;
1860 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1861 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1862 return $evt if $evt;
1864 my $api = $self->api_name();
1869 @xact = (xact_type => $type) if(defined($type));
1870 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1871 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1873 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1875 my $trans = $apputils->simple_scalar_request(
1877 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1878 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1880 return [ map { $_->id } @$trans ];
1885 sub user_transaction_history {
1886 my( $self, $conn, $auth, $userid, $type ) = @_;
1887 my $e = new_editor(authtoken=>$auth);
1888 return $e->event unless $e->checkauth;
1889 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1891 my $api = $self->api_name;
1892 my @xact = (xact_type => $type) if(defined($type));
1893 my @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1894 my @charge = (last_billing_ts => { "!=" => undef }) if $api =~ /have_charge/;
1896 return $e->search_money_billable_transaction_summary(
1898 { usr => $userid, @xact, @charge, @balance },
1899 { order_by => 'xact_start DESC' }
1905 __PACKAGE__->register_method(
1906 method => "user_perms",
1907 api_name => "open-ils.actor.permissions.user_perms.retrieve",
1909 notes => <<" NOTES");
1910 Returns a list of permissions
1913 my( $self, $client, $authtoken, $user ) = @_;
1915 my( $staff, $evt ) = $apputils->checkses($authtoken);
1916 return $evt if $evt;
1918 $user ||= $staff->id;
1920 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1924 return $apputils->simple_scalar_request(
1926 "open-ils.storage.permission.user_perms.atomic",
1930 __PACKAGE__->register_method(
1931 method => "retrieve_perms",
1932 api_name => "open-ils.actor.permissions.retrieve",
1933 notes => <<" NOTES");
1934 Returns a list of permissions
1936 sub retrieve_perms {
1937 my( $self, $client ) = @_;
1938 return $apputils->simple_scalar_request(
1940 "open-ils.cstore.direct.permission.perm_list.search.atomic",
1941 { id => { '!=' => undef } }
1945 __PACKAGE__->register_method(
1946 method => "retrieve_groups",
1947 api_name => "open-ils.actor.groups.retrieve",
1948 notes => <<" NOTES");
1949 Returns a list of user groupss
1951 sub retrieve_groups {
1952 my( $self, $client ) = @_;
1953 return new_editor()->retrieve_all_permission_grp_tree();
1956 __PACKAGE__->register_method(
1957 method => "retrieve_org_address",
1958 api_name => "open-ils.actor.org_unit.address.retrieve",
1959 notes => <<' NOTES');
1960 Returns an org_unit address by ID
1961 @param An org_address ID
1963 sub retrieve_org_address {
1964 my( $self, $client, $id ) = @_;
1965 return $apputils->simple_scalar_request(
1967 "open-ils.cstore.direct.actor.org_address.retrieve",
1972 __PACKAGE__->register_method(
1973 method => "retrieve_groups_tree",
1974 api_name => "open-ils.actor.groups.tree.retrieve",
1975 notes => <<" NOTES");
1976 Returns a list of user groups
1978 sub retrieve_groups_tree {
1979 my( $self, $client ) = @_;
1980 return new_editor()->search_permission_grp_tree(
1985 flesh_fields => { pgt => ["children"] },
1986 order_by => { pgt => 'name'}
1993 # turns an org list into an org tree
1995 sub build_group_tree {
1997 my( $self, $grplist) = @_;
1999 return $grplist unless (
2000 ref($grplist) and @$grplist > 1 );
2002 my @list = sort { $a->name cmp $b->name } @$grplist;
2005 for my $grp (@list) {
2007 if ($grp and !defined($grp->parent)) {
2011 my ($parent) = grep { $_->id == $grp->parent} @list;
2013 $parent->children([]) unless defined($parent->children);
2014 push( @{$parent->children}, $grp );
2022 __PACKAGE__->register_method(
2023 method => "add_user_to_groups",
2024 api_name => "open-ils.actor.user.set_groups",
2025 notes => <<" NOTES");
2026 Adds a user to one or more permission groups
2029 sub add_user_to_groups {
2030 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2032 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2033 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2034 return $evt if $evt;
2036 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2037 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2038 return $evt if $evt;
2040 $apputils->simplereq(
2042 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2044 for my $group (@$groups) {
2045 my $link = Fieldmapper::permission::usr_grp_map->new;
2047 $link->usr($userid);
2049 my $id = $apputils->simplereq(
2051 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2057 __PACKAGE__->register_method(
2058 method => "get_user_perm_groups",
2059 api_name => "open-ils.actor.user.get_groups",
2060 notes => <<" NOTES");
2061 Retrieve a user's permission groups.
2065 sub get_user_perm_groups {
2066 my( $self, $client, $authtoken, $userid ) = @_;
2068 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2069 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2070 return $evt if $evt;
2072 return $apputils->simplereq(
2074 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2079 __PACKAGE__->register_method (
2080 method => 'register_workstation',
2081 api_name => 'open-ils.actor.workstation.register.override',
2082 signature => q/@see open-ils.actor.workstation.register/);
2084 __PACKAGE__->register_method (
2085 method => 'register_workstation',
2086 api_name => 'open-ils.actor.workstation.register',
2088 Registers a new workstion in the system
2089 @param authtoken The login session key
2090 @param name The name of the workstation id
2091 @param owner The org unit that owns this workstation
2092 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2093 if the name is already in use.
2096 sub _register_workstation {
2097 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2098 my( $requestor, $evt ) = $U->checkses($authtoken);
2099 return $evt if $evt;
2100 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2101 return $evt if $evt;
2103 my $ws = $U->cstorereq(
2104 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2105 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2107 $ws = Fieldmapper::actor::workstation->new;
2108 $ws->owning_lib($owner);
2111 my $id = $U->storagereq(
2112 'open-ils.storage.direct.actor.workstation.create', $ws );
2113 return $U->DB_UPDATE_FAILED($ws) unless $id;
2119 sub register_workstation {
2120 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2122 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2123 return $e->event unless $e->checkauth;
2124 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2125 my $existing = $e->search_actor_workstation({name => $name});
2128 if( $self->api_name =~ /override/o ) {
2129 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2130 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2132 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2136 my $ws = Fieldmapper::actor::workstation->new;
2137 $ws->owning_lib($owner);
2139 $e->create_actor_workstation($ws) or return $e->event;
2141 return $ws->id; # note: editor sets the id on the new object for us
2145 __PACKAGE__->register_method (
2146 method => 'fetch_patron_note',
2147 api_name => 'open-ils.actor.note.retrieve.all',
2149 Returns a list of notes for a given user
2150 Requestor must have VIEW_USER permission if pub==false and
2151 @param authtoken The login session key
2152 @param args Hash of params including
2153 patronid : the patron's id
2154 pub : true if retrieving only public notes
2158 sub fetch_patron_note {
2159 my( $self, $conn, $authtoken, $args ) = @_;
2160 my $patronid = $$args{patronid};
2162 my($reqr, $evt) = $U->checkses($authtoken);
2165 ($patron, $evt) = $U->fetch_user($patronid);
2166 return $evt if $evt;
2169 if( $patronid ne $reqr->id ) {
2170 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2171 return $evt if $evt;
2173 return $U->cstorereq(
2174 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2175 { usr => $patronid, pub => 't' } );
2178 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2179 return $evt if $evt;
2181 return $U->cstorereq(
2182 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2185 __PACKAGE__->register_method (
2186 method => 'create_user_note',
2187 api_name => 'open-ils.actor.note.create',
2189 Creates a new note for the given user
2190 @param authtoken The login session key
2191 @param note The note object
2194 sub create_user_note {
2195 my( $self, $conn, $authtoken, $note ) = @_;
2196 my( $reqr, $patron, $evt ) =
2197 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2198 return $evt if $evt;
2199 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2201 $note->creator($reqr->id);
2202 my $id = $U->storagereq(
2203 'open-ils.storage.direct.actor.usr_note.create', $note );
2204 return $U->DB_UPDATE_FAILED($note) unless $id;
2209 __PACKAGE__->register_method (
2210 method => 'delete_user_note',
2211 api_name => 'open-ils.actor.note.delete',
2213 Deletes a note for the given user
2214 @param authtoken The login session key
2215 @param noteid The note id
2218 sub delete_user_note {
2219 my( $self, $conn, $authtoken, $noteid ) = @_;
2221 my $note = $U->cstorereq(
2222 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2223 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2225 my( $reqr, $patron, $evt ) =
2226 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2227 return $evt if $evt;
2228 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2230 my $stat = $U->storagereq(
2231 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2232 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2237 __PACKAGE__->register_method (
2238 method => 'update_user_note',
2239 api_name => 'open-ils.actor.note.update',
2241 @param authtoken The login session key
2242 @param note The note
2246 sub update_user_note {
2247 my( $self, $conn, $auth, $note ) = @_;
2248 my $e = new_editor(authtoken=>$auth, xact=>1);
2249 return $e->event unless $e->checkauth;
2250 my $patron = $e->retrieve_actor_user($note->usr)
2251 or return $e->event;
2252 return $e->event unless
2253 $e->allowed('UPDATE_USER', $patron->home_ou);
2254 $e->update_actor_user_note($note)
2255 or return $e->event;
2263 __PACKAGE__->register_method (
2264 method => 'create_closed_date',
2265 api_name => 'open-ils.actor.org_unit.closed_date.create',
2267 Creates a new closing entry for the given org_unit
2268 @param authtoken The login session key
2269 @param note The closed_date object
2272 sub create_closed_date {
2273 my( $self, $conn, $authtoken, $cd ) = @_;
2275 my( $user, $evt ) = $U->checkses($authtoken);
2276 return $evt if $evt;
2278 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2279 return $evt if $evt;
2281 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2283 my $id = $U->storagereq(
2284 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2285 return $U->DB_UPDATE_FAILED($cd) unless $id;
2290 __PACKAGE__->register_method (
2291 method => 'delete_closed_date',
2292 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2294 Deletes a closing entry for the given org_unit
2295 @param authtoken The login session key
2296 @param noteid The close_date id
2299 sub delete_closed_date {
2300 my( $self, $conn, $authtoken, $cd ) = @_;
2302 my( $user, $evt ) = $U->checkses($authtoken);
2303 return $evt if $evt;
2306 ($cd_obj, $evt) = fetch_closed_date($cd);
2307 return $evt if $evt;
2309 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2310 return $evt if $evt;
2312 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2314 my $stat = $U->storagereq(
2315 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2316 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2321 __PACKAGE__->register_method(
2322 method => 'usrname_exists',
2323 api_name => 'open-ils.actor.username.exists',
2325 Returns 1 if the requested username exists, returns 0 otherwise
2329 sub usrname_exists {
2330 my( $self, $conn, $auth, $usrname ) = @_;
2331 my $e = new_editor(authtoken=>$auth);
2332 return $e->event unless $e->checkauth;
2333 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2334 return $$a[0] if $a and @$a;
2338 __PACKAGE__->register_method(
2339 method => 'barcode_exists',
2340 api_name => 'open-ils.actor.barcode.exists',
2342 Returns 1 if the requested barcode exists, returns 0 otherwise
2346 sub barcode_exists {
2347 my( $self, $conn, $auth, $barcode ) = @_;
2348 my $e = new_editor(authtoken=>$auth);
2349 return $e->event unless $e->checkauth;
2350 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2351 return $$a[0] if $a and @$a;
2356 __PACKAGE__->register_method(
2357 method => 'retrieve_net_levels',
2358 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2361 sub retrieve_net_levels {
2362 my( $self, $conn, $auth ) = @_;
2363 my $e = new_editor(authtoken=>$auth);
2364 return $e->event unless $e->checkauth;
2365 return $e->retrieve_all_config_net_access_level();
2369 __PACKAGE__->register_method(
2370 method => 'fetch_org_by_shortname',
2371 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2373 sub fetch_org_by_shortname {
2374 my( $self, $conn, $sname ) = @_;
2375 my $e = new_editor();
2376 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2377 return $e->event unless $org;
2382 __PACKAGE__->register_method(
2383 method => 'session_home_lib',
2384 api_name => 'open-ils.actor.session.home_lib',
2387 sub session_home_lib {
2388 my( $self, $conn, $auth ) = @_;
2389 my $e = new_editor(authtoken=>$auth);
2390 return undef unless $e->checkauth;
2391 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2392 return $org->shortname;
2397 __PACKAGE__->register_method(
2398 method => 'slim_tree',
2399 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2402 my $tree = new_editor()->search_actor_org_unit(
2404 {"parent_ou" => undef },
2407 flesh_fields => { aou => ['children'] },
2408 order_by => { aou => 'name'},
2409 select => { aou => ["id","shortname", "name"]},
2414 return trim_tree($tree);
2420 return undef unless $tree;
2422 code => $tree->shortname,
2423 name => $tree->name,
2425 if( $tree->children and @{$tree->children} ) {
2426 $htree->{children} = [];
2427 for my $c (@{$tree->children}) {
2428 push( @{$htree->{children}}, trim_tree($c) );
2437 __PACKAGE__->register_method(
2438 method => "user_retrieve_fleshed_by_id",
2439 api_name => "open-ils.actor.user.fleshed.retrieve",);
2441 sub user_retrieve_fleshed_by_id {
2442 my( $self, $client, $auth, $user_id, $fields ) = @_;
2443 my $e = new_editor(authtoken => $auth);
2444 return $e->event unless $e->checkauth;
2445 if( $e->requestor->id != $user_id ) {
2446 return $e->event unless $e->allowed('VIEW_USER');
2451 "standing_penalties",
2455 "stat_cat_entries" ];
2456 return new_flesh_user($user_id, $fields, $e);
2460 sub new_flesh_user {
2463 my $fields = shift || [];
2464 my $e = shift || new_editor(xact=>1);
2466 my $user = $e->retrieve_actor_user(
2471 "flesh_fields" => { "au" => $fields }
2474 ) or return $e->event;
2477 if( grep { $_ eq 'addresses' } @$fields ) {
2479 $user->addresses([]) unless @{$user->addresses};
2481 if( ref $user->billing_address ) {
2482 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2483 push( @{$user->addresses}, $user->billing_address );
2487 if( ref $user->mailing_address ) {
2488 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2489 push( @{$user->addresses}, $user->mailing_address );
2494 $user->clear_passwd();