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 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
245 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
248 ($new_patron, $evt) = _create_standing_penalties($session, $user_session, $patron, $new_patron, $user_obj);
251 $logger->activity("user ".$user_obj->id." updating/creating user ".$new_patron->id);
254 if(!$patron->isnew) {
255 $opatron = new_editor()->retrieve_actor_user($new_patron->id);
258 $apputils->commit_db_session($session);
259 my $fuser = flesh_user($new_patron->id());
262 # Log the new and old patron for investigation
263 $logger->info("$user_session updating patron object. orig patron object = ".
264 JSON->perl2JSON($opatron). " |||| new patron = ".JSON->perl2JSON($fuser));
274 return new_flesh_user($id, [
277 "standing_penalties",
281 "stat_cat_entries" ] );
289 # clone and clear stuff that would break the database
293 my $new_patron = $patron->clone;
295 $new_patron->clear_billing_address();
296 $new_patron->clear_mailing_address();
297 $new_patron->clear_addresses();
298 $new_patron->clear_card();
299 $new_patron->clear_cards();
300 $new_patron->clear_id();
301 $new_patron->clear_isnew();
302 $new_patron->clear_ischanged();
303 $new_patron->clear_isdeleted();
304 $new_patron->clear_stat_cat_entries();
305 $new_patron->clear_permissions();
306 $new_patron->clear_standing_penalties();
316 my $user_obj = shift;
318 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
319 return (undef, $evt) if $evt;
321 my $ex = $session->request(
322 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
324 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
327 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
329 my $id = $session->request(
330 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
331 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
333 $logger->info("Successfully created new user [$id] in DB");
335 return ( $session->request(
336 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
340 sub check_group_perm {
341 my( $session, $requestor, $patron ) = @_;
344 # first let's see if the requestor has
345 # priveleges to update this user in any way
346 if( ! $patron->isnew ) {
347 my $p = $session->request(
348 'open-ils.storage.direct.actor.user.retrieve', $patron->id )->gather(1);
350 # If we are the requestor (trying to update our own account)
351 # and we are not trying to change our profile, we're good
352 if( $p->id == $requestor->id and
353 $p->profile == $patron->profile ) {
358 $evt = group_perm_failed($session, $requestor, $p);
362 # They are allowed to edit this patron.. can they put the
363 # patron into the group requested?
364 $evt = group_perm_failed($session, $requestor, $patron);
370 sub group_perm_failed {
371 my( $session, $requestor, $patron ) = @_;
375 my $grpid = $patron->profile;
379 $logger->debug("user update looking for group perm for group $grpid");
380 $grp = $session->request(
381 'open-ils.storage.direct.permission.grp_tree.retrieve', $grpid )->gather(1);
382 return OpenILS::Event->new('PERMISSION_GRP_TREE_NOT_FOUND') unless $grp;
384 } while( !($perm = $grp->application_perm) and ($grpid = $grp->parent) );
386 $logger->info("user update checking perm $perm on user ".
387 $requestor->id." for update/create on user username=".$patron->usrname);
389 my $evt = $U->check_perms($requestor->id, $patron->home_ou, $perm);
397 my( $session, $patron, $user_obj, $noperm) = @_;
399 $logger->info("Updating patron ".$patron->id." in DB");
404 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
405 return (undef, $evt) if $evt;
408 # update the password by itself to avoid the password protection magic
409 if( $patron->passwd ) {
410 my $s = $session->request(
411 'open-ils.storage.direct.actor.user.remote_update',
412 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
413 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
414 $patron->clear_passwd;
417 if(!$patron->ident_type) {
418 $patron->clear_ident_type;
419 $patron->clear_ident_value;
422 my $stat = $session->request(
423 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
424 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
429 sub _check_dup_ident {
430 my( $session, $patron ) = @_;
432 return undef unless $patron->ident_value;
435 ident_type => $patron->ident_type,
436 ident_value => $patron->ident_value,
439 $logger->debug("patron update searching for dup ident values: " .
440 $patron->ident_type . ':' . $patron->ident_value);
442 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
444 my $dups = $session->request(
445 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
448 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
455 sub _add_update_addresses {
459 my $new_patron = shift;
463 my $current_id; # id of the address before creation
465 for my $address (@{$patron->addresses()}) {
467 next unless ref $address;
468 $current_id = $address->id();
470 if( $patron->billing_address() and
471 $patron->billing_address() == $current_id ) {
472 $logger->info("setting billing addr to $current_id");
473 $new_patron->billing_address($address->id());
474 $new_patron->ischanged(1);
477 if( $patron->mailing_address() and
478 $patron->mailing_address() == $current_id ) {
479 $new_patron->mailing_address($address->id());
480 $logger->info("setting mailing addr to $current_id");
481 $new_patron->ischanged(1);
485 if($address->isnew()) {
487 $address->usr($new_patron->id());
489 ($address, $evt) = _add_address($session,$address);
490 return (undef, $evt) if $evt;
492 # we need to get the new id
493 if( $patron->billing_address() and
494 $patron->billing_address() == $current_id ) {
495 $new_patron->billing_address($address->id());
496 $logger->info("setting billing addr to $current_id");
497 $new_patron->ischanged(1);
500 if( $patron->mailing_address() and
501 $patron->mailing_address() == $current_id ) {
502 $new_patron->mailing_address($address->id());
503 $logger->info("setting mailing addr to $current_id");
504 $new_patron->ischanged(1);
507 } elsif($address->ischanged() ) {
509 ($address, $evt) = _update_address($session, $address);
510 return (undef, $evt) if $evt;
512 } elsif($address->isdeleted() ) {
514 if( $address->id() == $new_patron->mailing_address() ) {
515 $new_patron->clear_mailing_address();
516 ($new_patron, $evt) = _update_patron($session, $new_patron);
517 return (undef, $evt) if $evt;
520 if( $address->id() == $new_patron->billing_address() ) {
521 $new_patron->clear_billing_address();
522 ($new_patron, $evt) = _update_patron($session, $new_patron);
523 return (undef, $evt) if $evt;
526 $evt = _delete_address($session, $address);
527 return (undef, $evt) if $evt;
531 return ( $new_patron, undef );
535 # adds an address to the db and returns the address with new id
537 my($session, $address) = @_;
538 $address->clear_id();
540 $logger->info("Creating new address at street ".$address->street1);
542 # put the address into the database
543 my $id = $session->request(
544 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
545 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
548 return ($address, undef);
552 sub _update_address {
553 my( $session, $address ) = @_;
555 $logger->info("Updating address ".$address->id." in the DB");
557 my $stat = $session->request(
558 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
560 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
561 return ($address, undef);
566 sub _add_update_cards {
570 my $new_patron = shift;
574 my $virtual_id; #id of the card before creation
575 for my $card (@{$patron->cards()}) {
577 $card->usr($new_patron->id());
579 if(ref($card) and $card->isnew()) {
581 $virtual_id = $card->id();
582 ( $card, $evt ) = _add_card($session,$card);
583 return (undef, $evt) if $evt;
585 #if(ref($patron->card)) { $patron->card($patron->card->id); }
586 if($patron->card() == $virtual_id) {
587 $new_patron->card($card->id());
588 $new_patron->ischanged(1);
591 } elsif( ref($card) and $card->ischanged() ) {
592 $evt = _update_card($session, $card);
593 return (undef, $evt) if $evt;
597 return ( $new_patron, undef );
601 # adds an card to the db and returns the card with new id
603 my( $session, $card ) = @_;
606 $logger->info("Adding new patron card ".$card->barcode);
608 my $id = $session->request(
609 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
610 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
611 $logger->info("Successfully created patron card $id");
614 return ( $card, undef );
618 # returns event on error. returns undef otherwise
620 my( $session, $card ) = @_;
621 $logger->info("Updating patron card ".$card->id);
623 my $stat = $session->request(
624 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
625 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
632 # returns event on error. returns undef otherwise
633 sub _delete_address {
634 my( $session, $address ) = @_;
636 $logger->info("Deleting address ".$address->id." from DB");
638 my $stat = $session->request(
639 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
641 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
647 sub _add_survey_responses {
648 my ($session, $patron, $new_patron) = @_;
650 $logger->info( "Updating survey responses for patron ".$new_patron->id );
652 my $responses = $patron->survey_responses;
656 $_->usr($new_patron->id) for (@$responses);
658 my $evt = $U->simplereq( "open-ils.circ",
659 "open-ils.circ.survey.submit.user_id", $responses );
661 return (undef, $evt) if defined($U->event_code($evt));
665 return ( $new_patron, undef );
669 sub _create_stat_maps {
671 my($session, $user_session, $patron, $new_patron) = @_;
673 my $maps = $patron->stat_cat_entries();
675 for my $map (@$maps) {
677 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
679 if ($map->isdeleted()) {
680 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
682 } elsif ($map->isnew()) {
683 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
688 $map->target_usr($new_patron->id);
691 $logger->info("Updating stat entry with method $method and map $map");
693 my $stat = $session->request($method, $map)->gather(1);
694 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
698 return ($new_patron, undef);
701 sub _create_perm_maps {
703 my($session, $user_session, $patron, $new_patron) = @_;
705 my $maps = $patron->permissions;
707 for my $map (@$maps) {
709 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
710 if ($map->isdeleted()) {
711 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
712 } elsif ($map->isnew()) {
713 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
718 $map->usr($new_patron->id);
720 #warn( "Updating permissions with method $method and session $user_session and map $map" );
721 $logger->info( "Updating permissions with method $method and map $map" );
723 my $stat = $session->request($method, $map)->gather(1);
724 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
728 return ($new_patron, undef);
732 sub _create_standing_penalties {
734 my($session, $user_session, $patron, $new_patron) = @_;
736 my $maps = $patron->standing_penalties;
739 for my $map (@$maps) {
741 if ($map->isdeleted()) {
742 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
743 } elsif ($map->isnew()) {
744 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
750 $map->usr($new_patron->id);
752 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
754 my $stat = $session->request($method, $map)->gather(1);
755 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
758 return ($new_patron, undef);
763 __PACKAGE__->register_method(
764 method => "search_username",
765 api_name => "open-ils.actor.user.search.username",
768 sub search_username {
769 my($self, $client, $username) = @_;
770 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
772 "open-ils.cstore.direct.actor.user.search.atomic",
773 { usrname => $username }
781 __PACKAGE__->register_method(
782 method => "user_retrieve_by_barcode",
783 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
785 sub user_retrieve_by_barcode {
786 my($self, $client, $user_session, $barcode) = @_;
788 $logger->debug("Searching for user with barcode $barcode");
789 my ($user_obj, $evt) = $apputils->checkses($user_session);
792 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
794 "open-ils.cstore.direct.actor.card.search.atomic",
795 { barcode => $barcode }
798 if(!$card || !$card->[0]) {
799 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
803 my $user = flesh_user($card->usr());
805 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
808 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
815 __PACKAGE__->register_method(
816 method => "get_user_by_id",
817 api_name => "open-ils.actor.user.retrieve",);
820 my ($self, $client, $auth, $id) = @_;
821 my $e = new_editor(authtoken=>$auth);
822 return $e->event unless $e->checkauth;
823 my $user = $e->retrieve_actor_user($id)
825 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
831 __PACKAGE__->register_method(
832 method => "get_org_types",
833 api_name => "open-ils.actor.org_types.retrieve",);
837 my($self, $client) = @_;
838 return $org_types if $org_types;
839 return $org_types = new_editor()->retrieve_all_actor_org_unit_type();
844 __PACKAGE__->register_method(
845 method => "get_user_ident_types",
846 api_name => "open-ils.actor.user.ident_types.retrieve",
849 sub get_user_ident_types {
850 return $ident_types if $ident_types;
851 return $ident_types =
852 new_editor()->retrieve_all_config_identification_type();
858 __PACKAGE__->register_method(
859 method => "get_org_unit",
860 api_name => "open-ils.actor.org_unit.retrieve",
864 my( $self, $client, $user_session, $org_id ) = @_;
865 my $e = new_editor(authtoken => $user_session);
867 return $e->event unless $e->checkauth;
868 $org_id = $e->requestor->ws_ou;
870 my $o = $e->retrieve_actor_org_unit($org_id)
875 __PACKAGE__->register_method(
876 method => "search_org_unit",
877 api_name => "open-ils.actor.org_unit_list.search",
880 sub search_org_unit {
882 my( $self, $client, $field, $value ) = @_;
884 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
886 "open-ils.cstore.direct.actor.org_unit.search.atomic",
887 { $field => $value } );
895 __PACKAGE__->register_method(
896 method => "get_org_tree",
897 api_name => "open-ils.actor.org_tree.retrieve",
899 note => "Returns the entire org tree structure",
903 my( $self, $client) = @_;
905 $cache = OpenSRF::Utils::Cache->new("global", 0) unless $cache;
906 my $tree = $cache->get_cache('orgtree');
907 return $tree if $tree;
909 $tree = new_editor()->search_actor_org_unit(
911 {"parent_ou" => undef },
914 flesh_fields => { aou => ['children'] },
915 order_by => { aou => 'name'}
920 $cache->put_cache('orgtree', $tree);
925 # turns an org list into an org tree
928 my( $self, $orglist) = @_;
930 return $orglist unless (
931 ref($orglist) and @$orglist > 1 );
934 $a->ou_type <=> $b->ou_type ||
935 $a->name cmp $b->name } @$orglist;
937 for my $org (@list) {
939 next unless ($org and defined($org->parent_ou));
940 my ($parent) = grep { $_->id == $org->parent_ou } @list;
943 $parent->children([]) unless defined($parent->children);
944 push( @{$parent->children}, $org );
952 __PACKAGE__->register_method(
953 method => "get_org_descendants",
954 api_name => "open-ils.actor.org_tree.descendants.retrieve"
957 # depth is optional. org_unit is the id
958 sub get_org_descendants {
959 my( $self, $client, $org_unit, $depth ) = @_;
960 my $orglist = $apputils->simple_scalar_request(
962 "open-ils.storage.actor.org_unit.descendants.atomic",
964 return $self->build_org_tree($orglist);
968 __PACKAGE__->register_method(
969 method => "get_org_ancestors",
970 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
973 # depth is optional. org_unit is the id
974 sub get_org_ancestors {
975 my( $self, $client, $org_unit, $depth ) = @_;
976 my $orglist = $apputils->simple_scalar_request(
978 "open-ils.storage.actor.org_unit.ancestors.atomic",
980 return $self->build_org_tree($orglist);
984 __PACKAGE__->register_method(
985 method => "get_standings",
986 api_name => "open-ils.actor.standings.retrieve"
991 return $user_standings if $user_standings;
992 return $user_standings =
993 $apputils->simple_scalar_request(
995 "open-ils.cstore.direct.config.standing.search.atomic",
996 { id => { "!=" => undef } }
1002 __PACKAGE__->register_method(
1003 method => "get_my_org_path",
1004 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1007 sub get_my_org_path {
1008 my( $self, $client, $user_session, $org_id ) = @_;
1009 my $user_obj = $apputils->check_user_session($user_session);
1010 if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
1012 return $apputils->simple_scalar_request(
1014 "open-ils.storage.actor.org_unit.full_path.atomic",
1019 __PACKAGE__->register_method(
1020 method => "patron_adv_search",
1021 api_name => "open-ils.actor.patron.search.advanced" );
1022 sub patron_adv_search {
1023 my( $self, $client, $auth, $search_hash, $search_limit, $search_sort, $include_inactive ) = @_;
1024 my $e = new_editor(authtoken=>$auth);
1025 return $e->event unless $e->checkauth;
1026 return $e->event unless $e->allowed('VIEW_USER');
1027 return $U->storagereq(
1028 "open-ils.storage.actor.user.crazy_search",
1029 $search_hash, $search_limit, $search_sort, $include_inactive);
1034 sub _verify_password {
1035 my($user_session, $password) = @_;
1036 my $user_obj = $apputils->check_user_session($user_session);
1038 #grab the user with password
1039 $user_obj = $apputils->simple_scalar_request(
1041 "open-ils.cstore.direct.actor.user.retrieve",
1044 if($user_obj->passwd eq $password) {
1052 __PACKAGE__->register_method(
1053 method => "update_password",
1054 api_name => "open-ils.actor.user.password.update");
1056 __PACKAGE__->register_method(
1057 method => "update_password",
1058 api_name => "open-ils.actor.user.username.update");
1060 __PACKAGE__->register_method(
1061 method => "update_password",
1062 api_name => "open-ils.actor.user.email.update");
1064 sub update_password {
1065 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1069 my $user_obj = $apputils->check_user_session($user_session);
1071 if($self->api_name =~ /password/o) {
1073 #make sure they know the current password
1074 if(!_verify_password($user_session, md5_hex($current_password))) {
1075 return OpenILS::Event->new('INCORRECT_PASSWORD');
1078 $logger->debug("update_password setting new password $new_value");
1079 $user_obj->passwd($new_value);
1081 } elsif($self->api_name =~ /username/o) {
1082 my $users = search_username(undef, undef, $new_value);
1083 if( $users and $users->[0] ) {
1084 return OpenILS::Event->new('USERNAME_EXISTS');
1086 $user_obj->usrname($new_value);
1088 } elsif($self->api_name =~ /email/o) {
1089 #warn "Updating email to $new_value\n";
1090 $user_obj->email($new_value);
1093 my $session = $apputils->start_db_session();
1095 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1096 return $evt if $evt;
1098 $apputils->commit_db_session($session);
1100 if($user_obj) { return 1; }
1105 __PACKAGE__->register_method(
1106 method => "check_user_perms",
1107 api_name => "open-ils.actor.user.perm.check",
1108 notes => <<" NOTES");
1109 Takes a login session, user id, an org id, and an array of perm type strings. For each
1110 perm type, if the user does *not* have the given permission it is added
1111 to a list which is returned from the method. If all permissions
1112 are allowed, an empty list is returned
1113 if the logged in user does not match 'user_id', then the logged in user must
1114 have VIEW_PERMISSION priveleges.
1117 sub check_user_perms {
1118 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1120 my( $staff, $evt ) = $apputils->checkses($login_session);
1121 return $evt if $evt;
1123 if($staff->id ne $user_id) {
1124 if( $evt = $apputils->check_perms(
1125 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1131 for my $perm (@$perm_types) {
1132 if($apputils->check_perms($user_id, $org_id, $perm)) {
1133 push @not_allowed, $perm;
1137 return \@not_allowed
1140 __PACKAGE__->register_method(
1141 method => "check_user_perms2",
1142 api_name => "open-ils.actor.user.perm.check.multi_org",
1144 Checks the permissions on a list of perms and orgs for a user
1145 @param authtoken The login session key
1146 @param user_id The id of the user to check
1147 @param orgs The array of org ids
1148 @param perms The array of permission names
1149 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1150 if the logged in user does not match 'user_id', then the logged in user must
1151 have VIEW_PERMISSION priveleges.
1154 sub check_user_perms2 {
1155 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1157 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1158 $authtoken, $user_id, 'VIEW_PERMISSION' );
1159 return $evt if $evt;
1162 for my $org (@$orgs) {
1163 for my $perm (@$perms) {
1164 if($apputils->check_perms($user_id, $org, $perm)) {
1165 push @not_allowed, [ $org, $perm ];
1170 return \@not_allowed
1174 __PACKAGE__->register_method(
1175 method => 'check_user_perms3',
1176 api_name => 'open-ils.actor.user.perm.highest_org',
1178 Returns the highest org unit id at which a user has a given permission
1179 If the requestor does not match the target user, the requestor must have
1180 'VIEW_PERMISSION' rights at the home org unit of the target user
1181 @param authtoken The login session key
1182 @param userid The id of the user in question
1183 @param perm The permission to check
1184 @return The org unit highest in the org tree within which the user has
1185 the requested permission
1188 sub check_user_perms3 {
1189 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1191 my( $staff, $target, $org, $evt );
1193 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1194 $authtoken, $userid, 'VIEW_PERMISSION' );
1195 return $evt if $evt;
1197 my $tree = $self->get_org_tree();
1198 return _find_highest_perm_org( $perm, $userid, $target->ws_ou, $tree );
1202 sub _find_highest_perm_org {
1203 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1204 my $org = $apputils->find_org($org_tree, $start_org );
1208 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1210 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1216 __PACKAGE__->register_method(
1217 method => 'check_user_perms4',
1218 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1220 Returns the highest org unit id at which a user has a given permission
1221 If the requestor does not match the target user, the requestor must have
1222 'VIEW_PERMISSION' rights at the home org unit of the target user
1223 @param authtoken The login session key
1224 @param userid The id of the user in question
1225 @param perms An array of perm names to check
1226 @return An array of orgId's representing the org unit
1227 highest in the org tree within which the user has the requested permission
1228 The arrah of orgId's has matches the order of the perms array
1231 sub check_user_perms4 {
1232 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1234 my( $staff, $target, $org, $evt );
1236 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1237 $authtoken, $userid, 'VIEW_PERMISSION' );
1238 return $evt if $evt;
1241 return [] unless ref($perms);
1242 my $tree = $self->get_org_tree();
1244 for my $p (@$perms) {
1245 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1253 __PACKAGE__->register_method(
1254 method => "user_fines_summary",
1255 api_name => "open-ils.actor.user.fines.summary",
1256 notes => <<" NOTES");
1257 Returns a short summary of the users total open fines, excluding voided fines
1258 Params are login_session, user_id
1259 Returns a 'mous' object.
1262 sub user_fines_summary {
1263 my( $self, $client, $login_session, $user_id ) = @_;
1265 my $user_obj = $apputils->check_user_session($login_session);
1266 if($user_obj->id ne $user_id) {
1267 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1268 return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY");
1272 return $apputils->simple_scalar_request(
1274 "open-ils.cstore.direct.money.open_user_summary.search",
1275 { usr => $user_id } );
1282 __PACKAGE__->register_method(
1283 method => "user_transactions",
1284 api_name => "open-ils.actor.user.transactions",
1285 notes => <<" NOTES");
1286 Returns a list of open user transactions (mbts objects);
1287 Params are login_session, user_id
1288 Optional third parameter is the transactions type. defaults to all
1291 __PACKAGE__->register_method(
1292 method => "user_transactions",
1293 api_name => "open-ils.actor.user.transactions.have_charge",
1294 notes => <<" NOTES");
1295 Returns a list of all open user transactions (mbts objects) that have an initial charge
1296 Params are login_session, user_id
1297 Optional third parameter is the transactions type. defaults to all
1300 __PACKAGE__->register_method(
1301 method => "user_transactions",
1302 api_name => "open-ils.actor.user.transactions.have_balance",
1303 notes => <<" NOTES");
1304 Returns a list of all open user transactions (mbts objects) that have a balance
1305 Params are login_session, user_id
1306 Optional third parameter is the transactions type. defaults to all
1309 __PACKAGE__->register_method(
1310 method => "user_transactions",
1311 api_name => "open-ils.actor.user.transactions.fleshed",
1312 notes => <<" NOTES");
1313 Returns an object/hash of transaction, circ, title where transaction = an open
1314 user transactions (mbts objects), circ is the attached circluation, and title
1315 is the title the circ points to
1316 Params are login_session, user_id
1317 Optional third parameter is the transactions type. defaults to all
1320 __PACKAGE__->register_method(
1321 method => "user_transactions",
1322 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1323 notes => <<" NOTES");
1324 Returns an object/hash of transaction, circ, title where transaction = an open
1325 user transactions that has an initial charge (mbts objects), circ is the
1326 attached circluation, and title is the title the circ points to
1327 Params are login_session, user_id
1328 Optional third parameter is the transactions type. defaults to all
1331 __PACKAGE__->register_method(
1332 method => "user_transactions",
1333 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1334 notes => <<" NOTES");
1335 Returns an object/hash of transaction, circ, title where transaction = an open
1336 user transaction that has a balance (mbts objects), circ is the attached
1337 circluation, and title is the title the circ points to
1338 Params are login_session, user_id
1339 Optional third parameter is the transaction type. defaults to all
1342 __PACKAGE__->register_method(
1343 method => "user_transactions",
1344 api_name => "open-ils.actor.user.transactions.count",
1345 notes => <<" NOTES");
1346 Returns an object/hash of transaction, circ, title where transaction = an open
1347 user transactions (mbts objects), circ is the attached circluation, and title
1348 is the title the circ points to
1349 Params are login_session, user_id
1350 Optional third parameter is the transactions type. defaults to all
1353 __PACKAGE__->register_method(
1354 method => "user_transactions",
1355 api_name => "open-ils.actor.user.transactions.have_charge.count",
1356 notes => <<" NOTES");
1357 Returns an object/hash of transaction, circ, title where transaction = an open
1358 user transactions that has an initial charge (mbts objects), circ is the
1359 attached circluation, and title is the title the circ points to
1360 Params are login_session, user_id
1361 Optional third parameter is the transactions type. defaults to all
1364 __PACKAGE__->register_method(
1365 method => "user_transactions",
1366 api_name => "open-ils.actor.user.transactions.have_balance.count",
1367 notes => <<" NOTES");
1368 Returns an object/hash of transaction, circ, title where transaction = an open
1369 user transaction that has a balance (mbts objects), circ is the attached
1370 circluation, and title is the title the circ points to
1371 Params are login_session, user_id
1372 Optional third parameter is the transaction type. defaults to all
1375 __PACKAGE__->register_method(
1376 method => "user_transactions",
1377 api_name => "open-ils.actor.user.transactions.have_balance.total",
1378 notes => <<" NOTES");
1379 Returns an object/hash of transaction, circ, title where transaction = an open
1380 user transaction that has a balance (mbts objects), circ is the attached
1381 circluation, and title is the title the circ points to
1382 Params are login_session, user_id
1383 Optional third parameter is the transaction type. defaults to all
1388 sub user_transactions {
1389 my( $self, $client, $login_session, $user_id, $type ) = @_;
1391 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1392 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1393 return $evt if $evt;
1395 my $api = $self->api_name();
1399 if(defined($type)) { @xact = (xact_type => $type);
1401 } else { @xact = (); }
1404 ->method_lookup('open-ils.actor.user.transactions.history.still_open')
1405 ->run($login_session => $user_id => $type);
1407 if($api =~ /have_charge/o) {
1409 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1411 } elsif($api =~ /have_balance/o) {
1413 $trans = [ grep { int($_->balance_owed * 100) != 0 } @$trans ];
1416 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1420 if($api =~ /total/o) {
1422 for my $t (@$trans) {
1423 $total += $t->balance_owed;
1426 $logger->debug("Total balance owed by user $user_id: $total");
1430 if($api =~ /count/o) { return scalar @$trans; }
1431 if($api !~ /fleshed/o) { return $trans; }
1434 for my $t (@$trans) {
1436 if( $t->xact_type ne 'circulation' ) {
1437 push @resp, {transaction => $t};
1441 my $circ = $apputils->simple_scalar_request(
1443 "open-ils.cstore.direct.action.circulation.retrieve",
1448 my $title = $apputils->simple_scalar_request(
1450 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1451 $circ->target_copy );
1455 my $u = OpenILS::Utils::ModsParser->new();
1456 $u->start_mods_batch($title->marc());
1457 my $mods = $u->finish_mods_batch();
1458 $mods->doc_id($title->id) if $mods;
1460 push @resp, {transaction => $t, circ => $circ, record => $mods };
1468 __PACKAGE__->register_method(
1469 method => "user_transaction_retrieve",
1470 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1472 notes => <<" NOTES");
1473 Returns a fleshedtransaction record
1475 __PACKAGE__->register_method(
1476 method => "user_transaction_retrieve",
1477 api_name => "open-ils.actor.user.transaction.retrieve",
1479 notes => <<" NOTES");
1480 Returns a transaction record
1482 sub user_transaction_retrieve {
1483 my( $self, $client, $login_session, $bill_id ) = @_;
1485 my $trans = $apputils->simple_scalar_request(
1487 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1491 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1492 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1493 return $evt if $evt;
1495 my $api = $self->api_name();
1496 if($api !~ /fleshed/o) { return $trans; }
1498 if( $trans->xact_type ne 'circulation' ) {
1499 $logger->debug("Returning non-circ transaction");
1500 return {transaction => $trans};
1503 my $circ = $apputils->simple_scalar_request(
1505 "open-ils..direct.action.circulation.retrieve",
1508 return {transaction => $trans} unless $circ;
1509 $logger->debug("Found the circ transaction");
1511 my $title = $apputils->simple_scalar_request(
1513 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1514 $circ->target_copy );
1516 return {transaction => $trans, circ => $circ } unless $title;
1517 $logger->debug("Found the circ title");
1521 my $u = OpenILS::Utils::ModsParser->new();
1522 $u->start_mods_batch($title->marc());
1523 $mods = $u->finish_mods_batch();
1525 if ($title->id == OILS_PRECAT_RECORD) {
1526 my $copy = $apputils->simple_scalar_request(
1528 "open-ils.cstore.direct.asset.copy.retrieve",
1529 $circ->target_copy );
1531 $mods = new Fieldmapper::metabib::virtual_record;
1532 $mods->doc_id(OILS_PRECAT_RECORD);
1533 $mods->title($copy->dummy_title);
1534 $mods->author($copy->dummy_author);
1538 $logger->debug("MODSized the circ title");
1540 return {transaction => $trans, circ => $circ, record => $mods };
1544 __PACKAGE__->register_method(
1545 method => "hold_request_count",
1546 api_name => "open-ils.actor.user.hold_requests.count",
1548 notes => <<" NOTES");
1549 Returns hold ready/total counts
1551 sub hold_request_count {
1552 my( $self, $client, $login_session, $userid ) = @_;
1554 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1555 $login_session, $userid, 'VIEW_HOLD' );
1556 return $evt if $evt;
1559 my $holds = $apputils->simple_scalar_request(
1561 "open-ils.cstore.direct.action.hold_request.search.atomic",
1564 fulfillment_time => {"=" => undef },
1565 cancel_time => undef,
1570 for my $h (@$holds) {
1571 next unless $h->capture_time and $h->current_copy;
1573 my $copy = $apputils->simple_scalar_request(
1575 "open-ils.cstore.direct.asset.copy.retrieve",
1579 if ($copy and $copy->status == 8) {
1584 return { total => scalar(@$holds), ready => scalar(@ready) };
1588 __PACKAGE__->register_method(
1589 method => "checkedout_count",
1590 api_name => "open-ils.actor.user.checked_out.count__",
1592 notes => <<" NOTES");
1593 Returns a transaction record
1597 sub checkedout_count {
1598 my( $self, $client, $login_session, $userid ) = @_;
1600 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1601 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1602 return $evt if $evt;
1604 my $circs = $apputils->simple_scalar_request(
1606 "open-ils.cstore.direct.action.circulation.search.atomic",
1607 { usr => $userid, stop_fines => undef }
1608 #{ usr => $userid, checkin_time => {"=" => undef } }
1611 my $parser = DateTime::Format::ISO8601->new;
1614 for my $c (@$circs) {
1615 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1616 my $due = $due_dt->epoch;
1618 if ($due < DateTime->today->epoch) {
1623 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1627 __PACKAGE__->register_method(
1628 method => "checked_out",
1629 api_name => "open-ils.actor.user.checked_out",
1632 Returns a structure of circulations objects sorted by
1633 out, overdue, lost, claims_returned, long_overdue.
1634 A list of IDs are returned of each type.
1635 lost, long_overdue, and claims_returned circ will not
1636 be "finished" (there is an outstanding balance or some
1637 other pending action on the circ).
1639 The .count method also includes a 'total' field which
1640 sums all "open" circs
1644 __PACKAGE__->register_method(
1645 method => "checked_out",
1646 api_name => "open-ils.actor.user.checked_out.count",
1648 signature => q/@see open-ils.actor.user.checked_out/
1652 my( $self, $conn, $auth, $userid ) = @_;
1654 my $e = new_editor(authtoken=>$auth);
1655 return $e->event unless $e->checkauth;
1657 if( $userid ne $e->requestor->id ) {
1658 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1661 my $count = $self->api_name =~ /count/;
1662 return _checked_out( $count, $e, $userid );
1666 my( $iscount, $e, $userid ) = @_;
1669 my $meth = 'open-ils.storage.actor.user.checked_out';
1670 $meth = "$meth.count" if $iscount;
1671 return $U->storagereq($meth, $userid);
1673 # XXX Old code - moved to storage
1674 #------------------------------------------------------------------------------
1675 #------------------------------------------------------------------------------
1676 my $circs = $e->search_action_circulation(
1677 { usr => $userid, checkin_time => undef });
1679 my $parser = DateTime::Format::ISO8601->new;
1681 # split the circs up into overdue and not-overdue circs
1683 for my $c (@$circs) {
1684 if( $c->due_date ) {
1685 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1686 my $due = $due_dt->epoch;
1687 if ($due < DateTime->today->epoch) {
1697 my( @open, @od, @lost, @cr, @lo );
1699 while (my $c = shift(@out)) {
1700 push( @open, $c->id ) if (!$c->stop_fines || $c->stop_fines eq 'MAXFINES' || $c->stop_fines eq 'RENEW');
1701 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1702 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1703 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1706 while (my $c = shift(@overdue)) {
1707 push( @od, $c->id ) if (!$c->stop_fines || $c->stop_fines eq 'MAXFINES' || $c->stop_fines eq 'RENEW');
1708 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1709 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1710 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1715 total => @open + @od + @lost + @cr + @lo,
1716 out => scalar(@open),
1717 overdue => scalar(@od),
1718 lost => scalar(@lost),
1719 claims_returned => scalar(@cr),
1720 long_overdue => scalar(@lo)
1728 claims_returned => \@cr,
1729 long_overdue => \@lo
1734 sub _checked_out_WHAT {
1735 my( $iscount, $e, $userid ) = @_;
1737 my $circs = $e->search_action_circulation(
1738 { usr => $userid, stop_fines => undef });
1740 my $mcircs = $e->search_action_circulation(
1743 checkin_time => undef,
1744 xact_finish => undef,
1748 push( @$circs, @$mcircs );
1750 my $parser = DateTime::Format::ISO8601->new;
1752 # split the circs up into overdue and not-overdue circs
1754 for my $c (@$circs) {
1755 if( $c->due_date ) {
1756 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1757 my $due = $due_dt->epoch;
1758 if ($due < DateTime->today->epoch) {
1759 push @overdue, $c->id;
1768 # grab all of the lost, claims-returned, and longoverdue circs
1769 #my $open = $e->search_action_circulation(
1770 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1773 # these items have stop_fines, but no xact_finish, so money
1774 # is owed on them and they have not been checked in
1775 my $open = $e->search_action_circulation(
1778 stop_fines => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] },
1779 xact_finish => undef,
1780 checkin_time => undef,
1785 my( @lost, @cr, @lo );
1786 for my $c (@$open) {
1787 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1788 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1789 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1795 total => @$circs + @lost + @cr + @lo,
1796 out => scalar(@out),
1797 overdue => scalar(@overdue),
1798 lost => scalar(@lost),
1799 claims_returned => scalar(@cr),
1800 long_overdue => scalar(@lo)
1806 overdue => \@overdue,
1808 claims_returned => \@cr,
1809 long_overdue => \@lo
1815 __PACKAGE__->register_method(
1816 method => "checked_in_with_fines",
1817 api_name => "open-ils.actor.user.checked_in_with_fines",
1819 signature => q/@see open-ils.actor.user.checked_out/
1821 sub checked_in_with_fines {
1822 my( $self, $conn, $auth, $userid ) = @_;
1824 my $e = new_editor(authtoken=>$auth);
1825 return $e->event unless $e->checkauth;
1827 if( $userid ne $e->requestor->id ) {
1828 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1831 # money is owed on these items and they are checked in
1832 my $open = $e->search_action_circulation(
1835 xact_finish => undef,
1836 checkin_time => { "!=" => undef },
1841 my( @lost, @cr, @lo );
1842 for my $c (@$open) {
1843 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1844 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1845 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1850 claims_returned => \@cr,
1851 long_overdue => \@lo
1863 __PACKAGE__->register_method(
1864 method => "user_transaction_history",
1865 api_name => "open-ils.actor.user.transactions.history",
1867 notes => <<" NOTES");
1868 Returns a list of billable transaction ids for a user, optionally by type
1870 __PACKAGE__->register_method(
1871 method => "user_transaction_history",
1872 api_name => "open-ils.actor.user.transactions.history.have_charge",
1874 notes => <<" NOTES");
1875 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1877 __PACKAGE__->register_method(
1878 method => "user_transaction_history",
1879 api_name => "open-ils.actor.user.transactions.history.have_balance",
1881 notes => <<" NOTES");
1882 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1884 __PACKAGE__->register_method(
1885 method => "user_transaction_history",
1886 api_name => "open-ils.actor.user.transactions.history.still_open",
1888 notes => <<" NOTES");
1889 Returns a list of billable transaction ids for a user that are not finished
1891 __PACKAGE__->register_method(
1892 method => "user_transaction_history",
1893 api_name => "open-ils.actor.user.transactions.history.have_bill",
1895 notes => <<" NOTES");
1896 Returns a list of billable transaction ids for a user that has billings
1902 sub _user_transaction_history {
1903 my( $self, $client, $login_session, $user_id, $type ) = @_;
1905 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1906 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1907 return $evt if $evt;
1909 my $api = $self->api_name();
1914 @xact = (xact_type => $type) if(defined($type));
1915 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1916 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1918 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1920 my $trans = $apputils->simple_scalar_request(
1922 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1923 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1925 return [ map { $_->id } @$trans ];
1933 for my $x (@xacts) {
1934 my $s = new Fieldmapper::money::billable_transaction_summary;
1937 $s->xact_start( $x->xact_start );
1938 $s->xact_finish( $x->xact_finish );
1942 for my $b (@{ $x->billings }) {
1943 next if ($U->is_true($b->voided));
1944 $to += ($b->amount * 100);
1945 $lb ||= $b->billing_ts;
1946 if ($b->billing_ts ge $lb) {
1947 $lb = $b->billing_ts;
1948 $s->last_billing_note($b->note);
1949 $s->last_billing_ts($b->billing_ts);
1950 $s->last_billing_type($b->billing_type);
1954 $s->total_owed( sprintf('%0.2f', $to / 100 ) );
1958 for my $p (@{ $x->payments }) {
1959 next if ($U->is_true($p->voided));
1960 $tp += ($p->amount * 100);
1961 $lp ||= $p->payment_ts;
1962 if ($p->payment_ts ge $lp) {
1963 $lp = $p->payment_ts;
1964 $s->last_payment_note($p->note);
1965 $s->last_payment_ts($p->payment_ts);
1966 $s->last_payment_type($p->payment_type);
1969 $s->total_paid( sprintf('%0.2f', $tp / 100 ) );
1971 $s->balance_owed( sprintf('%0.2f', ($to - $tp) / 100) );
1973 $s->xact_type( 'grocery' ) if ($x->grocery);
1974 $s->xact_type( 'circulation' ) if ($x->circulation);
1982 sub user_transaction_history {
1983 my( $self, $conn, $auth, $userid, $type ) = @_;
1984 my $e = new_editor(authtoken=>$auth);
1985 return $e->event unless $e->checkauth;
1986 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1988 my $api = $self->api_name;
1989 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
1991 my @xacts = @{ $e->search_money_billable_transaction(
1992 [ { usr => $userid, @xact_finish },
1994 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
1995 order_by => { mbt => 'xact_start DESC' },
2000 my @mbts = _make_mbts( @xacts );
2002 if(defined($type)) {
2003 @mbts = grep { $_->xact_type eq $type } @mbts;
2006 if($api =~ /have_balance/o) {
2007 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
2010 if($api =~ /have_charge/o) {
2011 @mbts = grep { defined($_->last_billing_ts) } @mbts;
2014 if($api =~ /have_bill/o) {
2015 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
2023 __PACKAGE__->register_method(
2024 method => "user_perms",
2025 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2027 notes => <<" NOTES");
2028 Returns a list of permissions
2031 my( $self, $client, $authtoken, $user ) = @_;
2033 my( $staff, $evt ) = $apputils->checkses($authtoken);
2034 return $evt if $evt;
2036 $user ||= $staff->id;
2038 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2042 return $apputils->simple_scalar_request(
2044 "open-ils.storage.permission.user_perms.atomic",
2048 __PACKAGE__->register_method(
2049 method => "retrieve_perms",
2050 api_name => "open-ils.actor.permissions.retrieve",
2051 notes => <<" NOTES");
2052 Returns a list of permissions
2054 sub retrieve_perms {
2055 my( $self, $client ) = @_;
2056 return $apputils->simple_scalar_request(
2058 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2059 { id => { '!=' => undef } }
2063 __PACKAGE__->register_method(
2064 method => "retrieve_groups",
2065 api_name => "open-ils.actor.groups.retrieve",
2066 notes => <<" NOTES");
2067 Returns a list of user groupss
2069 sub retrieve_groups {
2070 my( $self, $client ) = @_;
2071 return new_editor()->retrieve_all_permission_grp_tree();
2074 __PACKAGE__->register_method(
2075 method => "retrieve_org_address",
2076 api_name => "open-ils.actor.org_unit.address.retrieve",
2077 notes => <<' NOTES');
2078 Returns an org_unit address by ID
2079 @param An org_address ID
2081 sub retrieve_org_address {
2082 my( $self, $client, $id ) = @_;
2083 return $apputils->simple_scalar_request(
2085 "open-ils.cstore.direct.actor.org_address.retrieve",
2090 __PACKAGE__->register_method(
2091 method => "retrieve_groups_tree",
2092 api_name => "open-ils.actor.groups.tree.retrieve",
2093 notes => <<" NOTES");
2094 Returns a list of user groups
2096 sub retrieve_groups_tree {
2097 my( $self, $client ) = @_;
2098 return new_editor()->search_permission_grp_tree(
2103 flesh_fields => { pgt => ["children"] },
2104 order_by => { pgt => 'name'}
2111 # turns an org list into an org tree
2113 sub build_group_tree {
2115 my( $self, $grplist) = @_;
2117 return $grplist unless (
2118 ref($grplist) and @$grplist > 1 );
2120 my @list = sort { $a->name cmp $b->name } @$grplist;
2123 for my $grp (@list) {
2125 if ($grp and !defined($grp->parent)) {
2129 my ($parent) = grep { $_->id == $grp->parent} @list;
2131 $parent->children([]) unless defined($parent->children);
2132 push( @{$parent->children}, $grp );
2140 __PACKAGE__->register_method(
2141 method => "add_user_to_groups",
2142 api_name => "open-ils.actor.user.set_groups",
2143 notes => <<" NOTES");
2144 Adds a user to one or more permission groups
2147 sub add_user_to_groups {
2148 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2150 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2151 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2152 return $evt if $evt;
2154 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2155 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2156 return $evt if $evt;
2158 $apputils->simplereq(
2160 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2162 for my $group (@$groups) {
2163 my $link = Fieldmapper::permission::usr_grp_map->new;
2165 $link->usr($userid);
2167 my $id = $apputils->simplereq(
2169 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2175 __PACKAGE__->register_method(
2176 method => "get_user_perm_groups",
2177 api_name => "open-ils.actor.user.get_groups",
2178 notes => <<" NOTES");
2179 Retrieve a user's permission groups.
2183 sub get_user_perm_groups {
2184 my( $self, $client, $authtoken, $userid ) = @_;
2186 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2187 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2188 return $evt if $evt;
2190 return $apputils->simplereq(
2192 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2197 __PACKAGE__->register_method (
2198 method => 'register_workstation',
2199 api_name => 'open-ils.actor.workstation.register.override',
2200 signature => q/@see open-ils.actor.workstation.register/);
2202 __PACKAGE__->register_method (
2203 method => 'register_workstation',
2204 api_name => 'open-ils.actor.workstation.register',
2206 Registers a new workstion in the system
2207 @param authtoken The login session key
2208 @param name The name of the workstation id
2209 @param owner The org unit that owns this workstation
2210 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2211 if the name is already in use.
2214 sub _register_workstation {
2215 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2216 my( $requestor, $evt ) = $U->checkses($authtoken);
2217 return $evt if $evt;
2218 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2219 return $evt if $evt;
2221 my $ws = $U->cstorereq(
2222 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2223 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2225 $ws = Fieldmapper::actor::workstation->new;
2226 $ws->owning_lib($owner);
2229 my $id = $U->storagereq(
2230 'open-ils.storage.direct.actor.workstation.create', $ws );
2231 return $U->DB_UPDATE_FAILED($ws) unless $id;
2237 sub register_workstation {
2238 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2240 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2241 return $e->event unless $e->checkauth;
2242 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2243 my $existing = $e->search_actor_workstation({name => $name});
2246 if( $self->api_name =~ /override/o ) {
2247 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2248 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2250 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2254 my $ws = Fieldmapper::actor::workstation->new;
2255 $ws->owning_lib($owner);
2257 $e->create_actor_workstation($ws) or return $e->event;
2259 return $ws->id; # note: editor sets the id on the new object for us
2263 __PACKAGE__->register_method (
2264 method => 'fetch_patron_note',
2265 api_name => 'open-ils.actor.note.retrieve.all',
2267 Returns a list of notes for a given user
2268 Requestor must have VIEW_USER permission if pub==false and
2269 @param authtoken The login session key
2270 @param args Hash of params including
2271 patronid : the patron's id
2272 pub : true if retrieving only public notes
2276 sub fetch_patron_note {
2277 my( $self, $conn, $authtoken, $args ) = @_;
2278 my $patronid = $$args{patronid};
2280 my($reqr, $evt) = $U->checkses($authtoken);
2281 return $evt if $evt;
2284 ($patron, $evt) = $U->fetch_user($patronid);
2285 return $evt if $evt;
2288 if( $patronid ne $reqr->id ) {
2289 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2290 return $evt if $evt;
2292 return $U->cstorereq(
2293 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2294 { usr => $patronid, pub => 't' } );
2297 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2298 return $evt if $evt;
2300 return $U->cstorereq(
2301 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2304 __PACKAGE__->register_method (
2305 method => 'create_user_note',
2306 api_name => 'open-ils.actor.note.create',
2308 Creates a new note for the given user
2309 @param authtoken The login session key
2310 @param note The note object
2313 sub create_user_note {
2314 my( $self, $conn, $authtoken, $note ) = @_;
2315 my( $reqr, $patron, $evt ) =
2316 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2317 return $evt if $evt;
2318 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2320 $note->creator($reqr->id);
2321 my $id = $U->storagereq(
2322 'open-ils.storage.direct.actor.usr_note.create', $note );
2323 return $U->DB_UPDATE_FAILED($note) unless $id;
2328 __PACKAGE__->register_method (
2329 method => 'delete_user_note',
2330 api_name => 'open-ils.actor.note.delete',
2332 Deletes a note for the given user
2333 @param authtoken The login session key
2334 @param noteid The note id
2337 sub delete_user_note {
2338 my( $self, $conn, $authtoken, $noteid ) = @_;
2340 my $note = $U->cstorereq(
2341 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2342 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2344 my( $reqr, $patron, $evt ) =
2345 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2346 return $evt if $evt;
2347 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2349 my $stat = $U->storagereq(
2350 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2351 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2356 __PACKAGE__->register_method (
2357 method => 'update_user_note',
2358 api_name => 'open-ils.actor.note.update',
2360 @param authtoken The login session key
2361 @param note The note
2365 sub update_user_note {
2366 my( $self, $conn, $auth, $note ) = @_;
2367 my $e = new_editor(authtoken=>$auth, xact=>1);
2368 return $e->event unless $e->checkauth;
2369 my $patron = $e->retrieve_actor_user($note->usr)
2370 or return $e->event;
2371 return $e->event unless
2372 $e->allowed('UPDATE_USER', $patron->home_ou);
2373 $e->update_actor_user_note($note)
2374 or return $e->event;
2382 __PACKAGE__->register_method (
2383 method => 'create_closed_date',
2384 api_name => 'open-ils.actor.org_unit.closed_date.create',
2386 Creates a new closing entry for the given org_unit
2387 @param authtoken The login session key
2388 @param note The closed_date object
2391 sub create_closed_date {
2392 my( $self, $conn, $authtoken, $cd ) = @_;
2394 my( $user, $evt ) = $U->checkses($authtoken);
2395 return $evt if $evt;
2397 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2398 return $evt if $evt;
2400 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2402 my $id = $U->storagereq(
2403 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2404 return $U->DB_UPDATE_FAILED($cd) unless $id;
2409 __PACKAGE__->register_method (
2410 method => 'delete_closed_date',
2411 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2413 Deletes a closing entry for the given org_unit
2414 @param authtoken The login session key
2415 @param noteid The close_date id
2418 sub delete_closed_date {
2419 my( $self, $conn, $authtoken, $cd ) = @_;
2421 my( $user, $evt ) = $U->checkses($authtoken);
2422 return $evt if $evt;
2425 ($cd_obj, $evt) = fetch_closed_date($cd);
2426 return $evt if $evt;
2428 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2429 return $evt if $evt;
2431 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2433 my $stat = $U->storagereq(
2434 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2435 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2440 __PACKAGE__->register_method(
2441 method => 'usrname_exists',
2442 api_name => 'open-ils.actor.username.exists',
2444 Returns 1 if the requested username exists, returns 0 otherwise
2448 sub usrname_exists {
2449 my( $self, $conn, $auth, $usrname ) = @_;
2450 my $e = new_editor(authtoken=>$auth);
2451 return $e->event unless $e->checkauth;
2452 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2453 return $$a[0] if $a and @$a;
2457 __PACKAGE__->register_method(
2458 method => 'barcode_exists',
2459 api_name => 'open-ils.actor.barcode.exists',
2461 Returns 1 if the requested barcode exists, returns 0 otherwise
2465 sub barcode_exists {
2466 my( $self, $conn, $auth, $barcode ) = @_;
2467 my $e = new_editor(authtoken=>$auth);
2468 return $e->event unless $e->checkauth;
2469 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2470 return $$a[0] if $a and @$a;
2475 __PACKAGE__->register_method(
2476 method => 'retrieve_net_levels',
2477 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2480 sub retrieve_net_levels {
2481 my( $self, $conn, $auth ) = @_;
2482 my $e = new_editor(authtoken=>$auth);
2483 return $e->event unless $e->checkauth;
2484 return $e->retrieve_all_config_net_access_level();
2488 __PACKAGE__->register_method(
2489 method => 'fetch_org_by_shortname',
2490 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2492 sub fetch_org_by_shortname {
2493 my( $self, $conn, $sname ) = @_;
2494 my $e = new_editor();
2495 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2496 return $e->event unless $org;
2501 __PACKAGE__->register_method(
2502 method => 'session_home_lib',
2503 api_name => 'open-ils.actor.session.home_lib',
2506 sub session_home_lib {
2507 my( $self, $conn, $auth ) = @_;
2508 my $e = new_editor(authtoken=>$auth);
2509 return undef unless $e->checkauth;
2510 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2511 return $org->shortname;
2516 __PACKAGE__->register_method(
2517 method => 'slim_tree',
2518 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2521 my $tree = new_editor()->search_actor_org_unit(
2523 {"parent_ou" => undef },
2526 flesh_fields => { aou => ['children'] },
2527 order_by => { aou => 'name'},
2528 select => { aou => ["id","shortname", "name"]},
2533 return trim_tree($tree);
2539 return undef unless $tree;
2541 code => $tree->shortname,
2542 name => $tree->name,
2544 if( $tree->children and @{$tree->children} ) {
2545 $htree->{children} = [];
2546 for my $c (@{$tree->children}) {
2547 push( @{$htree->{children}}, trim_tree($c) );
2556 __PACKAGE__->register_method(
2557 method => "user_retrieve_fleshed_by_id",
2558 api_name => "open-ils.actor.user.fleshed.retrieve",);
2560 sub user_retrieve_fleshed_by_id {
2561 my( $self, $client, $auth, $user_id, $fields ) = @_;
2562 my $e = new_editor(authtoken => $auth);
2563 return $e->event unless $e->checkauth;
2564 if( $e->requestor->id != $user_id ) {
2565 return $e->event unless $e->allowed('VIEW_USER');
2570 "standing_penalties",
2574 "stat_cat_entries" ];
2575 return new_flesh_user($user_id, $fields, $e);
2579 sub new_flesh_user {
2582 my $fields = shift || [];
2583 my $e = shift || new_editor(xact=>1);
2585 my $user = $e->retrieve_actor_user(
2590 "flesh_fields" => { "au" => $fields }
2593 ) or return $e->event;
2596 if( grep { $_ eq 'addresses' } @$fields ) {
2598 $user->addresses([]) unless @{$user->addresses};
2600 if( ref $user->billing_address ) {
2601 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2602 push( @{$user->addresses}, $user->billing_address );
2606 if( ref $user->mailing_address ) {
2607 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2608 push( @{$user->addresses}, $user->mailing_address );
2614 $user->clear_passwd();
2621 __PACKAGE__->register_method(
2622 method => "user_retrieve_parts",
2623 api_name => "open-ils.actor.user.retrieve.parts",);
2625 sub user_retrieve_parts {
2626 my( $self, $client, $auth, $user_id, $fields ) = @_;
2627 my $e = new_editor(authtoken => $auth);
2628 return $e->event unless $e->checkauth;
2629 if( $e->requestor->id != $user_id ) {
2630 return $e->event unless $e->allowed('VIEW_USER');
2633 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2634 push(@resp, $user->$_()) for(@$fields);