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();
197 $logger->info("Creating new patron...") if $patron->isnew;
198 $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
200 my( $user_obj, $evt ) = $U->checkses($user_session);
203 $evt = check_group_perm($session, $user_obj, $patron);
207 # XXX does this user have permission to add/create users. Granularity?
208 # $new_patron is the patron in progress. $patron is the original patron
209 # passed in with the method. new_patron will change as the components
210 # of patron are added/updated.
214 # unflesh the real items on the patron
215 $patron->card( $patron->card->id ) if(ref($patron->card));
216 $patron->billing_address( $patron->billing_address->id )
217 if(ref($patron->billing_address));
218 $patron->mailing_address( $patron->mailing_address->id )
219 if(ref($patron->mailing_address));
221 # create/update the patron first so we can use his id
222 if($patron->isnew()) {
223 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
225 } else { $new_patron = $patron; }
227 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
230 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
233 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
236 # re-update the patron if anything has happened to him during this process
237 if($new_patron->ischanged()) {
238 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
242 #$session = OpenSRF::AppSession->create("open-ils.storage"); # why did i put this here?
244 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
247 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
250 ($new_patron, $evt) = _create_standing_penalties($session, $user_session, $patron, $new_patron, $user_obj);
253 $logger->activity("user ".$user_obj->id." updating/creating user ".$new_patron->id);
256 if(!$patron->isnew) {
257 $opatron = new_editor()->retrieve_actor_user($new_patron->id);
260 $apputils->commit_db_session($session);
261 my $fuser = flesh_user($new_patron->id());
264 # Log the new and old patron for investigation
265 $logger->info("$user_session updating patron object. orig patron object = ".
266 JSON->perl2JSON($opatron). " |||| new patron = ".JSON->perl2JSON($fuser));
276 return new_flesh_user($id, [
279 "standing_penalties",
283 "stat_cat_entries" ] );
291 # clone and clear stuff that would break the database
295 my $new_patron = $patron->clone;
297 $new_patron->clear_billing_address();
298 $new_patron->clear_mailing_address();
299 $new_patron->clear_addresses();
300 $new_patron->clear_card();
301 $new_patron->clear_cards();
302 $new_patron->clear_id();
303 $new_patron->clear_isnew();
304 $new_patron->clear_ischanged();
305 $new_patron->clear_isdeleted();
306 $new_patron->clear_stat_cat_entries();
307 $new_patron->clear_permissions();
308 $new_patron->clear_standing_penalties();
318 my $user_obj = shift;
320 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
321 return (undef, $evt) if $evt;
323 my $ex = $session->request(
324 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
326 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
329 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
331 my $id = $session->request(
332 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
333 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
335 $logger->info("Successfully created new user [$id] in DB");
337 return ( $session->request(
338 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
342 sub check_group_perm {
343 my( $session, $requestor, $patron ) = @_;
346 # first let's see if the requestor has
347 # priveleges to update this user in any way
348 if( ! $patron->isnew ) {
349 my $p = $session->request(
350 'open-ils.storage.direct.actor.user.retrieve', $patron->id )->gather(1);
351 $evt = group_perm_failed($session, $requestor, $p);
355 # They are allowed to edit this patron.. can they put the
356 # patron into the group requested?
357 $evt = group_perm_failed($session, $requestor, $patron);
363 sub group_perm_failed {
364 my( $session, $requestor, $patron ) = @_;
368 my $grpid = $patron->profile;
372 $logger->debug("user update looking for group perm for group $grpid");
373 $grp = $session->request(
374 'open-ils.storage.direct.permission.grp_tree.retrieve', $grpid )->gather(1);
375 return OpenILS::Event->new('PERMISSION_GRP_TREE_NOT_FOUND') unless $grp;
377 } while( !($perm = $grp->application_perm) and ($grpid = $grp->parent) );
379 $logger->info("user update checking perm $perm on user ".
380 $requestor->id." for update/create on user username=".$patron->usrname);
382 my $evt = $U->check_perms($requestor->id, $patron->home_ou, $perm);
390 my( $session, $patron, $user_obj, $noperm) = @_;
392 $logger->info("Updating patron ".$patron->id." in DB");
397 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
398 return (undef, $evt) if $evt;
401 # update the password by itself to avoid the password protection magic
402 if( $patron->passwd ) {
403 my $s = $session->request(
404 'open-ils.storage.direct.actor.user.remote_update',
405 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
406 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
407 $patron->clear_passwd;
410 if(!$patron->ident_type) {
411 $patron->clear_ident_type;
412 $patron->clear_ident_value;
415 my $stat = $session->request(
416 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
417 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
422 sub _check_dup_ident {
423 my( $session, $patron ) = @_;
425 return undef unless $patron->ident_value;
428 ident_type => $patron->ident_type,
429 ident_value => $patron->ident_value,
432 $logger->debug("patron update searching for dup ident values: " .
433 $patron->ident_type . ':' . $patron->ident_value);
435 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
437 my $dups = $session->request(
438 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
441 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
448 sub _add_update_addresses {
452 my $new_patron = shift;
456 my $current_id; # id of the address before creation
458 for my $address (@{$patron->addresses()}) {
460 next unless ref $address;
461 $current_id = $address->id();
463 if( $patron->billing_address() and
464 $patron->billing_address() == $current_id ) {
465 $logger->info("setting billing addr to $current_id");
466 $new_patron->billing_address($address->id());
467 $new_patron->ischanged(1);
470 if( $patron->mailing_address() and
471 $patron->mailing_address() == $current_id ) {
472 $new_patron->mailing_address($address->id());
473 $logger->info("setting mailing addr to $current_id");
474 $new_patron->ischanged(1);
478 if($address->isnew()) {
480 $address->usr($new_patron->id());
482 ($address, $evt) = _add_address($session,$address);
483 return (undef, $evt) if $evt;
485 # we need to get the new id
486 if( $patron->billing_address() and
487 $patron->billing_address() == $current_id ) {
488 $new_patron->billing_address($address->id());
489 $logger->info("setting billing addr to $current_id");
490 $new_patron->ischanged(1);
493 if( $patron->mailing_address() and
494 $patron->mailing_address() == $current_id ) {
495 $new_patron->mailing_address($address->id());
496 $logger->info("setting mailing addr to $current_id");
497 $new_patron->ischanged(1);
500 } elsif($address->ischanged() ) {
502 ($address, $evt) = _update_address($session, $address);
503 return (undef, $evt) if $evt;
505 } elsif($address->isdeleted() ) {
507 if( $address->id() == $new_patron->mailing_address() ) {
508 $new_patron->clear_mailing_address();
509 ($new_patron, $evt) = _update_patron($session, $new_patron);
510 return (undef, $evt) if $evt;
513 if( $address->id() == $new_patron->billing_address() ) {
514 $new_patron->clear_billing_address();
515 ($new_patron, $evt) = _update_patron($session, $new_patron);
516 return (undef, $evt) if $evt;
519 $evt = _delete_address($session, $address);
520 return (undef, $evt) if $evt;
524 return ( $new_patron, undef );
528 # adds an address to the db and returns the address with new id
530 my($session, $address) = @_;
531 $address->clear_id();
533 $logger->info("Creating new address at street ".$address->street1);
535 # put the address into the database
536 my $id = $session->request(
537 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
538 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
541 return ($address, undef);
545 sub _update_address {
546 my( $session, $address ) = @_;
548 $logger->info("Updating address ".$address->id." in the DB");
550 my $stat = $session->request(
551 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
553 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
554 return ($address, undef);
559 sub _add_update_cards {
563 my $new_patron = shift;
567 my $virtual_id; #id of the card before creation
568 for my $card (@{$patron->cards()}) {
570 $card->usr($new_patron->id());
572 if(ref($card) and $card->isnew()) {
574 $virtual_id = $card->id();
575 ( $card, $evt ) = _add_card($session,$card);
576 return (undef, $evt) if $evt;
578 #if(ref($patron->card)) { $patron->card($patron->card->id); }
579 if($patron->card() == $virtual_id) {
580 $new_patron->card($card->id());
581 $new_patron->ischanged(1);
584 } elsif( ref($card) and $card->ischanged() ) {
585 $evt = _update_card($session, $card);
586 return (undef, $evt) if $evt;
590 return ( $new_patron, undef );
594 # adds an card to the db and returns the card with new id
596 my( $session, $card ) = @_;
599 $logger->info("Adding new patron card ".$card->barcode);
601 my $id = $session->request(
602 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
603 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
604 $logger->info("Successfully created patron card $id");
607 return ( $card, undef );
611 # returns event on error. returns undef otherwise
613 my( $session, $card ) = @_;
614 $logger->info("Updating patron card ".$card->id);
616 my $stat = $session->request(
617 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
618 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
625 # returns event on error. returns undef otherwise
626 sub _delete_address {
627 my( $session, $address ) = @_;
629 $logger->info("Deleting address ".$address->id." from DB");
631 my $stat = $session->request(
632 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
634 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
640 sub _add_survey_responses {
641 my ($session, $patron, $new_patron) = @_;
643 $logger->info( "Updating survey responses for patron ".$new_patron->id );
645 my $responses = $patron->survey_responses;
649 $_->usr($new_patron->id) for (@$responses);
651 my $evt = $U->simplereq( "open-ils.circ",
652 "open-ils.circ.survey.submit.user_id", $responses );
654 return (undef, $evt) if defined($U->event_code($evt));
658 return ( $new_patron, undef );
662 sub _create_stat_maps {
664 my($session, $user_session, $patron, $new_patron) = @_;
666 my $maps = $patron->stat_cat_entries();
668 for my $map (@$maps) {
670 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
672 if ($map->isdeleted()) {
673 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
675 } elsif ($map->isnew()) {
676 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
681 $map->target_usr($new_patron->id);
684 $logger->info("Updating stat entry with method $method and map $map");
686 my $stat = $session->request($method, $map)->gather(1);
687 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
691 return ($new_patron, undef);
694 sub _create_perm_maps {
696 my($session, $user_session, $patron, $new_patron) = @_;
698 my $maps = $patron->permissions;
700 for my $map (@$maps) {
702 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
703 if ($map->isdeleted()) {
704 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
705 } elsif ($map->isnew()) {
706 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
711 $map->usr($new_patron->id);
713 #warn( "Updating permissions with method $method and session $user_session and map $map" );
714 $logger->info( "Updating permissions with method $method and map $map" );
716 my $stat = $session->request($method, $map)->gather(1);
717 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
721 return ($new_patron, undef);
725 sub _create_standing_penalties {
727 my($session, $user_session, $patron, $new_patron) = @_;
729 my $maps = $patron->standing_penalties;
732 for my $map (@$maps) {
734 if ($map->isdeleted()) {
735 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
736 } elsif ($map->isnew()) {
737 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
743 $map->usr($new_patron->id);
745 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
747 my $stat = $session->request($method, $map)->gather(1);
748 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
751 return ($new_patron, undef);
756 __PACKAGE__->register_method(
757 method => "search_username",
758 api_name => "open-ils.actor.user.search.username",
761 sub search_username {
762 my($self, $client, $username) = @_;
763 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
765 "open-ils.cstore.direct.actor.user.search.atomic",
766 { usrname => $username }
774 __PACKAGE__->register_method(
775 method => "user_retrieve_by_barcode",
776 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
778 sub user_retrieve_by_barcode {
779 my($self, $client, $user_session, $barcode) = @_;
781 $logger->debug("Searching for user with barcode $barcode");
782 my ($user_obj, $evt) = $apputils->checkses($user_session);
785 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
787 "open-ils.cstore.direct.actor.card.search.atomic",
788 { barcode => $barcode }
791 if(!$card || !$card->[0]) {
792 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
796 my $user = flesh_user($card->usr());
798 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
801 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
808 __PACKAGE__->register_method(
809 method => "get_user_by_id",
810 api_name => "open-ils.actor.user.retrieve",);
813 my ($self, $client, $auth, $id) = @_;
814 my $e = new_editor(authtoken=>$auth);
815 return $e->event unless $e->checkauth;
816 my $user = $e->retrieve_actor_user($id)
818 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
824 __PACKAGE__->register_method(
825 method => "get_org_types",
826 api_name => "open-ils.actor.org_types.retrieve",);
830 my($self, $client) = @_;
831 return $org_types if $org_types;
832 return $org_types = new_editor()->retrieve_all_actor_org_unit_type();
837 __PACKAGE__->register_method(
838 method => "get_user_ident_types",
839 api_name => "open-ils.actor.user.ident_types.retrieve",
842 sub get_user_ident_types {
843 return $ident_types if $ident_types;
844 return $ident_types =
845 new_editor()->retrieve_all_config_identification_type();
851 __PACKAGE__->register_method(
852 method => "get_org_unit",
853 api_name => "open-ils.actor.org_unit.retrieve",
857 my( $self, $client, $user_session, $org_id ) = @_;
858 my $e = new_editor(authtoken => $user_session);
860 return $e->event unless $e->checkauth;
861 $org_id = $e->requestor->ws_ou;
863 my $o = $e->retrieve_actor_org_unit($org_id)
868 __PACKAGE__->register_method(
869 method => "search_org_unit",
870 api_name => "open-ils.actor.org_unit_list.search",
873 sub search_org_unit {
875 my( $self, $client, $field, $value ) = @_;
877 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
879 "open-ils.cstore.direct.actor.org_unit.search.atomic",
880 { $field => $value } );
888 __PACKAGE__->register_method(
889 method => "get_org_tree",
890 api_name => "open-ils.actor.org_tree.retrieve",
892 note => "Returns the entire org tree structure",
896 my( $self, $client) = @_;
898 $cache = OpenSRF::Utils::Cache->new("global", 0) unless $cache;
899 my $tree = $cache->get_cache('orgtree');
900 return $tree if $tree;
902 $tree = new_editor()->search_actor_org_unit(
904 {"parent_ou" => undef },
907 flesh_fields => { aou => ['children'] },
908 order_by => { aou => 'name'}
913 $cache->put_cache('orgtree', $tree);
918 # turns an org list into an org tree
921 my( $self, $orglist) = @_;
923 return $orglist unless (
924 ref($orglist) and @$orglist > 1 );
927 $a->ou_type <=> $b->ou_type ||
928 $a->name cmp $b->name } @$orglist;
930 for my $org (@list) {
932 next unless ($org and defined($org->parent_ou));
933 my ($parent) = grep { $_->id == $org->parent_ou } @list;
936 $parent->children([]) unless defined($parent->children);
937 push( @{$parent->children}, $org );
945 __PACKAGE__->register_method(
946 method => "get_org_descendants",
947 api_name => "open-ils.actor.org_tree.descendants.retrieve"
950 # depth is optional. org_unit is the id
951 sub get_org_descendants {
952 my( $self, $client, $org_unit, $depth ) = @_;
953 my $orglist = $apputils->simple_scalar_request(
955 "open-ils.storage.actor.org_unit.descendants.atomic",
957 return $self->build_org_tree($orglist);
961 __PACKAGE__->register_method(
962 method => "get_org_ancestors",
963 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
966 # depth is optional. org_unit is the id
967 sub get_org_ancestors {
968 my( $self, $client, $org_unit, $depth ) = @_;
969 my $orglist = $apputils->simple_scalar_request(
971 "open-ils.storage.actor.org_unit.ancestors.atomic",
973 return $self->build_org_tree($orglist);
977 __PACKAGE__->register_method(
978 method => "get_standings",
979 api_name => "open-ils.actor.standings.retrieve"
984 return $user_standings if $user_standings;
985 return $user_standings =
986 $apputils->simple_scalar_request(
988 "open-ils.cstore.direct.config.standing.search.atomic",
989 { id => { "!=" => undef } }
995 __PACKAGE__->register_method(
996 method => "get_my_org_path",
997 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1000 sub get_my_org_path {
1001 my( $self, $client, $user_session, $org_id ) = @_;
1002 my $user_obj = $apputils->check_user_session($user_session);
1003 if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
1005 return $apputils->simple_scalar_request(
1007 "open-ils.storage.actor.org_unit.full_path.atomic",
1012 __PACKAGE__->register_method(
1013 method => "patron_adv_search",
1014 api_name => "open-ils.actor.patron.search.advanced" );
1015 sub patron_adv_search {
1016 my( $self, $client, $auth, $search_hash, $search_limit, $search_sort, $include_inactive ) = @_;
1017 my $e = new_editor(authtoken=>$auth);
1018 return $e->event unless $e->checkauth;
1019 return $e->event unless $e->allowed('VIEW_USER');
1020 return $U->storagereq(
1021 "open-ils.storage.actor.user.crazy_search",
1022 $search_hash, $search_limit, $search_sort, $include_inactive);
1027 sub _verify_password {
1028 my($user_session, $password) = @_;
1029 my $user_obj = $apputils->check_user_session($user_session);
1031 #grab the user with password
1032 $user_obj = $apputils->simple_scalar_request(
1034 "open-ils.cstore.direct.actor.user.retrieve",
1037 if($user_obj->passwd eq $password) {
1045 __PACKAGE__->register_method(
1046 method => "update_password",
1047 api_name => "open-ils.actor.user.password.update");
1049 __PACKAGE__->register_method(
1050 method => "update_password",
1051 api_name => "open-ils.actor.user.username.update");
1053 __PACKAGE__->register_method(
1054 method => "update_password",
1055 api_name => "open-ils.actor.user.email.update");
1057 sub update_password {
1058 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1062 my $user_obj = $apputils->check_user_session($user_session);
1064 if($self->api_name =~ /password/o) {
1066 #make sure they know the current password
1067 if(!_verify_password($user_session, md5_hex($current_password))) {
1068 return OpenILS::Event->new('INCORRECT_PASSWORD');
1071 $logger->debug("update_password setting new password $new_value");
1072 $user_obj->passwd($new_value);
1074 } elsif($self->api_name =~ /username/o) {
1075 my $users = search_username(undef, undef, $new_value);
1076 if( $users and $users->[0] ) {
1077 return OpenILS::Event->new('USERNAME_EXISTS');
1079 $user_obj->usrname($new_value);
1081 } elsif($self->api_name =~ /email/o) {
1082 #warn "Updating email to $new_value\n";
1083 $user_obj->email($new_value);
1086 my $session = $apputils->start_db_session();
1088 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1089 return $evt if $evt;
1091 $apputils->commit_db_session($session);
1093 if($user_obj) { return 1; }
1098 __PACKAGE__->register_method(
1099 method => "check_user_perms",
1100 api_name => "open-ils.actor.user.perm.check",
1101 notes => <<" NOTES");
1102 Takes a login session, user id, an org id, and an array of perm type strings. For each
1103 perm type, if the user does *not* have the given permission it is added
1104 to a list which is returned from the method. If all permissions
1105 are allowed, an empty list is returned
1106 if the logged in user does not match 'user_id', then the logged in user must
1107 have VIEW_PERMISSION priveleges.
1110 sub check_user_perms {
1111 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1113 my( $staff, $evt ) = $apputils->checkses($login_session);
1114 return $evt if $evt;
1116 if($staff->id ne $user_id) {
1117 if( $evt = $apputils->check_perms(
1118 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1124 for my $perm (@$perm_types) {
1125 if($apputils->check_perms($user_id, $org_id, $perm)) {
1126 push @not_allowed, $perm;
1130 return \@not_allowed
1133 __PACKAGE__->register_method(
1134 method => "check_user_perms2",
1135 api_name => "open-ils.actor.user.perm.check.multi_org",
1137 Checks the permissions on a list of perms and orgs for a user
1138 @param authtoken The login session key
1139 @param user_id The id of the user to check
1140 @param orgs The array of org ids
1141 @param perms The array of permission names
1142 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1143 if the logged in user does not match 'user_id', then the logged in user must
1144 have VIEW_PERMISSION priveleges.
1147 sub check_user_perms2 {
1148 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1150 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1151 $authtoken, $user_id, 'VIEW_PERMISSION' );
1152 return $evt if $evt;
1155 for my $org (@$orgs) {
1156 for my $perm (@$perms) {
1157 if($apputils->check_perms($user_id, $org, $perm)) {
1158 push @not_allowed, [ $org, $perm ];
1163 return \@not_allowed
1167 __PACKAGE__->register_method(
1168 method => 'check_user_perms3',
1169 api_name => 'open-ils.actor.user.perm.highest_org',
1171 Returns the highest org unit id at which a user has a given permission
1172 If the requestor does not match the target user, the requestor must have
1173 'VIEW_PERMISSION' rights at the home org unit of the target user
1174 @param authtoken The login session key
1175 @param userid The id of the user in question
1176 @param perm The permission to check
1177 @return The org unit highest in the org tree within which the user has
1178 the requested permission
1181 sub check_user_perms3 {
1182 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1184 my( $staff, $target, $org, $evt );
1186 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1187 $authtoken, $userid, 'VIEW_PERMISSION' );
1188 return $evt if $evt;
1190 my $tree = $self->get_org_tree();
1191 return _find_highest_perm_org( $perm, $userid, $target->ws_ou, $tree );
1195 sub _find_highest_perm_org {
1196 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1197 my $org = $apputils->find_org($org_tree, $start_org );
1201 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1203 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1209 __PACKAGE__->register_method(
1210 method => 'check_user_perms4',
1211 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1213 Returns the highest org unit id at which a user has a given permission
1214 If the requestor does not match the target user, the requestor must have
1215 'VIEW_PERMISSION' rights at the home org unit of the target user
1216 @param authtoken The login session key
1217 @param userid The id of the user in question
1218 @param perms An array of perm names to check
1219 @return An array of orgId's representing the org unit
1220 highest in the org tree within which the user has the requested permission
1221 The arrah of orgId's has matches the order of the perms array
1224 sub check_user_perms4 {
1225 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1227 my( $staff, $target, $org, $evt );
1229 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1230 $authtoken, $userid, 'VIEW_PERMISSION' );
1231 return $evt if $evt;
1234 return [] unless ref($perms);
1235 my $tree = $self->get_org_tree();
1237 for my $p (@$perms) {
1238 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1246 __PACKAGE__->register_method(
1247 method => "user_fines_summary",
1248 api_name => "open-ils.actor.user.fines.summary",
1249 notes => <<" NOTES");
1250 Returns a short summary of the users total open fines, excluding voided fines
1251 Params are login_session, user_id
1252 Returns a 'mous' object.
1255 sub user_fines_summary {
1256 my( $self, $client, $login_session, $user_id ) = @_;
1258 my $user_obj = $apputils->check_user_session($login_session);
1259 if($user_obj->id ne $user_id) {
1260 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1261 return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY");
1265 return $apputils->simple_scalar_request(
1267 "open-ils.cstore.direct.money.open_user_summary.search",
1268 { usr => $user_id } );
1275 __PACKAGE__->register_method(
1276 method => "user_transactions",
1277 api_name => "open-ils.actor.user.transactions",
1278 notes => <<" NOTES");
1279 Returns a list of open user transactions (mbts objects);
1280 Params are login_session, user_id
1281 Optional third parameter is the transactions type. defaults to all
1284 __PACKAGE__->register_method(
1285 method => "user_transactions",
1286 api_name => "open-ils.actor.user.transactions.have_charge",
1287 notes => <<" NOTES");
1288 Returns a list of all open user transactions (mbts objects) that have an initial charge
1289 Params are login_session, user_id
1290 Optional third parameter is the transactions type. defaults to all
1293 __PACKAGE__->register_method(
1294 method => "user_transactions",
1295 api_name => "open-ils.actor.user.transactions.have_balance",
1296 notes => <<" NOTES");
1297 Returns a list of all open user transactions (mbts objects) that have a balance
1298 Params are login_session, user_id
1299 Optional third parameter is the transactions type. defaults to all
1302 __PACKAGE__->register_method(
1303 method => "user_transactions",
1304 api_name => "open-ils.actor.user.transactions.fleshed",
1305 notes => <<" NOTES");
1306 Returns an object/hash of transaction, circ, title where transaction = an open
1307 user transactions (mbts objects), circ is the attached circluation, and title
1308 is the title the circ points to
1309 Params are login_session, user_id
1310 Optional third parameter is the transactions type. defaults to all
1313 __PACKAGE__->register_method(
1314 method => "user_transactions",
1315 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1316 notes => <<" NOTES");
1317 Returns an object/hash of transaction, circ, title where transaction = an open
1318 user transactions that has an initial charge (mbts objects), circ is the
1319 attached circluation, and title is the title the circ points to
1320 Params are login_session, user_id
1321 Optional third parameter is the transactions type. defaults to all
1324 __PACKAGE__->register_method(
1325 method => "user_transactions",
1326 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1327 notes => <<" NOTES");
1328 Returns an object/hash of transaction, circ, title where transaction = an open
1329 user transaction that has a balance (mbts objects), circ is the attached
1330 circluation, and title is the title the circ points to
1331 Params are login_session, user_id
1332 Optional third parameter is the transaction type. defaults to all
1335 __PACKAGE__->register_method(
1336 method => "user_transactions",
1337 api_name => "open-ils.actor.user.transactions.count",
1338 notes => <<" NOTES");
1339 Returns an object/hash of transaction, circ, title where transaction = an open
1340 user transactions (mbts objects), circ is the attached circluation, and title
1341 is the title the circ points to
1342 Params are login_session, user_id
1343 Optional third parameter is the transactions type. defaults to all
1346 __PACKAGE__->register_method(
1347 method => "user_transactions",
1348 api_name => "open-ils.actor.user.transactions.have_charge.count",
1349 notes => <<" NOTES");
1350 Returns an object/hash of transaction, circ, title where transaction = an open
1351 user transactions that has an initial charge (mbts objects), circ is the
1352 attached circluation, and title is the title the circ points to
1353 Params are login_session, user_id
1354 Optional third parameter is the transactions type. defaults to all
1357 __PACKAGE__->register_method(
1358 method => "user_transactions",
1359 api_name => "open-ils.actor.user.transactions.have_balance.count",
1360 notes => <<" NOTES");
1361 Returns an object/hash of transaction, circ, title where transaction = an open
1362 user transaction that has a balance (mbts objects), circ is the attached
1363 circluation, and title is the title the circ points to
1364 Params are login_session, user_id
1365 Optional third parameter is the transaction type. defaults to all
1368 __PACKAGE__->register_method(
1369 method => "user_transactions",
1370 api_name => "open-ils.actor.user.transactions.have_balance.total",
1371 notes => <<" NOTES");
1372 Returns an object/hash of transaction, circ, title where transaction = an open
1373 user transaction that has a balance (mbts objects), circ is the attached
1374 circluation, and title is the title the circ points to
1375 Params are login_session, user_id
1376 Optional third parameter is the transaction type. defaults to all
1381 sub user_transactions {
1382 my( $self, $client, $login_session, $user_id, $type ) = @_;
1384 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1385 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1386 return $evt if $evt;
1388 my $api = $self->api_name();
1392 if(defined($type)) { @xact = (xact_type => $type);
1394 } else { @xact = (); }
1397 ->method_lookup('open-ils.actor.user.transactions.history.still_open')
1398 ->run($login_session => $user_id => $type);
1400 if($api =~ /have_charge/o) {
1402 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1404 } elsif($api =~ /have_balance/o) {
1406 $trans = [ grep { int($_->balance_owed * 100) != 0 } @$trans ];
1409 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1413 if($api =~ /total/o) {
1415 for my $t (@$trans) {
1416 $total += $t->balance_owed;
1419 $logger->debug("Total balance owed by user $user_id: $total");
1423 if($api =~ /count/o) { return scalar @$trans; }
1424 if($api !~ /fleshed/o) { return $trans; }
1427 for my $t (@$trans) {
1429 if( $t->xact_type ne 'circulation' ) {
1430 push @resp, {transaction => $t};
1434 my $circ = $apputils->simple_scalar_request(
1436 "open-ils.cstore.direct.action.circulation.retrieve",
1441 my $title = $apputils->simple_scalar_request(
1443 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1444 $circ->target_copy );
1448 my $u = OpenILS::Utils::ModsParser->new();
1449 $u->start_mods_batch($title->marc());
1450 my $mods = $u->finish_mods_batch();
1451 $mods->doc_id($title->id) if $mods;
1453 push @resp, {transaction => $t, circ => $circ, record => $mods };
1461 __PACKAGE__->register_method(
1462 method => "user_transaction_retrieve",
1463 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1465 notes => <<" NOTES");
1466 Returns a fleshedtransaction record
1468 __PACKAGE__->register_method(
1469 method => "user_transaction_retrieve",
1470 api_name => "open-ils.actor.user.transaction.retrieve",
1472 notes => <<" NOTES");
1473 Returns a transaction record
1475 sub user_transaction_retrieve {
1476 my( $self, $client, $login_session, $bill_id ) = @_;
1478 my $trans = $apputils->simple_scalar_request(
1480 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1484 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1485 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1486 return $evt if $evt;
1488 my $api = $self->api_name();
1489 if($api !~ /fleshed/o) { return $trans; }
1491 if( $trans->xact_type ne 'circulation' ) {
1492 $logger->debug("Returning non-circ transaction");
1493 return {transaction => $trans};
1496 my $circ = $apputils->simple_scalar_request(
1498 "open-ils..direct.action.circulation.retrieve",
1501 return {transaction => $trans} unless $circ;
1502 $logger->debug("Found the circ transaction");
1504 my $title = $apputils->simple_scalar_request(
1506 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1507 $circ->target_copy );
1509 return {transaction => $trans, circ => $circ } unless $title;
1510 $logger->debug("Found the circ title");
1514 my $u = OpenILS::Utils::ModsParser->new();
1515 $u->start_mods_batch($title->marc());
1516 $mods = $u->finish_mods_batch();
1518 if ($title->id == OILS_PRECAT_RECORD) {
1519 my $copy = $apputils->simple_scalar_request(
1521 "open-ils.cstore.direct.asset.copy.retrieve",
1522 $circ->target_copy );
1524 $mods = new Fieldmapper::metabib::virtual_record;
1525 $mods->doc_id(OILS_PRECAT_RECORD);
1526 $mods->title($copy->dummy_title);
1527 $mods->author($copy->dummy_author);
1531 $logger->debug("MODSized the circ title");
1533 return {transaction => $trans, circ => $circ, record => $mods };
1537 __PACKAGE__->register_method(
1538 method => "hold_request_count",
1539 api_name => "open-ils.actor.user.hold_requests.count",
1541 notes => <<" NOTES");
1542 Returns hold ready/total counts
1544 sub hold_request_count {
1545 my( $self, $client, $login_session, $userid ) = @_;
1547 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1548 $login_session, $userid, 'VIEW_HOLD' );
1549 return $evt if $evt;
1552 my $holds = $apputils->simple_scalar_request(
1554 "open-ils.cstore.direct.action.hold_request.search.atomic",
1557 fulfillment_time => {"=" => undef },
1558 cancel_time => undef,
1563 for my $h (@$holds) {
1564 next unless $h->capture_time and $h->current_copy;
1566 my $copy = $apputils->simple_scalar_request(
1568 "open-ils.cstore.direct.asset.copy.retrieve",
1572 if ($copy and $copy->status == 8) {
1577 return { total => scalar(@$holds), ready => scalar(@ready) };
1581 __PACKAGE__->register_method(
1582 method => "checkedout_count",
1583 api_name => "open-ils.actor.user.checked_out.count__",
1585 notes => <<" NOTES");
1586 Returns a transaction record
1590 sub checkedout_count {
1591 my( $self, $client, $login_session, $userid ) = @_;
1593 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1594 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1595 return $evt if $evt;
1597 my $circs = $apputils->simple_scalar_request(
1599 "open-ils.cstore.direct.action.circulation.search.atomic",
1600 { usr => $userid, stop_fines => undef }
1601 #{ usr => $userid, checkin_time => {"=" => undef } }
1604 my $parser = DateTime::Format::ISO8601->new;
1607 for my $c (@$circs) {
1608 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1609 my $due = $due_dt->epoch;
1611 if ($due < DateTime->today->epoch) {
1616 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1620 __PACKAGE__->register_method(
1621 method => "checked_out",
1622 api_name => "open-ils.actor.user.checked_out",
1625 Returns a structure of circulations objects sorted by
1626 out, overdue, lost, claims_returned, long_overdue.
1627 A list of IDs are returned of each type.
1628 lost, long_overdue, and claims_returned circ will not
1629 be "finished" (there is an outstanding balance or some
1630 other pending action on the circ).
1632 The .count method also includes a 'total' field which
1633 sums all "open" circs
1637 __PACKAGE__->register_method(
1638 method => "checked_out",
1639 api_name => "open-ils.actor.user.checked_out.count",
1641 signature => q/@see open-ils.actor.user.checked_out/
1645 my( $self, $conn, $auth, $userid ) = @_;
1647 my $e = new_editor(authtoken=>$auth);
1648 return $e->event unless $e->checkauth;
1650 if( $userid ne $e->requestor->id ) {
1651 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1654 my $count = $self->api_name =~ /count/;
1655 return _checked_out( $count, $e, $userid );
1659 my( $iscount, $e, $userid ) = @_;
1661 my $circs = $e->search_action_circulation(
1662 { usr => $userid, stop_fines => undef });
1664 my $mcircs = $e->search_action_circulation(
1667 checkin_time => undef,
1668 xact_finish => undef,
1669 stop_fines => OILS_STOP_FINES_MAX_FINES
1673 push( @$circs, @$mcircs );
1675 my $parser = DateTime::Format::ISO8601->new;
1677 # split the circs up into overdue and not-overdue circs
1679 for my $c (@$circs) {
1680 if( $c->due_date ) {
1681 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1682 my $due = $due_dt->epoch;
1683 if ($due < DateTime->today->epoch) {
1684 push @overdue, $c->id;
1693 # grab all of the lost, claims-returned, and longoverdue circs
1694 #my $open = $e->search_action_circulation(
1695 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1698 # these items have stop_fines, but no xact_finish, so money
1699 # is owed on them and they have not been checked in
1700 my $open = $e->search_action_circulation(
1703 stop_fines => { '!=' => undef },
1704 xact_finish => undef,
1705 checkin_time => undef,
1710 my( @lost, @cr, @lo );
1711 for my $c (@$open) {
1712 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1713 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1714 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1720 total => @$circs + @lost + @cr + @lo,
1721 out => scalar(@out),
1722 overdue => scalar(@overdue),
1723 lost => scalar(@lost),
1724 claims_returned => scalar(@cr),
1725 long_overdue => scalar(@lo)
1731 overdue => \@overdue,
1733 claims_returned => \@cr,
1734 long_overdue => \@lo
1740 __PACKAGE__->register_method(
1741 method => "checked_in_with_fines",
1742 api_name => "open-ils.actor.user.checked_in_with_fines",
1744 signature => q/@see open-ils.actor.user.checked_out/
1746 sub checked_in_with_fines {
1747 my( $self, $conn, $auth, $userid ) = @_;
1749 my $e = new_editor(authtoken=>$auth);
1750 return $e->event unless $e->checkauth;
1752 if( $userid ne $e->requestor->id ) {
1753 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1756 # money is owed on these items and they are checked in
1757 my $open = $e->search_action_circulation(
1760 xact_finish => undef,
1761 checkin_time => { "!=" => undef },
1766 my( @lost, @cr, @lo );
1767 for my $c (@$open) {
1768 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1769 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1770 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1775 claims_returned => \@cr,
1776 long_overdue => \@lo
1788 __PACKAGE__->register_method(
1789 method => "user_transaction_history",
1790 api_name => "open-ils.actor.user.transactions.history",
1792 notes => <<" NOTES");
1793 Returns a list of billable transaction ids for a user, optionally by type
1795 __PACKAGE__->register_method(
1796 method => "user_transaction_history",
1797 api_name => "open-ils.actor.user.transactions.history.have_charge",
1799 notes => <<" NOTES");
1800 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1802 __PACKAGE__->register_method(
1803 method => "user_transaction_history",
1804 api_name => "open-ils.actor.user.transactions.history.have_balance",
1806 notes => <<" NOTES");
1807 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1809 __PACKAGE__->register_method(
1810 method => "user_transaction_history",
1811 api_name => "open-ils.actor.user.transactions.history.still_open",
1813 notes => <<" NOTES");
1814 Returns a list of billable transaction ids for a user that are not finished
1816 __PACKAGE__->register_method(
1817 method => "user_transaction_history",
1818 api_name => "open-ils.actor.user.transactions.history.have_bill",
1820 notes => <<" NOTES");
1821 Returns a list of billable transaction ids for a user that has billings
1827 sub _user_transaction_history {
1828 my( $self, $client, $login_session, $user_id, $type ) = @_;
1830 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1831 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1832 return $evt if $evt;
1834 my $api = $self->api_name();
1839 @xact = (xact_type => $type) if(defined($type));
1840 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1841 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1843 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1845 my $trans = $apputils->simple_scalar_request(
1847 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1848 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1850 return [ map { $_->id } @$trans ];
1858 for my $x (@xacts) {
1859 my $s = new Fieldmapper::money::billable_transaction_summary;
1862 $s->xact_start( $x->xact_start );
1863 $s->xact_finish( $x->xact_finish );
1867 for my $b (@{ $x->billings }) {
1868 next if ($U->is_true($b->voided));
1869 $to += int($b->amount * 100);
1870 $lb ||= $b->billing_ts;
1871 if ($b->billing_ts ge $lb) {
1872 $lb = $b->billing_ts;
1873 $s->last_billing_note($b->note);
1874 $s->last_billing_ts($b->billing_ts);
1875 $s->last_billing_type($b->billing_type);
1878 $s->total_owed( sprintf('%0.2f', $to / 100 );
1882 for my $p (@{ $x->payments }) {
1883 next if ($U->is_true($p->voided));
1884 $tp += int($p->amount * 100);
1885 $lp ||= $p->payment_ts;
1886 if ($p->payment_ts ge $lp) {
1887 $lp = $p->payment_ts;
1888 $s->last_payment_note($p->note);
1889 $s->last_payment_ts($p->payment_ts);
1890 $s->last_payment_type($p->payment_type);
1893 $s->total_paid( sprintf('%0.2f', $tp / 100 );
1895 $s->balance_owed( sprintf('%0.2f', int($to - $tp) / 100);
1897 $s->xact_type( 'grocery' ) if ($x->grocery);
1898 $s->xact_type( 'circulation' ) if ($x->circulation);
1906 sub user_transaction_history {
1907 my( $self, $conn, $auth, $userid, $type ) = @_;
1908 my $e = new_editor(authtoken=>$auth);
1909 return $e->event unless $e->checkauth;
1910 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1912 my $api = $self->api_name;
1913 my @xact_finish = (xact_finish => undef ) if ($api !~ /history$/);
1915 my @xacts = @{ $e->search_money_billable_transaction(
1916 [ { usr => $userid, @xact_finish },
1918 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
1919 order_by => { mbt => 'xact_start DESC' },
1924 my @mbts = _make_mbts( @xacts );
1926 if(defined($type)) {
1927 @mbts = grep { $_->xact_type eq $type } @mbts;
1930 if($api =~ /have_balance/o) {
1931 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
1934 if($api =~ /have_charge/o) {
1935 @mbts = grep { defined($_->last_billing_ts) } @mbts;
1938 if($api =~ /have_bill/o) {
1939 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
1947 __PACKAGE__->register_method(
1948 method => "user_perms",
1949 api_name => "open-ils.actor.permissions.user_perms.retrieve",
1951 notes => <<" NOTES");
1952 Returns a list of permissions
1955 my( $self, $client, $authtoken, $user ) = @_;
1957 my( $staff, $evt ) = $apputils->checkses($authtoken);
1958 return $evt if $evt;
1960 $user ||= $staff->id;
1962 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1966 return $apputils->simple_scalar_request(
1968 "open-ils.storage.permission.user_perms.atomic",
1972 __PACKAGE__->register_method(
1973 method => "retrieve_perms",
1974 api_name => "open-ils.actor.permissions.retrieve",
1975 notes => <<" NOTES");
1976 Returns a list of permissions
1978 sub retrieve_perms {
1979 my( $self, $client ) = @_;
1980 return $apputils->simple_scalar_request(
1982 "open-ils.cstore.direct.permission.perm_list.search.atomic",
1983 { id => { '!=' => undef } }
1987 __PACKAGE__->register_method(
1988 method => "retrieve_groups",
1989 api_name => "open-ils.actor.groups.retrieve",
1990 notes => <<" NOTES");
1991 Returns a list of user groupss
1993 sub retrieve_groups {
1994 my( $self, $client ) = @_;
1995 return new_editor()->retrieve_all_permission_grp_tree();
1998 __PACKAGE__->register_method(
1999 method => "retrieve_org_address",
2000 api_name => "open-ils.actor.org_unit.address.retrieve",
2001 notes => <<' NOTES');
2002 Returns an org_unit address by ID
2003 @param An org_address ID
2005 sub retrieve_org_address {
2006 my( $self, $client, $id ) = @_;
2007 return $apputils->simple_scalar_request(
2009 "open-ils.cstore.direct.actor.org_address.retrieve",
2014 __PACKAGE__->register_method(
2015 method => "retrieve_groups_tree",
2016 api_name => "open-ils.actor.groups.tree.retrieve",
2017 notes => <<" NOTES");
2018 Returns a list of user groups
2020 sub retrieve_groups_tree {
2021 my( $self, $client ) = @_;
2022 return new_editor()->search_permission_grp_tree(
2027 flesh_fields => { pgt => ["children"] },
2028 order_by => { pgt => 'name'}
2035 # turns an org list into an org tree
2037 sub build_group_tree {
2039 my( $self, $grplist) = @_;
2041 return $grplist unless (
2042 ref($grplist) and @$grplist > 1 );
2044 my @list = sort { $a->name cmp $b->name } @$grplist;
2047 for my $grp (@list) {
2049 if ($grp and !defined($grp->parent)) {
2053 my ($parent) = grep { $_->id == $grp->parent} @list;
2055 $parent->children([]) unless defined($parent->children);
2056 push( @{$parent->children}, $grp );
2064 __PACKAGE__->register_method(
2065 method => "add_user_to_groups",
2066 api_name => "open-ils.actor.user.set_groups",
2067 notes => <<" NOTES");
2068 Adds a user to one or more permission groups
2071 sub add_user_to_groups {
2072 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2074 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2075 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2076 return $evt if $evt;
2078 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2079 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2080 return $evt if $evt;
2082 $apputils->simplereq(
2084 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2086 for my $group (@$groups) {
2087 my $link = Fieldmapper::permission::usr_grp_map->new;
2089 $link->usr($userid);
2091 my $id = $apputils->simplereq(
2093 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2099 __PACKAGE__->register_method(
2100 method => "get_user_perm_groups",
2101 api_name => "open-ils.actor.user.get_groups",
2102 notes => <<" NOTES");
2103 Retrieve a user's permission groups.
2107 sub get_user_perm_groups {
2108 my( $self, $client, $authtoken, $userid ) = @_;
2110 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2111 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2112 return $evt if $evt;
2114 return $apputils->simplereq(
2116 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2121 __PACKAGE__->register_method (
2122 method => 'register_workstation',
2123 api_name => 'open-ils.actor.workstation.register.override',
2124 signature => q/@see open-ils.actor.workstation.register/);
2126 __PACKAGE__->register_method (
2127 method => 'register_workstation',
2128 api_name => 'open-ils.actor.workstation.register',
2130 Registers a new workstion in the system
2131 @param authtoken The login session key
2132 @param name The name of the workstation id
2133 @param owner The org unit that owns this workstation
2134 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2135 if the name is already in use.
2138 sub _register_workstation {
2139 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2140 my( $requestor, $evt ) = $U->checkses($authtoken);
2141 return $evt if $evt;
2142 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2143 return $evt if $evt;
2145 my $ws = $U->cstorereq(
2146 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2147 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2149 $ws = Fieldmapper::actor::workstation->new;
2150 $ws->owning_lib($owner);
2153 my $id = $U->storagereq(
2154 'open-ils.storage.direct.actor.workstation.create', $ws );
2155 return $U->DB_UPDATE_FAILED($ws) unless $id;
2161 sub register_workstation {
2162 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2164 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2165 return $e->event unless $e->checkauth;
2166 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2167 my $existing = $e->search_actor_workstation({name => $name});
2170 if( $self->api_name =~ /override/o ) {
2171 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2172 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2174 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2178 my $ws = Fieldmapper::actor::workstation->new;
2179 $ws->owning_lib($owner);
2181 $e->create_actor_workstation($ws) or return $e->event;
2183 return $ws->id; # note: editor sets the id on the new object for us
2187 __PACKAGE__->register_method (
2188 method => 'fetch_patron_note',
2189 api_name => 'open-ils.actor.note.retrieve.all',
2191 Returns a list of notes for a given user
2192 Requestor must have VIEW_USER permission if pub==false and
2193 @param authtoken The login session key
2194 @param args Hash of params including
2195 patronid : the patron's id
2196 pub : true if retrieving only public notes
2200 sub fetch_patron_note {
2201 my( $self, $conn, $authtoken, $args ) = @_;
2202 my $patronid = $$args{patronid};
2204 my($reqr, $evt) = $U->checkses($authtoken);
2207 ($patron, $evt) = $U->fetch_user($patronid);
2208 return $evt if $evt;
2211 if( $patronid ne $reqr->id ) {
2212 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2213 return $evt if $evt;
2215 return $U->cstorereq(
2216 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2217 { usr => $patronid, pub => 't' } );
2220 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2221 return $evt if $evt;
2223 return $U->cstorereq(
2224 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2227 __PACKAGE__->register_method (
2228 method => 'create_user_note',
2229 api_name => 'open-ils.actor.note.create',
2231 Creates a new note for the given user
2232 @param authtoken The login session key
2233 @param note The note object
2236 sub create_user_note {
2237 my( $self, $conn, $authtoken, $note ) = @_;
2238 my( $reqr, $patron, $evt ) =
2239 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2240 return $evt if $evt;
2241 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2243 $note->creator($reqr->id);
2244 my $id = $U->storagereq(
2245 'open-ils.storage.direct.actor.usr_note.create', $note );
2246 return $U->DB_UPDATE_FAILED($note) unless $id;
2251 __PACKAGE__->register_method (
2252 method => 'delete_user_note',
2253 api_name => 'open-ils.actor.note.delete',
2255 Deletes a note for the given user
2256 @param authtoken The login session key
2257 @param noteid The note id
2260 sub delete_user_note {
2261 my( $self, $conn, $authtoken, $noteid ) = @_;
2263 my $note = $U->cstorereq(
2264 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2265 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2267 my( $reqr, $patron, $evt ) =
2268 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2269 return $evt if $evt;
2270 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2272 my $stat = $U->storagereq(
2273 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2274 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2279 __PACKAGE__->register_method (
2280 method => 'update_user_note',
2281 api_name => 'open-ils.actor.note.update',
2283 @param authtoken The login session key
2284 @param note The note
2288 sub update_user_note {
2289 my( $self, $conn, $auth, $note ) = @_;
2290 my $e = new_editor(authtoken=>$auth, xact=>1);
2291 return $e->event unless $e->checkauth;
2292 my $patron = $e->retrieve_actor_user($note->usr)
2293 or return $e->event;
2294 return $e->event unless
2295 $e->allowed('UPDATE_USER', $patron->home_ou);
2296 $e->update_actor_user_note($note)
2297 or return $e->event;
2305 __PACKAGE__->register_method (
2306 method => 'create_closed_date',
2307 api_name => 'open-ils.actor.org_unit.closed_date.create',
2309 Creates a new closing entry for the given org_unit
2310 @param authtoken The login session key
2311 @param note The closed_date object
2314 sub create_closed_date {
2315 my( $self, $conn, $authtoken, $cd ) = @_;
2317 my( $user, $evt ) = $U->checkses($authtoken);
2318 return $evt if $evt;
2320 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2321 return $evt if $evt;
2323 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2325 my $id = $U->storagereq(
2326 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2327 return $U->DB_UPDATE_FAILED($cd) unless $id;
2332 __PACKAGE__->register_method (
2333 method => 'delete_closed_date',
2334 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2336 Deletes a closing entry for the given org_unit
2337 @param authtoken The login session key
2338 @param noteid The close_date id
2341 sub delete_closed_date {
2342 my( $self, $conn, $authtoken, $cd ) = @_;
2344 my( $user, $evt ) = $U->checkses($authtoken);
2345 return $evt if $evt;
2348 ($cd_obj, $evt) = fetch_closed_date($cd);
2349 return $evt if $evt;
2351 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2352 return $evt if $evt;
2354 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2356 my $stat = $U->storagereq(
2357 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2358 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2363 __PACKAGE__->register_method(
2364 method => 'usrname_exists',
2365 api_name => 'open-ils.actor.username.exists',
2367 Returns 1 if the requested username exists, returns 0 otherwise
2371 sub usrname_exists {
2372 my( $self, $conn, $auth, $usrname ) = @_;
2373 my $e = new_editor(authtoken=>$auth);
2374 return $e->event unless $e->checkauth;
2375 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2376 return $$a[0] if $a and @$a;
2380 __PACKAGE__->register_method(
2381 method => 'barcode_exists',
2382 api_name => 'open-ils.actor.barcode.exists',
2384 Returns 1 if the requested barcode exists, returns 0 otherwise
2388 sub barcode_exists {
2389 my( $self, $conn, $auth, $barcode ) = @_;
2390 my $e = new_editor(authtoken=>$auth);
2391 return $e->event unless $e->checkauth;
2392 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2393 return $$a[0] if $a and @$a;
2398 __PACKAGE__->register_method(
2399 method => 'retrieve_net_levels',
2400 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2403 sub retrieve_net_levels {
2404 my( $self, $conn, $auth ) = @_;
2405 my $e = new_editor(authtoken=>$auth);
2406 return $e->event unless $e->checkauth;
2407 return $e->retrieve_all_config_net_access_level();
2411 __PACKAGE__->register_method(
2412 method => 'fetch_org_by_shortname',
2413 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2415 sub fetch_org_by_shortname {
2416 my( $self, $conn, $sname ) = @_;
2417 my $e = new_editor();
2418 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2419 return $e->event unless $org;
2424 __PACKAGE__->register_method(
2425 method => 'session_home_lib',
2426 api_name => 'open-ils.actor.session.home_lib',
2429 sub session_home_lib {
2430 my( $self, $conn, $auth ) = @_;
2431 my $e = new_editor(authtoken=>$auth);
2432 return undef unless $e->checkauth;
2433 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2434 return $org->shortname;
2439 __PACKAGE__->register_method(
2440 method => 'slim_tree',
2441 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2444 my $tree = new_editor()->search_actor_org_unit(
2446 {"parent_ou" => undef },
2449 flesh_fields => { aou => ['children'] },
2450 order_by => { aou => 'name'},
2451 select => { aou => ["id","shortname", "name"]},
2456 return trim_tree($tree);
2462 return undef unless $tree;
2464 code => $tree->shortname,
2465 name => $tree->name,
2467 if( $tree->children and @{$tree->children} ) {
2468 $htree->{children} = [];
2469 for my $c (@{$tree->children}) {
2470 push( @{$htree->{children}}, trim_tree($c) );
2479 __PACKAGE__->register_method(
2480 method => "user_retrieve_fleshed_by_id",
2481 api_name => "open-ils.actor.user.fleshed.retrieve",);
2483 sub user_retrieve_fleshed_by_id {
2484 my( $self, $client, $auth, $user_id, $fields ) = @_;
2485 my $e = new_editor(authtoken => $auth);
2486 return $e->event unless $e->checkauth;
2487 if( $e->requestor->id != $user_id ) {
2488 return $e->event unless $e->allowed('VIEW_USER');
2493 "standing_penalties",
2497 "stat_cat_entries" ];
2498 return new_flesh_user($user_id, $fields, $e);
2502 sub new_flesh_user {
2505 my $fields = shift || [];
2506 my $e = shift || new_editor(xact=>1);
2508 my $user = $e->retrieve_actor_user(
2513 "flesh_fields" => { "au" => $fields }
2516 ) or return $e->event;
2519 if( grep { $_ eq 'addresses' } @$fields ) {
2521 $user->addresses([]) unless @{$user->addresses};
2523 if( ref $user->billing_address ) {
2524 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2525 push( @{$user->addresses}, $user->billing_address );
2529 if( ref $user->mailing_address ) {
2530 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2531 push( @{$user->addresses}, $user->mailing_address );
2537 $user->clear_passwd();
2544 __PACKAGE__->register_method(
2545 method => "user_retrieve_parts",
2546 api_name => "open-ils.actor.user.retrieve.parts",);
2548 sub user_retrieve_parts {
2549 my( $self, $client, $auth, $user_id, $fields ) = @_;
2550 my $e = new_editor(authtoken => $auth);
2551 return $e->event unless $e->checkauth;
2552 if( $e->requestor->id != $user_id ) {
2553 return $e->event unless $e->allowed('VIEW_USER');
2556 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2557 push(@resp, $user->$_()) for(@$fields);