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);
352 # If we are the requestor (trying to update our own account)
353 # and we are not trying to change our profile, we're good
354 if( $p->id == $requestor->id and
355 $p->profile == $patron->profile ) {
360 $evt = group_perm_failed($session, $requestor, $p);
364 # They are allowed to edit this patron.. can they put the
365 # patron into the group requested?
366 $evt = group_perm_failed($session, $requestor, $patron);
372 sub group_perm_failed {
373 my( $session, $requestor, $patron ) = @_;
377 my $grpid = $patron->profile;
381 $logger->debug("user update looking for group perm for group $grpid");
382 $grp = $session->request(
383 'open-ils.storage.direct.permission.grp_tree.retrieve', $grpid )->gather(1);
384 return OpenILS::Event->new('PERMISSION_GRP_TREE_NOT_FOUND') unless $grp;
386 } while( !($perm = $grp->application_perm) and ($grpid = $grp->parent) );
388 $logger->info("user update checking perm $perm on user ".
389 $requestor->id." for update/create on user username=".$patron->usrname);
391 my $evt = $U->check_perms($requestor->id, $patron->home_ou, $perm);
399 my( $session, $patron, $user_obj, $noperm) = @_;
401 $logger->info("Updating patron ".$patron->id." in DB");
406 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
407 return (undef, $evt) if $evt;
410 # update the password by itself to avoid the password protection magic
411 if( $patron->passwd ) {
412 my $s = $session->request(
413 'open-ils.storage.direct.actor.user.remote_update',
414 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
415 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
416 $patron->clear_passwd;
419 if(!$patron->ident_type) {
420 $patron->clear_ident_type;
421 $patron->clear_ident_value;
424 my $stat = $session->request(
425 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
426 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
431 sub _check_dup_ident {
432 my( $session, $patron ) = @_;
434 return undef unless $patron->ident_value;
437 ident_type => $patron->ident_type,
438 ident_value => $patron->ident_value,
441 $logger->debug("patron update searching for dup ident values: " .
442 $patron->ident_type . ':' . $patron->ident_value);
444 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
446 my $dups = $session->request(
447 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
450 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
457 sub _add_update_addresses {
461 my $new_patron = shift;
465 my $current_id; # id of the address before creation
467 for my $address (@{$patron->addresses()}) {
469 next unless ref $address;
470 $current_id = $address->id();
472 if( $patron->billing_address() and
473 $patron->billing_address() == $current_id ) {
474 $logger->info("setting billing addr to $current_id");
475 $new_patron->billing_address($address->id());
476 $new_patron->ischanged(1);
479 if( $patron->mailing_address() and
480 $patron->mailing_address() == $current_id ) {
481 $new_patron->mailing_address($address->id());
482 $logger->info("setting mailing addr to $current_id");
483 $new_patron->ischanged(1);
487 if($address->isnew()) {
489 $address->usr($new_patron->id());
491 ($address, $evt) = _add_address($session,$address);
492 return (undef, $evt) if $evt;
494 # we need to get the new id
495 if( $patron->billing_address() and
496 $patron->billing_address() == $current_id ) {
497 $new_patron->billing_address($address->id());
498 $logger->info("setting billing addr to $current_id");
499 $new_patron->ischanged(1);
502 if( $patron->mailing_address() and
503 $patron->mailing_address() == $current_id ) {
504 $new_patron->mailing_address($address->id());
505 $logger->info("setting mailing addr to $current_id");
506 $new_patron->ischanged(1);
509 } elsif($address->ischanged() ) {
511 ($address, $evt) = _update_address($session, $address);
512 return (undef, $evt) if $evt;
514 } elsif($address->isdeleted() ) {
516 if( $address->id() == $new_patron->mailing_address() ) {
517 $new_patron->clear_mailing_address();
518 ($new_patron, $evt) = _update_patron($session, $new_patron);
519 return (undef, $evt) if $evt;
522 if( $address->id() == $new_patron->billing_address() ) {
523 $new_patron->clear_billing_address();
524 ($new_patron, $evt) = _update_patron($session, $new_patron);
525 return (undef, $evt) if $evt;
528 $evt = _delete_address($session, $address);
529 return (undef, $evt) if $evt;
533 return ( $new_patron, undef );
537 # adds an address to the db and returns the address with new id
539 my($session, $address) = @_;
540 $address->clear_id();
542 $logger->info("Creating new address at street ".$address->street1);
544 # put the address into the database
545 my $id = $session->request(
546 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
547 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
550 return ($address, undef);
554 sub _update_address {
555 my( $session, $address ) = @_;
557 $logger->info("Updating address ".$address->id." in the DB");
559 my $stat = $session->request(
560 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
562 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
563 return ($address, undef);
568 sub _add_update_cards {
572 my $new_patron = shift;
576 my $virtual_id; #id of the card before creation
577 for my $card (@{$patron->cards()}) {
579 $card->usr($new_patron->id());
581 if(ref($card) and $card->isnew()) {
583 $virtual_id = $card->id();
584 ( $card, $evt ) = _add_card($session,$card);
585 return (undef, $evt) if $evt;
587 #if(ref($patron->card)) { $patron->card($patron->card->id); }
588 if($patron->card() == $virtual_id) {
589 $new_patron->card($card->id());
590 $new_patron->ischanged(1);
593 } elsif( ref($card) and $card->ischanged() ) {
594 $evt = _update_card($session, $card);
595 return (undef, $evt) if $evt;
599 return ( $new_patron, undef );
603 # adds an card to the db and returns the card with new id
605 my( $session, $card ) = @_;
608 $logger->info("Adding new patron card ".$card->barcode);
610 my $id = $session->request(
611 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
612 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
613 $logger->info("Successfully created patron card $id");
616 return ( $card, undef );
620 # returns event on error. returns undef otherwise
622 my( $session, $card ) = @_;
623 $logger->info("Updating patron card ".$card->id);
625 my $stat = $session->request(
626 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
627 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
634 # returns event on error. returns undef otherwise
635 sub _delete_address {
636 my( $session, $address ) = @_;
638 $logger->info("Deleting address ".$address->id." from DB");
640 my $stat = $session->request(
641 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
643 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
649 sub _add_survey_responses {
650 my ($session, $patron, $new_patron) = @_;
652 $logger->info( "Updating survey responses for patron ".$new_patron->id );
654 my $responses = $patron->survey_responses;
658 $_->usr($new_patron->id) for (@$responses);
660 my $evt = $U->simplereq( "open-ils.circ",
661 "open-ils.circ.survey.submit.user_id", $responses );
663 return (undef, $evt) if defined($U->event_code($evt));
667 return ( $new_patron, undef );
671 sub _create_stat_maps {
673 my($session, $user_session, $patron, $new_patron) = @_;
675 my $maps = $patron->stat_cat_entries();
677 for my $map (@$maps) {
679 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
681 if ($map->isdeleted()) {
682 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
684 } elsif ($map->isnew()) {
685 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
690 $map->target_usr($new_patron->id);
693 $logger->info("Updating stat entry with method $method and map $map");
695 my $stat = $session->request($method, $map)->gather(1);
696 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
700 return ($new_patron, undef);
703 sub _create_perm_maps {
705 my($session, $user_session, $patron, $new_patron) = @_;
707 my $maps = $patron->permissions;
709 for my $map (@$maps) {
711 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
712 if ($map->isdeleted()) {
713 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
714 } elsif ($map->isnew()) {
715 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
720 $map->usr($new_patron->id);
722 #warn( "Updating permissions with method $method and session $user_session and map $map" );
723 $logger->info( "Updating permissions with method $method and map $map" );
725 my $stat = $session->request($method, $map)->gather(1);
726 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
730 return ($new_patron, undef);
734 sub _create_standing_penalties {
736 my($session, $user_session, $patron, $new_patron) = @_;
738 my $maps = $patron->standing_penalties;
741 for my $map (@$maps) {
743 if ($map->isdeleted()) {
744 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
745 } elsif ($map->isnew()) {
746 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
752 $map->usr($new_patron->id);
754 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
756 my $stat = $session->request($method, $map)->gather(1);
757 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
760 return ($new_patron, undef);
765 __PACKAGE__->register_method(
766 method => "search_username",
767 api_name => "open-ils.actor.user.search.username",
770 sub search_username {
771 my($self, $client, $username) = @_;
772 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
774 "open-ils.cstore.direct.actor.user.search.atomic",
775 { usrname => $username }
783 __PACKAGE__->register_method(
784 method => "user_retrieve_by_barcode",
785 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
787 sub user_retrieve_by_barcode {
788 my($self, $client, $user_session, $barcode) = @_;
790 $logger->debug("Searching for user with barcode $barcode");
791 my ($user_obj, $evt) = $apputils->checkses($user_session);
794 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
796 "open-ils.cstore.direct.actor.card.search.atomic",
797 { barcode => $barcode }
800 if(!$card || !$card->[0]) {
801 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
805 my $user = flesh_user($card->usr());
807 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
810 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
817 __PACKAGE__->register_method(
818 method => "get_user_by_id",
819 api_name => "open-ils.actor.user.retrieve",);
822 my ($self, $client, $auth, $id) = @_;
823 my $e = new_editor(authtoken=>$auth);
824 return $e->event unless $e->checkauth;
825 my $user = $e->retrieve_actor_user($id)
827 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
833 __PACKAGE__->register_method(
834 method => "get_org_types",
835 api_name => "open-ils.actor.org_types.retrieve",);
839 my($self, $client) = @_;
840 return $org_types if $org_types;
841 return $org_types = new_editor()->retrieve_all_actor_org_unit_type();
846 __PACKAGE__->register_method(
847 method => "get_user_ident_types",
848 api_name => "open-ils.actor.user.ident_types.retrieve",
851 sub get_user_ident_types {
852 return $ident_types if $ident_types;
853 return $ident_types =
854 new_editor()->retrieve_all_config_identification_type();
860 __PACKAGE__->register_method(
861 method => "get_org_unit",
862 api_name => "open-ils.actor.org_unit.retrieve",
866 my( $self, $client, $user_session, $org_id ) = @_;
867 my $e = new_editor(authtoken => $user_session);
869 return $e->event unless $e->checkauth;
870 $org_id = $e->requestor->ws_ou;
872 my $o = $e->retrieve_actor_org_unit($org_id)
877 __PACKAGE__->register_method(
878 method => "search_org_unit",
879 api_name => "open-ils.actor.org_unit_list.search",
882 sub search_org_unit {
884 my( $self, $client, $field, $value ) = @_;
886 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
888 "open-ils.cstore.direct.actor.org_unit.search.atomic",
889 { $field => $value } );
897 __PACKAGE__->register_method(
898 method => "get_org_tree",
899 api_name => "open-ils.actor.org_tree.retrieve",
901 note => "Returns the entire org tree structure",
905 my( $self, $client) = @_;
907 $cache = OpenSRF::Utils::Cache->new("global", 0) unless $cache;
908 my $tree = $cache->get_cache('orgtree');
909 return $tree if $tree;
911 $tree = new_editor()->search_actor_org_unit(
913 {"parent_ou" => undef },
916 flesh_fields => { aou => ['children'] },
917 order_by => { aou => 'name'}
922 $cache->put_cache('orgtree', $tree);
927 # turns an org list into an org tree
930 my( $self, $orglist) = @_;
932 return $orglist unless (
933 ref($orglist) and @$orglist > 1 );
936 $a->ou_type <=> $b->ou_type ||
937 $a->name cmp $b->name } @$orglist;
939 for my $org (@list) {
941 next unless ($org and defined($org->parent_ou));
942 my ($parent) = grep { $_->id == $org->parent_ou } @list;
945 $parent->children([]) unless defined($parent->children);
946 push( @{$parent->children}, $org );
954 __PACKAGE__->register_method(
955 method => "get_org_descendants",
956 api_name => "open-ils.actor.org_tree.descendants.retrieve"
959 # depth is optional. org_unit is the id
960 sub get_org_descendants {
961 my( $self, $client, $org_unit, $depth ) = @_;
962 my $orglist = $apputils->simple_scalar_request(
964 "open-ils.storage.actor.org_unit.descendants.atomic",
966 return $self->build_org_tree($orglist);
970 __PACKAGE__->register_method(
971 method => "get_org_ancestors",
972 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
975 # depth is optional. org_unit is the id
976 sub get_org_ancestors {
977 my( $self, $client, $org_unit, $depth ) = @_;
978 my $orglist = $apputils->simple_scalar_request(
980 "open-ils.storage.actor.org_unit.ancestors.atomic",
982 return $self->build_org_tree($orglist);
986 __PACKAGE__->register_method(
987 method => "get_standings",
988 api_name => "open-ils.actor.standings.retrieve"
993 return $user_standings if $user_standings;
994 return $user_standings =
995 $apputils->simple_scalar_request(
997 "open-ils.cstore.direct.config.standing.search.atomic",
998 { id => { "!=" => undef } }
1004 __PACKAGE__->register_method(
1005 method => "get_my_org_path",
1006 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1009 sub get_my_org_path {
1010 my( $self, $client, $user_session, $org_id ) = @_;
1011 my $user_obj = $apputils->check_user_session($user_session);
1012 if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
1014 return $apputils->simple_scalar_request(
1016 "open-ils.storage.actor.org_unit.full_path.atomic",
1021 __PACKAGE__->register_method(
1022 method => "patron_adv_search",
1023 api_name => "open-ils.actor.patron.search.advanced" );
1024 sub patron_adv_search {
1025 my( $self, $client, $auth, $search_hash, $search_limit, $search_sort, $include_inactive ) = @_;
1026 my $e = new_editor(authtoken=>$auth);
1027 return $e->event unless $e->checkauth;
1028 return $e->event unless $e->allowed('VIEW_USER');
1029 return $U->storagereq(
1030 "open-ils.storage.actor.user.crazy_search",
1031 $search_hash, $search_limit, $search_sort, $include_inactive);
1036 sub _verify_password {
1037 my($user_session, $password) = @_;
1038 my $user_obj = $apputils->check_user_session($user_session);
1040 #grab the user with password
1041 $user_obj = $apputils->simple_scalar_request(
1043 "open-ils.cstore.direct.actor.user.retrieve",
1046 if($user_obj->passwd eq $password) {
1054 __PACKAGE__->register_method(
1055 method => "update_password",
1056 api_name => "open-ils.actor.user.password.update");
1058 __PACKAGE__->register_method(
1059 method => "update_password",
1060 api_name => "open-ils.actor.user.username.update");
1062 __PACKAGE__->register_method(
1063 method => "update_password",
1064 api_name => "open-ils.actor.user.email.update");
1066 sub update_password {
1067 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1071 my $user_obj = $apputils->check_user_session($user_session);
1073 if($self->api_name =~ /password/o) {
1075 #make sure they know the current password
1076 if(!_verify_password($user_session, md5_hex($current_password))) {
1077 return OpenILS::Event->new('INCORRECT_PASSWORD');
1080 $logger->debug("update_password setting new password $new_value");
1081 $user_obj->passwd($new_value);
1083 } elsif($self->api_name =~ /username/o) {
1084 my $users = search_username(undef, undef, $new_value);
1085 if( $users and $users->[0] ) {
1086 return OpenILS::Event->new('USERNAME_EXISTS');
1088 $user_obj->usrname($new_value);
1090 } elsif($self->api_name =~ /email/o) {
1091 #warn "Updating email to $new_value\n";
1092 $user_obj->email($new_value);
1095 my $session = $apputils->start_db_session();
1097 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1098 return $evt if $evt;
1100 $apputils->commit_db_session($session);
1102 if($user_obj) { return 1; }
1107 __PACKAGE__->register_method(
1108 method => "check_user_perms",
1109 api_name => "open-ils.actor.user.perm.check",
1110 notes => <<" NOTES");
1111 Takes a login session, user id, an org id, and an array of perm type strings. For each
1112 perm type, if the user does *not* have the given permission it is added
1113 to a list which is returned from the method. If all permissions
1114 are allowed, an empty list is returned
1115 if the logged in user does not match 'user_id', then the logged in user must
1116 have VIEW_PERMISSION priveleges.
1119 sub check_user_perms {
1120 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1122 my( $staff, $evt ) = $apputils->checkses($login_session);
1123 return $evt if $evt;
1125 if($staff->id ne $user_id) {
1126 if( $evt = $apputils->check_perms(
1127 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1133 for my $perm (@$perm_types) {
1134 if($apputils->check_perms($user_id, $org_id, $perm)) {
1135 push @not_allowed, $perm;
1139 return \@not_allowed
1142 __PACKAGE__->register_method(
1143 method => "check_user_perms2",
1144 api_name => "open-ils.actor.user.perm.check.multi_org",
1146 Checks the permissions on a list of perms and orgs for a user
1147 @param authtoken The login session key
1148 @param user_id The id of the user to check
1149 @param orgs The array of org ids
1150 @param perms The array of permission names
1151 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1152 if the logged in user does not match 'user_id', then the logged in user must
1153 have VIEW_PERMISSION priveleges.
1156 sub check_user_perms2 {
1157 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1159 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1160 $authtoken, $user_id, 'VIEW_PERMISSION' );
1161 return $evt if $evt;
1164 for my $org (@$orgs) {
1165 for my $perm (@$perms) {
1166 if($apputils->check_perms($user_id, $org, $perm)) {
1167 push @not_allowed, [ $org, $perm ];
1172 return \@not_allowed
1176 __PACKAGE__->register_method(
1177 method => 'check_user_perms3',
1178 api_name => 'open-ils.actor.user.perm.highest_org',
1180 Returns the highest org unit id at which a user has a given permission
1181 If the requestor does not match the target user, the requestor must have
1182 'VIEW_PERMISSION' rights at the home org unit of the target user
1183 @param authtoken The login session key
1184 @param userid The id of the user in question
1185 @param perm The permission to check
1186 @return The org unit highest in the org tree within which the user has
1187 the requested permission
1190 sub check_user_perms3 {
1191 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1193 my( $staff, $target, $org, $evt );
1195 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1196 $authtoken, $userid, 'VIEW_PERMISSION' );
1197 return $evt if $evt;
1199 my $tree = $self->get_org_tree();
1200 return _find_highest_perm_org( $perm, $userid, $target->ws_ou, $tree );
1204 sub _find_highest_perm_org {
1205 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1206 my $org = $apputils->find_org($org_tree, $start_org );
1210 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1212 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1218 __PACKAGE__->register_method(
1219 method => 'check_user_perms4',
1220 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1222 Returns the highest org unit id at which a user has a given permission
1223 If the requestor does not match the target user, the requestor must have
1224 'VIEW_PERMISSION' rights at the home org unit of the target user
1225 @param authtoken The login session key
1226 @param userid The id of the user in question
1227 @param perms An array of perm names to check
1228 @return An array of orgId's representing the org unit
1229 highest in the org tree within which the user has the requested permission
1230 The arrah of orgId's has matches the order of the perms array
1233 sub check_user_perms4 {
1234 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1236 my( $staff, $target, $org, $evt );
1238 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1239 $authtoken, $userid, 'VIEW_PERMISSION' );
1240 return $evt if $evt;
1243 return [] unless ref($perms);
1244 my $tree = $self->get_org_tree();
1246 for my $p (@$perms) {
1247 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1255 __PACKAGE__->register_method(
1256 method => "user_fines_summary",
1257 api_name => "open-ils.actor.user.fines.summary",
1258 notes => <<" NOTES");
1259 Returns a short summary of the users total open fines, excluding voided fines
1260 Params are login_session, user_id
1261 Returns a 'mous' object.
1264 sub user_fines_summary {
1265 my( $self, $client, $login_session, $user_id ) = @_;
1267 my $user_obj = $apputils->check_user_session($login_session);
1268 if($user_obj->id ne $user_id) {
1269 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1270 return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY");
1274 return $apputils->simple_scalar_request(
1276 "open-ils.cstore.direct.money.open_user_summary.search",
1277 { usr => $user_id } );
1284 __PACKAGE__->register_method(
1285 method => "user_transactions",
1286 api_name => "open-ils.actor.user.transactions",
1287 notes => <<" NOTES");
1288 Returns a list of open user transactions (mbts objects);
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_charge",
1296 notes => <<" NOTES");
1297 Returns a list of all open user transactions (mbts objects) that have an initial charge
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.have_balance",
1305 notes => <<" NOTES");
1306 Returns a list of all open user transactions (mbts objects) that have a balance
1307 Params are login_session, user_id
1308 Optional third parameter is the transactions type. defaults to all
1311 __PACKAGE__->register_method(
1312 method => "user_transactions",
1313 api_name => "open-ils.actor.user.transactions.fleshed",
1314 notes => <<" NOTES");
1315 Returns an object/hash of transaction, circ, title where transaction = an open
1316 user transactions (mbts objects), circ is the attached circluation, and title
1317 is the title the circ points to
1318 Params are login_session, user_id
1319 Optional third parameter is the transactions type. defaults to all
1322 __PACKAGE__->register_method(
1323 method => "user_transactions",
1324 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1325 notes => <<" NOTES");
1326 Returns an object/hash of transaction, circ, title where transaction = an open
1327 user transactions that has an initial charge (mbts objects), circ is the
1328 attached circluation, and title is the title the circ points to
1329 Params are login_session, user_id
1330 Optional third parameter is the transactions type. defaults to all
1333 __PACKAGE__->register_method(
1334 method => "user_transactions",
1335 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1336 notes => <<" NOTES");
1337 Returns an object/hash of transaction, circ, title where transaction = an open
1338 user transaction that has a balance (mbts objects), circ is the attached
1339 circluation, and title is the title the circ points to
1340 Params are login_session, user_id
1341 Optional third parameter is the transaction type. defaults to all
1344 __PACKAGE__->register_method(
1345 method => "user_transactions",
1346 api_name => "open-ils.actor.user.transactions.count",
1347 notes => <<" NOTES");
1348 Returns an object/hash of transaction, circ, title where transaction = an open
1349 user transactions (mbts objects), circ is the attached circluation, and title
1350 is the title the circ points to
1351 Params are login_session, user_id
1352 Optional third parameter is the transactions type. defaults to all
1355 __PACKAGE__->register_method(
1356 method => "user_transactions",
1357 api_name => "open-ils.actor.user.transactions.have_charge.count",
1358 notes => <<" NOTES");
1359 Returns an object/hash of transaction, circ, title where transaction = an open
1360 user transactions that has an initial charge (mbts objects), circ is the
1361 attached circluation, and title is the title the circ points to
1362 Params are login_session, user_id
1363 Optional third parameter is the transactions type. defaults to all
1366 __PACKAGE__->register_method(
1367 method => "user_transactions",
1368 api_name => "open-ils.actor.user.transactions.have_balance.count",
1369 notes => <<" NOTES");
1370 Returns an object/hash of transaction, circ, title where transaction = an open
1371 user transaction that has a balance (mbts objects), circ is the attached
1372 circluation, and title is the title the circ points to
1373 Params are login_session, user_id
1374 Optional third parameter is the transaction type. defaults to all
1377 __PACKAGE__->register_method(
1378 method => "user_transactions",
1379 api_name => "open-ils.actor.user.transactions.have_balance.total",
1380 notes => <<" NOTES");
1381 Returns an object/hash of transaction, circ, title where transaction = an open
1382 user transaction that has a balance (mbts objects), circ is the attached
1383 circluation, and title is the title the circ points to
1384 Params are login_session, user_id
1385 Optional third parameter is the transaction type. defaults to all
1390 sub user_transactions {
1391 my( $self, $client, $login_session, $user_id, $type ) = @_;
1393 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1394 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1395 return $evt if $evt;
1397 my $api = $self->api_name();
1401 if(defined($type)) { @xact = (xact_type => $type);
1403 } else { @xact = (); }
1406 ->method_lookup('open-ils.actor.user.transactions.history.still_open')
1407 ->run($login_session => $user_id => $type);
1409 if($api =~ /have_charge/o) {
1411 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1413 } elsif($api =~ /have_balance/o) {
1415 $trans = [ grep { int($_->balance_owed * 100) != 0 } @$trans ];
1418 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1422 if($api =~ /total/o) {
1424 for my $t (@$trans) {
1425 $total += $t->balance_owed;
1428 $logger->debug("Total balance owed by user $user_id: $total");
1432 if($api =~ /count/o) { return scalar @$trans; }
1433 if($api !~ /fleshed/o) { return $trans; }
1436 for my $t (@$trans) {
1438 if( $t->xact_type ne 'circulation' ) {
1439 push @resp, {transaction => $t};
1443 my $circ = $apputils->simple_scalar_request(
1445 "open-ils.cstore.direct.action.circulation.retrieve",
1450 my $title = $apputils->simple_scalar_request(
1452 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1453 $circ->target_copy );
1457 my $u = OpenILS::Utils::ModsParser->new();
1458 $u->start_mods_batch($title->marc());
1459 my $mods = $u->finish_mods_batch();
1460 $mods->doc_id($title->id) if $mods;
1462 push @resp, {transaction => $t, circ => $circ, record => $mods };
1470 __PACKAGE__->register_method(
1471 method => "user_transaction_retrieve",
1472 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1474 notes => <<" NOTES");
1475 Returns a fleshedtransaction record
1477 __PACKAGE__->register_method(
1478 method => "user_transaction_retrieve",
1479 api_name => "open-ils.actor.user.transaction.retrieve",
1481 notes => <<" NOTES");
1482 Returns a transaction record
1484 sub user_transaction_retrieve {
1485 my( $self, $client, $login_session, $bill_id ) = @_;
1487 my $trans = $apputils->simple_scalar_request(
1489 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1493 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1494 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1495 return $evt if $evt;
1497 my $api = $self->api_name();
1498 if($api !~ /fleshed/o) { return $trans; }
1500 if( $trans->xact_type ne 'circulation' ) {
1501 $logger->debug("Returning non-circ transaction");
1502 return {transaction => $trans};
1505 my $circ = $apputils->simple_scalar_request(
1507 "open-ils..direct.action.circulation.retrieve",
1510 return {transaction => $trans} unless $circ;
1511 $logger->debug("Found the circ transaction");
1513 my $title = $apputils->simple_scalar_request(
1515 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1516 $circ->target_copy );
1518 return {transaction => $trans, circ => $circ } unless $title;
1519 $logger->debug("Found the circ title");
1523 my $u = OpenILS::Utils::ModsParser->new();
1524 $u->start_mods_batch($title->marc());
1525 $mods = $u->finish_mods_batch();
1527 if ($title->id == OILS_PRECAT_RECORD) {
1528 my $copy = $apputils->simple_scalar_request(
1530 "open-ils.cstore.direct.asset.copy.retrieve",
1531 $circ->target_copy );
1533 $mods = new Fieldmapper::metabib::virtual_record;
1534 $mods->doc_id(OILS_PRECAT_RECORD);
1535 $mods->title($copy->dummy_title);
1536 $mods->author($copy->dummy_author);
1540 $logger->debug("MODSized the circ title");
1542 return {transaction => $trans, circ => $circ, record => $mods };
1546 __PACKAGE__->register_method(
1547 method => "hold_request_count",
1548 api_name => "open-ils.actor.user.hold_requests.count",
1550 notes => <<" NOTES");
1551 Returns hold ready/total counts
1553 sub hold_request_count {
1554 my( $self, $client, $login_session, $userid ) = @_;
1556 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1557 $login_session, $userid, 'VIEW_HOLD' );
1558 return $evt if $evt;
1561 my $holds = $apputils->simple_scalar_request(
1563 "open-ils.cstore.direct.action.hold_request.search.atomic",
1566 fulfillment_time => {"=" => undef },
1567 cancel_time => undef,
1572 for my $h (@$holds) {
1573 next unless $h->capture_time and $h->current_copy;
1575 my $copy = $apputils->simple_scalar_request(
1577 "open-ils.cstore.direct.asset.copy.retrieve",
1581 if ($copy and $copy->status == 8) {
1586 return { total => scalar(@$holds), ready => scalar(@ready) };
1590 __PACKAGE__->register_method(
1591 method => "checkedout_count",
1592 api_name => "open-ils.actor.user.checked_out.count__",
1594 notes => <<" NOTES");
1595 Returns a transaction record
1599 sub checkedout_count {
1600 my( $self, $client, $login_session, $userid ) = @_;
1602 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1603 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1604 return $evt if $evt;
1606 my $circs = $apputils->simple_scalar_request(
1608 "open-ils.cstore.direct.action.circulation.search.atomic",
1609 { usr => $userid, stop_fines => undef }
1610 #{ usr => $userid, checkin_time => {"=" => undef } }
1613 my $parser = DateTime::Format::ISO8601->new;
1616 for my $c (@$circs) {
1617 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1618 my $due = $due_dt->epoch;
1620 if ($due < DateTime->today->epoch) {
1625 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1629 __PACKAGE__->register_method(
1630 method => "checked_out",
1631 api_name => "open-ils.actor.user.checked_out",
1634 Returns a structure of circulations objects sorted by
1635 out, overdue, lost, claims_returned, long_overdue.
1636 A list of IDs are returned of each type.
1637 lost, long_overdue, and claims_returned circ will not
1638 be "finished" (there is an outstanding balance or some
1639 other pending action on the circ).
1641 The .count method also includes a 'total' field which
1642 sums all "open" circs
1646 __PACKAGE__->register_method(
1647 method => "checked_out",
1648 api_name => "open-ils.actor.user.checked_out.count",
1650 signature => q/@see open-ils.actor.user.checked_out/
1654 my( $self, $conn, $auth, $userid ) = @_;
1656 my $e = new_editor(authtoken=>$auth);
1657 return $e->event unless $e->checkauth;
1659 if( $userid ne $e->requestor->id ) {
1660 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1663 my $count = $self->api_name =~ /count/;
1664 return _checked_out( $count, $e, $userid );
1668 my( $iscount, $e, $userid ) = @_;
1671 my $meth = 'open-ils.storage.actor.user.checked_out';
1672 $meth = "$meth.count" if $iscount;
1673 return $U->storagereq($meth, $userid);
1675 # XXX Old code - moved to storage
1676 #------------------------------------------------------------------------------
1677 #------------------------------------------------------------------------------
1678 my $circs = $e->search_action_circulation(
1679 { usr => $userid, checkin_time => undef });
1681 my $parser = DateTime::Format::ISO8601->new;
1683 # split the circs up into overdue and not-overdue circs
1685 for my $c (@$circs) {
1686 if( $c->due_date ) {
1687 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1688 my $due = $due_dt->epoch;
1689 if ($due < DateTime->today->epoch) {
1699 my( @open, @od, @lost, @cr, @lo );
1701 while (my $c = shift(@out)) {
1702 push( @open, $c->id ) if (!$c->stop_fines || $c->stop_fines eq 'MAXFINES' || $c->stop_fines eq 'RENEW');
1703 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1704 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1705 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1708 while (my $c = shift(@overdue)) {
1709 push( @od, $c->id ) if (!$c->stop_fines || $c->stop_fines eq 'MAXFINES' || $c->stop_fines eq 'RENEW');
1710 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1711 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1712 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1717 total => @open + @od + @lost + @cr + @lo,
1718 out => scalar(@open),
1719 overdue => scalar(@od),
1720 lost => scalar(@lost),
1721 claims_returned => scalar(@cr),
1722 long_overdue => scalar(@lo)
1730 claims_returned => \@cr,
1731 long_overdue => \@lo
1736 sub _checked_out_WHAT {
1737 my( $iscount, $e, $userid ) = @_;
1739 my $circs = $e->search_action_circulation(
1740 { usr => $userid, stop_fines => undef });
1742 my $mcircs = $e->search_action_circulation(
1745 checkin_time => undef,
1746 xact_finish => undef,
1750 push( @$circs, @$mcircs );
1752 my $parser = DateTime::Format::ISO8601->new;
1754 # split the circs up into overdue and not-overdue circs
1756 for my $c (@$circs) {
1757 if( $c->due_date ) {
1758 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1759 my $due = $due_dt->epoch;
1760 if ($due < DateTime->today->epoch) {
1761 push @overdue, $c->id;
1770 # grab all of the lost, claims-returned, and longoverdue circs
1771 #my $open = $e->search_action_circulation(
1772 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1775 # these items have stop_fines, but no xact_finish, so money
1776 # is owed on them and they have not been checked in
1777 my $open = $e->search_action_circulation(
1780 stop_fines => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] },
1781 xact_finish => undef,
1782 checkin_time => undef,
1787 my( @lost, @cr, @lo );
1788 for my $c (@$open) {
1789 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1790 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1791 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1797 total => @$circs + @lost + @cr + @lo,
1798 out => scalar(@out),
1799 overdue => scalar(@overdue),
1800 lost => scalar(@lost),
1801 claims_returned => scalar(@cr),
1802 long_overdue => scalar(@lo)
1808 overdue => \@overdue,
1810 claims_returned => \@cr,
1811 long_overdue => \@lo
1817 __PACKAGE__->register_method(
1818 method => "checked_in_with_fines",
1819 api_name => "open-ils.actor.user.checked_in_with_fines",
1821 signature => q/@see open-ils.actor.user.checked_out/
1823 sub checked_in_with_fines {
1824 my( $self, $conn, $auth, $userid ) = @_;
1826 my $e = new_editor(authtoken=>$auth);
1827 return $e->event unless $e->checkauth;
1829 if( $userid ne $e->requestor->id ) {
1830 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1833 # money is owed on these items and they are checked in
1834 my $open = $e->search_action_circulation(
1837 xact_finish => undef,
1838 checkin_time => { "!=" => undef },
1843 my( @lost, @cr, @lo );
1844 for my $c (@$open) {
1845 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1846 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1847 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1852 claims_returned => \@cr,
1853 long_overdue => \@lo
1865 __PACKAGE__->register_method(
1866 method => "user_transaction_history",
1867 api_name => "open-ils.actor.user.transactions.history",
1869 notes => <<" NOTES");
1870 Returns a list of billable transaction ids for a user, optionally by type
1872 __PACKAGE__->register_method(
1873 method => "user_transaction_history",
1874 api_name => "open-ils.actor.user.transactions.history.have_charge",
1876 notes => <<" NOTES");
1877 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1879 __PACKAGE__->register_method(
1880 method => "user_transaction_history",
1881 api_name => "open-ils.actor.user.transactions.history.have_balance",
1883 notes => <<" NOTES");
1884 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1886 __PACKAGE__->register_method(
1887 method => "user_transaction_history",
1888 api_name => "open-ils.actor.user.transactions.history.still_open",
1890 notes => <<" NOTES");
1891 Returns a list of billable transaction ids for a user that are not finished
1893 __PACKAGE__->register_method(
1894 method => "user_transaction_history",
1895 api_name => "open-ils.actor.user.transactions.history.have_bill",
1897 notes => <<" NOTES");
1898 Returns a list of billable transaction ids for a user that has billings
1904 sub _user_transaction_history {
1905 my( $self, $client, $login_session, $user_id, $type ) = @_;
1907 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1908 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1909 return $evt if $evt;
1911 my $api = $self->api_name();
1916 @xact = (xact_type => $type) if(defined($type));
1917 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1918 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1920 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1922 my $trans = $apputils->simple_scalar_request(
1924 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1925 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1927 return [ map { $_->id } @$trans ];
1935 for my $x (@xacts) {
1936 my $s = new Fieldmapper::money::billable_transaction_summary;
1939 $s->xact_start( $x->xact_start );
1940 $s->xact_finish( $x->xact_finish );
1944 for my $b (@{ $x->billings }) {
1945 next if ($U->is_true($b->voided));
1946 $to += int($b->amount * 100);
1947 $lb ||= $b->billing_ts;
1948 if ($b->billing_ts ge $lb) {
1949 $lb = $b->billing_ts;
1950 $s->last_billing_note($b->note);
1951 $s->last_billing_ts($b->billing_ts);
1952 $s->last_billing_type($b->billing_type);
1956 $s->total_owed( sprintf('%0.2f', $to / 100 ) );
1960 for my $p (@{ $x->payments }) {
1961 next if ($U->is_true($p->voided));
1962 $tp += int($p->amount * 100);
1963 $lp ||= $p->payment_ts;
1964 if ($p->payment_ts ge $lp) {
1965 $lp = $p->payment_ts;
1966 $s->last_payment_note($p->note);
1967 $s->last_payment_ts($p->payment_ts);
1968 $s->last_payment_type($p->payment_type);
1971 $s->total_paid( sprintf('%0.2f', $tp / 100 ) );
1973 $s->balance_owed( sprintf('%0.2f', int($to - $tp) / 100) );
1975 $s->xact_type( 'grocery' ) if ($x->grocery);
1976 $s->xact_type( 'circulation' ) if ($x->circulation);
1984 sub user_transaction_history {
1985 my( $self, $conn, $auth, $userid, $type ) = @_;
1986 my $e = new_editor(authtoken=>$auth);
1987 return $e->event unless $e->checkauth;
1988 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1990 my $api = $self->api_name;
1991 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
1993 my @xacts = @{ $e->search_money_billable_transaction(
1994 [ { usr => $userid, @xact_finish },
1996 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
1997 order_by => { mbt => 'xact_start DESC' },
2002 my @mbts = _make_mbts( @xacts );
2004 if(defined($type)) {
2005 @mbts = grep { $_->xact_type eq $type } @mbts;
2008 if($api =~ /have_balance/o) {
2009 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
2012 if($api =~ /have_charge/o) {
2013 @mbts = grep { defined($_->last_billing_ts) } @mbts;
2016 if($api =~ /have_bill/o) {
2017 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
2025 __PACKAGE__->register_method(
2026 method => "user_perms",
2027 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2029 notes => <<" NOTES");
2030 Returns a list of permissions
2033 my( $self, $client, $authtoken, $user ) = @_;
2035 my( $staff, $evt ) = $apputils->checkses($authtoken);
2036 return $evt if $evt;
2038 $user ||= $staff->id;
2040 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2044 return $apputils->simple_scalar_request(
2046 "open-ils.storage.permission.user_perms.atomic",
2050 __PACKAGE__->register_method(
2051 method => "retrieve_perms",
2052 api_name => "open-ils.actor.permissions.retrieve",
2053 notes => <<" NOTES");
2054 Returns a list of permissions
2056 sub retrieve_perms {
2057 my( $self, $client ) = @_;
2058 return $apputils->simple_scalar_request(
2060 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2061 { id => { '!=' => undef } }
2065 __PACKAGE__->register_method(
2066 method => "retrieve_groups",
2067 api_name => "open-ils.actor.groups.retrieve",
2068 notes => <<" NOTES");
2069 Returns a list of user groupss
2071 sub retrieve_groups {
2072 my( $self, $client ) = @_;
2073 return new_editor()->retrieve_all_permission_grp_tree();
2076 __PACKAGE__->register_method(
2077 method => "retrieve_org_address",
2078 api_name => "open-ils.actor.org_unit.address.retrieve",
2079 notes => <<' NOTES');
2080 Returns an org_unit address by ID
2081 @param An org_address ID
2083 sub retrieve_org_address {
2084 my( $self, $client, $id ) = @_;
2085 return $apputils->simple_scalar_request(
2087 "open-ils.cstore.direct.actor.org_address.retrieve",
2092 __PACKAGE__->register_method(
2093 method => "retrieve_groups_tree",
2094 api_name => "open-ils.actor.groups.tree.retrieve",
2095 notes => <<" NOTES");
2096 Returns a list of user groups
2098 sub retrieve_groups_tree {
2099 my( $self, $client ) = @_;
2100 return new_editor()->search_permission_grp_tree(
2105 flesh_fields => { pgt => ["children"] },
2106 order_by => { pgt => 'name'}
2113 # turns an org list into an org tree
2115 sub build_group_tree {
2117 my( $self, $grplist) = @_;
2119 return $grplist unless (
2120 ref($grplist) and @$grplist > 1 );
2122 my @list = sort { $a->name cmp $b->name } @$grplist;
2125 for my $grp (@list) {
2127 if ($grp and !defined($grp->parent)) {
2131 my ($parent) = grep { $_->id == $grp->parent} @list;
2133 $parent->children([]) unless defined($parent->children);
2134 push( @{$parent->children}, $grp );
2142 __PACKAGE__->register_method(
2143 method => "add_user_to_groups",
2144 api_name => "open-ils.actor.user.set_groups",
2145 notes => <<" NOTES");
2146 Adds a user to one or more permission groups
2149 sub add_user_to_groups {
2150 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2152 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2153 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2154 return $evt if $evt;
2156 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2157 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2158 return $evt if $evt;
2160 $apputils->simplereq(
2162 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2164 for my $group (@$groups) {
2165 my $link = Fieldmapper::permission::usr_grp_map->new;
2167 $link->usr($userid);
2169 my $id = $apputils->simplereq(
2171 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2177 __PACKAGE__->register_method(
2178 method => "get_user_perm_groups",
2179 api_name => "open-ils.actor.user.get_groups",
2180 notes => <<" NOTES");
2181 Retrieve a user's permission groups.
2185 sub get_user_perm_groups {
2186 my( $self, $client, $authtoken, $userid ) = @_;
2188 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2189 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2190 return $evt if $evt;
2192 return $apputils->simplereq(
2194 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2199 __PACKAGE__->register_method (
2200 method => 'register_workstation',
2201 api_name => 'open-ils.actor.workstation.register.override',
2202 signature => q/@see open-ils.actor.workstation.register/);
2204 __PACKAGE__->register_method (
2205 method => 'register_workstation',
2206 api_name => 'open-ils.actor.workstation.register',
2208 Registers a new workstion in the system
2209 @param authtoken The login session key
2210 @param name The name of the workstation id
2211 @param owner The org unit that owns this workstation
2212 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2213 if the name is already in use.
2216 sub _register_workstation {
2217 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2218 my( $requestor, $evt ) = $U->checkses($authtoken);
2219 return $evt if $evt;
2220 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2221 return $evt if $evt;
2223 my $ws = $U->cstorereq(
2224 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2225 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2227 $ws = Fieldmapper::actor::workstation->new;
2228 $ws->owning_lib($owner);
2231 my $id = $U->storagereq(
2232 'open-ils.storage.direct.actor.workstation.create', $ws );
2233 return $U->DB_UPDATE_FAILED($ws) unless $id;
2239 sub register_workstation {
2240 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2242 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2243 return $e->event unless $e->checkauth;
2244 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2245 my $existing = $e->search_actor_workstation({name => $name});
2248 if( $self->api_name =~ /override/o ) {
2249 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2250 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2252 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2256 my $ws = Fieldmapper::actor::workstation->new;
2257 $ws->owning_lib($owner);
2259 $e->create_actor_workstation($ws) or return $e->event;
2261 return $ws->id; # note: editor sets the id on the new object for us
2265 __PACKAGE__->register_method (
2266 method => 'fetch_patron_note',
2267 api_name => 'open-ils.actor.note.retrieve.all',
2269 Returns a list of notes for a given user
2270 Requestor must have VIEW_USER permission if pub==false and
2271 @param authtoken The login session key
2272 @param args Hash of params including
2273 patronid : the patron's id
2274 pub : true if retrieving only public notes
2278 sub fetch_patron_note {
2279 my( $self, $conn, $authtoken, $args ) = @_;
2280 my $patronid = $$args{patronid};
2282 my($reqr, $evt) = $U->checkses($authtoken);
2283 return $evt if $evt;
2286 ($patron, $evt) = $U->fetch_user($patronid);
2287 return $evt if $evt;
2290 if( $patronid ne $reqr->id ) {
2291 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2292 return $evt if $evt;
2294 return $U->cstorereq(
2295 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2296 { usr => $patronid, pub => 't' } );
2299 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2300 return $evt if $evt;
2302 return $U->cstorereq(
2303 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2306 __PACKAGE__->register_method (
2307 method => 'create_user_note',
2308 api_name => 'open-ils.actor.note.create',
2310 Creates a new note for the given user
2311 @param authtoken The login session key
2312 @param note The note object
2315 sub create_user_note {
2316 my( $self, $conn, $authtoken, $note ) = @_;
2317 my( $reqr, $patron, $evt ) =
2318 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2319 return $evt if $evt;
2320 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2322 $note->creator($reqr->id);
2323 my $id = $U->storagereq(
2324 'open-ils.storage.direct.actor.usr_note.create', $note );
2325 return $U->DB_UPDATE_FAILED($note) unless $id;
2330 __PACKAGE__->register_method (
2331 method => 'delete_user_note',
2332 api_name => 'open-ils.actor.note.delete',
2334 Deletes a note for the given user
2335 @param authtoken The login session key
2336 @param noteid The note id
2339 sub delete_user_note {
2340 my( $self, $conn, $authtoken, $noteid ) = @_;
2342 my $note = $U->cstorereq(
2343 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2344 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2346 my( $reqr, $patron, $evt ) =
2347 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2348 return $evt if $evt;
2349 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2351 my $stat = $U->storagereq(
2352 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2353 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2358 __PACKAGE__->register_method (
2359 method => 'update_user_note',
2360 api_name => 'open-ils.actor.note.update',
2362 @param authtoken The login session key
2363 @param note The note
2367 sub update_user_note {
2368 my( $self, $conn, $auth, $note ) = @_;
2369 my $e = new_editor(authtoken=>$auth, xact=>1);
2370 return $e->event unless $e->checkauth;
2371 my $patron = $e->retrieve_actor_user($note->usr)
2372 or return $e->event;
2373 return $e->event unless
2374 $e->allowed('UPDATE_USER', $patron->home_ou);
2375 $e->update_actor_user_note($note)
2376 or return $e->event;
2384 __PACKAGE__->register_method (
2385 method => 'create_closed_date',
2386 api_name => 'open-ils.actor.org_unit.closed_date.create',
2388 Creates a new closing entry for the given org_unit
2389 @param authtoken The login session key
2390 @param note The closed_date object
2393 sub create_closed_date {
2394 my( $self, $conn, $authtoken, $cd ) = @_;
2396 my( $user, $evt ) = $U->checkses($authtoken);
2397 return $evt if $evt;
2399 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2400 return $evt if $evt;
2402 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2404 my $id = $U->storagereq(
2405 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2406 return $U->DB_UPDATE_FAILED($cd) unless $id;
2411 __PACKAGE__->register_method (
2412 method => 'delete_closed_date',
2413 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2415 Deletes a closing entry for the given org_unit
2416 @param authtoken The login session key
2417 @param noteid The close_date id
2420 sub delete_closed_date {
2421 my( $self, $conn, $authtoken, $cd ) = @_;
2423 my( $user, $evt ) = $U->checkses($authtoken);
2424 return $evt if $evt;
2427 ($cd_obj, $evt) = fetch_closed_date($cd);
2428 return $evt if $evt;
2430 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2431 return $evt if $evt;
2433 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2435 my $stat = $U->storagereq(
2436 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2437 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2442 __PACKAGE__->register_method(
2443 method => 'usrname_exists',
2444 api_name => 'open-ils.actor.username.exists',
2446 Returns 1 if the requested username exists, returns 0 otherwise
2450 sub usrname_exists {
2451 my( $self, $conn, $auth, $usrname ) = @_;
2452 my $e = new_editor(authtoken=>$auth);
2453 return $e->event unless $e->checkauth;
2454 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2455 return $$a[0] if $a and @$a;
2459 __PACKAGE__->register_method(
2460 method => 'barcode_exists',
2461 api_name => 'open-ils.actor.barcode.exists',
2463 Returns 1 if the requested barcode exists, returns 0 otherwise
2467 sub barcode_exists {
2468 my( $self, $conn, $auth, $barcode ) = @_;
2469 my $e = new_editor(authtoken=>$auth);
2470 return $e->event unless $e->checkauth;
2471 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2472 return $$a[0] if $a and @$a;
2477 __PACKAGE__->register_method(
2478 method => 'retrieve_net_levels',
2479 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2482 sub retrieve_net_levels {
2483 my( $self, $conn, $auth ) = @_;
2484 my $e = new_editor(authtoken=>$auth);
2485 return $e->event unless $e->checkauth;
2486 return $e->retrieve_all_config_net_access_level();
2490 __PACKAGE__->register_method(
2491 method => 'fetch_org_by_shortname',
2492 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2494 sub fetch_org_by_shortname {
2495 my( $self, $conn, $sname ) = @_;
2496 my $e = new_editor();
2497 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2498 return $e->event unless $org;
2503 __PACKAGE__->register_method(
2504 method => 'session_home_lib',
2505 api_name => 'open-ils.actor.session.home_lib',
2508 sub session_home_lib {
2509 my( $self, $conn, $auth ) = @_;
2510 my $e = new_editor(authtoken=>$auth);
2511 return undef unless $e->checkauth;
2512 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2513 return $org->shortname;
2518 __PACKAGE__->register_method(
2519 method => 'slim_tree',
2520 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2523 my $tree = new_editor()->search_actor_org_unit(
2525 {"parent_ou" => undef },
2528 flesh_fields => { aou => ['children'] },
2529 order_by => { aou => 'name'},
2530 select => { aou => ["id","shortname", "name"]},
2535 return trim_tree($tree);
2541 return undef unless $tree;
2543 code => $tree->shortname,
2544 name => $tree->name,
2546 if( $tree->children and @{$tree->children} ) {
2547 $htree->{children} = [];
2548 for my $c (@{$tree->children}) {
2549 push( @{$htree->{children}}, trim_tree($c) );
2558 __PACKAGE__->register_method(
2559 method => "user_retrieve_fleshed_by_id",
2560 api_name => "open-ils.actor.user.fleshed.retrieve",);
2562 sub user_retrieve_fleshed_by_id {
2563 my( $self, $client, $auth, $user_id, $fields ) = @_;
2564 my $e = new_editor(authtoken => $auth);
2565 return $e->event unless $e->checkauth;
2566 if( $e->requestor->id != $user_id ) {
2567 return $e->event unless $e->allowed('VIEW_USER');
2572 "standing_penalties",
2576 "stat_cat_entries" ];
2577 return new_flesh_user($user_id, $fields, $e);
2581 sub new_flesh_user {
2584 my $fields = shift || [];
2585 my $e = shift || new_editor(xact=>1);
2587 my $user = $e->retrieve_actor_user(
2592 "flesh_fields" => { "au" => $fields }
2595 ) or return $e->event;
2598 if( grep { $_ eq 'addresses' } @$fields ) {
2600 $user->addresses([]) unless @{$user->addresses};
2602 if( ref $user->billing_address ) {
2603 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2604 push( @{$user->addresses}, $user->billing_address );
2608 if( ref $user->mailing_address ) {
2609 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2610 push( @{$user->addresses}, $user->mailing_address );
2616 $user->clear_passwd();
2623 __PACKAGE__->register_method(
2624 method => "user_retrieve_parts",
2625 api_name => "open-ils.actor.user.retrieve.parts",);
2627 sub user_retrieve_parts {
2628 my( $self, $client, $auth, $user_id, $fields ) = @_;
2629 my $e = new_editor(authtoken => $auth);
2630 return $e->event unless $e->checkauth;
2631 if( $e->requestor->id != $user_id ) {
2632 return $e->event unless $e->allowed('VIEW_USER');
2635 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2636 push(@resp, $user->$_()) for(@$fields);