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 ) = @_;
1670 my $circs = $e->search_action_circulation(
1671 { usr => $userid, checkin_time => undef });
1673 my $parser = DateTime::Format::ISO8601->new;
1675 # split the circs up into overdue and not-overdue circs
1677 for my $c (@$circs) {
1678 if( $c->due_date ) {
1679 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1680 my $due = $due_dt->epoch;
1681 if ($due < DateTime->today->epoch) {
1682 push @overdue, $c->id;
1691 my( @lost, @cr, @lo );
1692 for my $c (@$open) {
1693 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1694 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1695 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1701 total => @$circs + @lost + @cr + @lo,
1702 out => scalar(@out),
1703 overdue => scalar(@overdue),
1704 lost => scalar(@lost),
1705 claims_returned => scalar(@cr),
1706 long_overdue => scalar(@lo)
1712 overdue => \@overdue,
1714 claims_returned => \@cr,
1715 long_overdue => \@lo
1720 sub _checked_out_WHAT {
1721 my( $iscount, $e, $userid ) = @_;
1723 my $circs = $e->search_action_circulation(
1724 { usr => $userid, stop_fines => undef });
1726 my $mcircs = $e->search_action_circulation(
1729 checkin_time => undef,
1730 xact_finish => undef,
1734 push( @$circs, @$mcircs );
1736 my $parser = DateTime::Format::ISO8601->new;
1738 # split the circs up into overdue and not-overdue circs
1740 for my $c (@$circs) {
1741 if( $c->due_date ) {
1742 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1743 my $due = $due_dt->epoch;
1744 if ($due < DateTime->today->epoch) {
1745 push @overdue, $c->id;
1754 # grab all of the lost, claims-returned, and longoverdue circs
1755 #my $open = $e->search_action_circulation(
1756 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1759 # these items have stop_fines, but no xact_finish, so money
1760 # is owed on them and they have not been checked in
1761 my $open = $e->search_action_circulation(
1764 stop_fines => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] },
1765 xact_finish => undef,
1766 checkin_time => undef,
1771 my( @lost, @cr, @lo );
1772 for my $c (@$open) {
1773 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1774 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1775 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1781 total => @$circs + @lost + @cr + @lo,
1782 out => scalar(@out),
1783 overdue => scalar(@overdue),
1784 lost => scalar(@lost),
1785 claims_returned => scalar(@cr),
1786 long_overdue => scalar(@lo)
1792 overdue => \@overdue,
1794 claims_returned => \@cr,
1795 long_overdue => \@lo
1801 __PACKAGE__->register_method(
1802 method => "checked_in_with_fines",
1803 api_name => "open-ils.actor.user.checked_in_with_fines",
1805 signature => q/@see open-ils.actor.user.checked_out/
1807 sub checked_in_with_fines {
1808 my( $self, $conn, $auth, $userid ) = @_;
1810 my $e = new_editor(authtoken=>$auth);
1811 return $e->event unless $e->checkauth;
1813 if( $userid ne $e->requestor->id ) {
1814 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1817 # money is owed on these items and they are checked in
1818 my $open = $e->search_action_circulation(
1821 xact_finish => undef,
1822 checkin_time => { "!=" => undef },
1827 my( @lost, @cr, @lo );
1828 for my $c (@$open) {
1829 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1830 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1831 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1836 claims_returned => \@cr,
1837 long_overdue => \@lo
1849 __PACKAGE__->register_method(
1850 method => "user_transaction_history",
1851 api_name => "open-ils.actor.user.transactions.history",
1853 notes => <<" NOTES");
1854 Returns a list of billable transaction ids for a user, optionally by type
1856 __PACKAGE__->register_method(
1857 method => "user_transaction_history",
1858 api_name => "open-ils.actor.user.transactions.history.have_charge",
1860 notes => <<" NOTES");
1861 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1863 __PACKAGE__->register_method(
1864 method => "user_transaction_history",
1865 api_name => "open-ils.actor.user.transactions.history.have_balance",
1867 notes => <<" NOTES");
1868 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1870 __PACKAGE__->register_method(
1871 method => "user_transaction_history",
1872 api_name => "open-ils.actor.user.transactions.history.still_open",
1874 notes => <<" NOTES");
1875 Returns a list of billable transaction ids for a user that are not finished
1877 __PACKAGE__->register_method(
1878 method => "user_transaction_history",
1879 api_name => "open-ils.actor.user.transactions.history.have_bill",
1881 notes => <<" NOTES");
1882 Returns a list of billable transaction ids for a user that has billings
1888 sub _user_transaction_history {
1889 my( $self, $client, $login_session, $user_id, $type ) = @_;
1891 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1892 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1893 return $evt if $evt;
1895 my $api = $self->api_name();
1900 @xact = (xact_type => $type) if(defined($type));
1901 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1902 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1904 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1906 my $trans = $apputils->simple_scalar_request(
1908 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1909 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1911 return [ map { $_->id } @$trans ];
1919 for my $x (@xacts) {
1920 my $s = new Fieldmapper::money::billable_transaction_summary;
1923 $s->xact_start( $x->xact_start );
1924 $s->xact_finish( $x->xact_finish );
1928 for my $b (@{ $x->billings }) {
1929 next if ($U->is_true($b->voided));
1930 $to += int($b->amount * 100);
1931 $lb ||= $b->billing_ts;
1932 if ($b->billing_ts ge $lb) {
1933 $lb = $b->billing_ts;
1934 $s->last_billing_note($b->note);
1935 $s->last_billing_ts($b->billing_ts);
1936 $s->last_billing_type($b->billing_type);
1940 $s->total_owed( sprintf('%0.2f', $to / 100 ) );
1944 for my $p (@{ $x->payments }) {
1945 next if ($U->is_true($p->voided));
1946 $tp += int($p->amount * 100);
1947 $lp ||= $p->payment_ts;
1948 if ($p->payment_ts ge $lp) {
1949 $lp = $p->payment_ts;
1950 $s->last_payment_note($p->note);
1951 $s->last_payment_ts($p->payment_ts);
1952 $s->last_payment_type($p->payment_type);
1955 $s->total_paid( sprintf('%0.2f', $tp / 100 ) );
1957 $s->balance_owed( sprintf('%0.2f', int($to - $tp) / 100) );
1959 $s->xact_type( 'grocery' ) if ($x->grocery);
1960 $s->xact_type( 'circulation' ) if ($x->circulation);
1968 sub user_transaction_history {
1969 my( $self, $conn, $auth, $userid, $type ) = @_;
1970 my $e = new_editor(authtoken=>$auth);
1971 return $e->event unless $e->checkauth;
1972 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1974 my $api = $self->api_name;
1975 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
1977 my @xacts = @{ $e->search_money_billable_transaction(
1978 [ { usr => $userid, @xact_finish },
1980 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
1981 order_by => { mbt => 'xact_start DESC' },
1986 my @mbts = _make_mbts( @xacts );
1988 if(defined($type)) {
1989 @mbts = grep { $_->xact_type eq $type } @mbts;
1992 if($api =~ /have_balance/o) {
1993 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
1996 if($api =~ /have_charge/o) {
1997 @mbts = grep { defined($_->last_billing_ts) } @mbts;
2000 if($api =~ /have_bill/o) {
2001 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
2009 __PACKAGE__->register_method(
2010 method => "user_perms",
2011 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2013 notes => <<" NOTES");
2014 Returns a list of permissions
2017 my( $self, $client, $authtoken, $user ) = @_;
2019 my( $staff, $evt ) = $apputils->checkses($authtoken);
2020 return $evt if $evt;
2022 $user ||= $staff->id;
2024 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2028 return $apputils->simple_scalar_request(
2030 "open-ils.storage.permission.user_perms.atomic",
2034 __PACKAGE__->register_method(
2035 method => "retrieve_perms",
2036 api_name => "open-ils.actor.permissions.retrieve",
2037 notes => <<" NOTES");
2038 Returns a list of permissions
2040 sub retrieve_perms {
2041 my( $self, $client ) = @_;
2042 return $apputils->simple_scalar_request(
2044 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2045 { id => { '!=' => undef } }
2049 __PACKAGE__->register_method(
2050 method => "retrieve_groups",
2051 api_name => "open-ils.actor.groups.retrieve",
2052 notes => <<" NOTES");
2053 Returns a list of user groupss
2055 sub retrieve_groups {
2056 my( $self, $client ) = @_;
2057 return new_editor()->retrieve_all_permission_grp_tree();
2060 __PACKAGE__->register_method(
2061 method => "retrieve_org_address",
2062 api_name => "open-ils.actor.org_unit.address.retrieve",
2063 notes => <<' NOTES');
2064 Returns an org_unit address by ID
2065 @param An org_address ID
2067 sub retrieve_org_address {
2068 my( $self, $client, $id ) = @_;
2069 return $apputils->simple_scalar_request(
2071 "open-ils.cstore.direct.actor.org_address.retrieve",
2076 __PACKAGE__->register_method(
2077 method => "retrieve_groups_tree",
2078 api_name => "open-ils.actor.groups.tree.retrieve",
2079 notes => <<" NOTES");
2080 Returns a list of user groups
2082 sub retrieve_groups_tree {
2083 my( $self, $client ) = @_;
2084 return new_editor()->search_permission_grp_tree(
2089 flesh_fields => { pgt => ["children"] },
2090 order_by => { pgt => 'name'}
2097 # turns an org list into an org tree
2099 sub build_group_tree {
2101 my( $self, $grplist) = @_;
2103 return $grplist unless (
2104 ref($grplist) and @$grplist > 1 );
2106 my @list = sort { $a->name cmp $b->name } @$grplist;
2109 for my $grp (@list) {
2111 if ($grp and !defined($grp->parent)) {
2115 my ($parent) = grep { $_->id == $grp->parent} @list;
2117 $parent->children([]) unless defined($parent->children);
2118 push( @{$parent->children}, $grp );
2126 __PACKAGE__->register_method(
2127 method => "add_user_to_groups",
2128 api_name => "open-ils.actor.user.set_groups",
2129 notes => <<" NOTES");
2130 Adds a user to one or more permission groups
2133 sub add_user_to_groups {
2134 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2136 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2137 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2138 return $evt if $evt;
2140 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2141 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2142 return $evt if $evt;
2144 $apputils->simplereq(
2146 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2148 for my $group (@$groups) {
2149 my $link = Fieldmapper::permission::usr_grp_map->new;
2151 $link->usr($userid);
2153 my $id = $apputils->simplereq(
2155 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2161 __PACKAGE__->register_method(
2162 method => "get_user_perm_groups",
2163 api_name => "open-ils.actor.user.get_groups",
2164 notes => <<" NOTES");
2165 Retrieve a user's permission groups.
2169 sub get_user_perm_groups {
2170 my( $self, $client, $authtoken, $userid ) = @_;
2172 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2173 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2174 return $evt if $evt;
2176 return $apputils->simplereq(
2178 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2183 __PACKAGE__->register_method (
2184 method => 'register_workstation',
2185 api_name => 'open-ils.actor.workstation.register.override',
2186 signature => q/@see open-ils.actor.workstation.register/);
2188 __PACKAGE__->register_method (
2189 method => 'register_workstation',
2190 api_name => 'open-ils.actor.workstation.register',
2192 Registers a new workstion in the system
2193 @param authtoken The login session key
2194 @param name The name of the workstation id
2195 @param owner The org unit that owns this workstation
2196 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2197 if the name is already in use.
2200 sub _register_workstation {
2201 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2202 my( $requestor, $evt ) = $U->checkses($authtoken);
2203 return $evt if $evt;
2204 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2205 return $evt if $evt;
2207 my $ws = $U->cstorereq(
2208 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2209 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2211 $ws = Fieldmapper::actor::workstation->new;
2212 $ws->owning_lib($owner);
2215 my $id = $U->storagereq(
2216 'open-ils.storage.direct.actor.workstation.create', $ws );
2217 return $U->DB_UPDATE_FAILED($ws) unless $id;
2223 sub register_workstation {
2224 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2226 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2227 return $e->event unless $e->checkauth;
2228 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2229 my $existing = $e->search_actor_workstation({name => $name});
2232 if( $self->api_name =~ /override/o ) {
2233 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2234 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2236 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2240 my $ws = Fieldmapper::actor::workstation->new;
2241 $ws->owning_lib($owner);
2243 $e->create_actor_workstation($ws) or return $e->event;
2245 return $ws->id; # note: editor sets the id on the new object for us
2249 __PACKAGE__->register_method (
2250 method => 'fetch_patron_note',
2251 api_name => 'open-ils.actor.note.retrieve.all',
2253 Returns a list of notes for a given user
2254 Requestor must have VIEW_USER permission if pub==false and
2255 @param authtoken The login session key
2256 @param args Hash of params including
2257 patronid : the patron's id
2258 pub : true if retrieving only public notes
2262 sub fetch_patron_note {
2263 my( $self, $conn, $authtoken, $args ) = @_;
2264 my $patronid = $$args{patronid};
2266 my($reqr, $evt) = $U->checkses($authtoken);
2269 ($patron, $evt) = $U->fetch_user($patronid);
2270 return $evt if $evt;
2273 if( $patronid ne $reqr->id ) {
2274 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2275 return $evt if $evt;
2277 return $U->cstorereq(
2278 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2279 { usr => $patronid, pub => 't' } );
2282 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2283 return $evt if $evt;
2285 return $U->cstorereq(
2286 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2289 __PACKAGE__->register_method (
2290 method => 'create_user_note',
2291 api_name => 'open-ils.actor.note.create',
2293 Creates a new note for the given user
2294 @param authtoken The login session key
2295 @param note The note object
2298 sub create_user_note {
2299 my( $self, $conn, $authtoken, $note ) = @_;
2300 my( $reqr, $patron, $evt ) =
2301 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2302 return $evt if $evt;
2303 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2305 $note->creator($reqr->id);
2306 my $id = $U->storagereq(
2307 'open-ils.storage.direct.actor.usr_note.create', $note );
2308 return $U->DB_UPDATE_FAILED($note) unless $id;
2313 __PACKAGE__->register_method (
2314 method => 'delete_user_note',
2315 api_name => 'open-ils.actor.note.delete',
2317 Deletes a note for the given user
2318 @param authtoken The login session key
2319 @param noteid The note id
2322 sub delete_user_note {
2323 my( $self, $conn, $authtoken, $noteid ) = @_;
2325 my $note = $U->cstorereq(
2326 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2327 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2329 my( $reqr, $patron, $evt ) =
2330 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2331 return $evt if $evt;
2332 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2334 my $stat = $U->storagereq(
2335 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2336 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2341 __PACKAGE__->register_method (
2342 method => 'update_user_note',
2343 api_name => 'open-ils.actor.note.update',
2345 @param authtoken The login session key
2346 @param note The note
2350 sub update_user_note {
2351 my( $self, $conn, $auth, $note ) = @_;
2352 my $e = new_editor(authtoken=>$auth, xact=>1);
2353 return $e->event unless $e->checkauth;
2354 my $patron = $e->retrieve_actor_user($note->usr)
2355 or return $e->event;
2356 return $e->event unless
2357 $e->allowed('UPDATE_USER', $patron->home_ou);
2358 $e->update_actor_user_note($note)
2359 or return $e->event;
2367 __PACKAGE__->register_method (
2368 method => 'create_closed_date',
2369 api_name => 'open-ils.actor.org_unit.closed_date.create',
2371 Creates a new closing entry for the given org_unit
2372 @param authtoken The login session key
2373 @param note The closed_date object
2376 sub create_closed_date {
2377 my( $self, $conn, $authtoken, $cd ) = @_;
2379 my( $user, $evt ) = $U->checkses($authtoken);
2380 return $evt if $evt;
2382 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2383 return $evt if $evt;
2385 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2387 my $id = $U->storagereq(
2388 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2389 return $U->DB_UPDATE_FAILED($cd) unless $id;
2394 __PACKAGE__->register_method (
2395 method => 'delete_closed_date',
2396 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2398 Deletes a closing entry for the given org_unit
2399 @param authtoken The login session key
2400 @param noteid The close_date id
2403 sub delete_closed_date {
2404 my( $self, $conn, $authtoken, $cd ) = @_;
2406 my( $user, $evt ) = $U->checkses($authtoken);
2407 return $evt if $evt;
2410 ($cd_obj, $evt) = fetch_closed_date($cd);
2411 return $evt if $evt;
2413 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2414 return $evt if $evt;
2416 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2418 my $stat = $U->storagereq(
2419 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2420 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2425 __PACKAGE__->register_method(
2426 method => 'usrname_exists',
2427 api_name => 'open-ils.actor.username.exists',
2429 Returns 1 if the requested username exists, returns 0 otherwise
2433 sub usrname_exists {
2434 my( $self, $conn, $auth, $usrname ) = @_;
2435 my $e = new_editor(authtoken=>$auth);
2436 return $e->event unless $e->checkauth;
2437 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2438 return $$a[0] if $a and @$a;
2442 __PACKAGE__->register_method(
2443 method => 'barcode_exists',
2444 api_name => 'open-ils.actor.barcode.exists',
2446 Returns 1 if the requested barcode exists, returns 0 otherwise
2450 sub barcode_exists {
2451 my( $self, $conn, $auth, $barcode ) = @_;
2452 my $e = new_editor(authtoken=>$auth);
2453 return $e->event unless $e->checkauth;
2454 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2455 return $$a[0] if $a and @$a;
2460 __PACKAGE__->register_method(
2461 method => 'retrieve_net_levels',
2462 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2465 sub retrieve_net_levels {
2466 my( $self, $conn, $auth ) = @_;
2467 my $e = new_editor(authtoken=>$auth);
2468 return $e->event unless $e->checkauth;
2469 return $e->retrieve_all_config_net_access_level();
2473 __PACKAGE__->register_method(
2474 method => 'fetch_org_by_shortname',
2475 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2477 sub fetch_org_by_shortname {
2478 my( $self, $conn, $sname ) = @_;
2479 my $e = new_editor();
2480 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2481 return $e->event unless $org;
2486 __PACKAGE__->register_method(
2487 method => 'session_home_lib',
2488 api_name => 'open-ils.actor.session.home_lib',
2491 sub session_home_lib {
2492 my( $self, $conn, $auth ) = @_;
2493 my $e = new_editor(authtoken=>$auth);
2494 return undef unless $e->checkauth;
2495 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2496 return $org->shortname;
2501 __PACKAGE__->register_method(
2502 method => 'slim_tree',
2503 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2506 my $tree = new_editor()->search_actor_org_unit(
2508 {"parent_ou" => undef },
2511 flesh_fields => { aou => ['children'] },
2512 order_by => { aou => 'name'},
2513 select => { aou => ["id","shortname", "name"]},
2518 return trim_tree($tree);
2524 return undef unless $tree;
2526 code => $tree->shortname,
2527 name => $tree->name,
2529 if( $tree->children and @{$tree->children} ) {
2530 $htree->{children} = [];
2531 for my $c (@{$tree->children}) {
2532 push( @{$htree->{children}}, trim_tree($c) );
2541 __PACKAGE__->register_method(
2542 method => "user_retrieve_fleshed_by_id",
2543 api_name => "open-ils.actor.user.fleshed.retrieve",);
2545 sub user_retrieve_fleshed_by_id {
2546 my( $self, $client, $auth, $user_id, $fields ) = @_;
2547 my $e = new_editor(authtoken => $auth);
2548 return $e->event unless $e->checkauth;
2549 if( $e->requestor->id != $user_id ) {
2550 return $e->event unless $e->allowed('VIEW_USER');
2555 "standing_penalties",
2559 "stat_cat_entries" ];
2560 return new_flesh_user($user_id, $fields, $e);
2564 sub new_flesh_user {
2567 my $fields = shift || [];
2568 my $e = shift || new_editor(xact=>1);
2570 my $user = $e->retrieve_actor_user(
2575 "flesh_fields" => { "au" => $fields }
2578 ) or return $e->event;
2581 if( grep { $_ eq 'addresses' } @$fields ) {
2583 $user->addresses([]) unless @{$user->addresses};
2585 if( ref $user->billing_address ) {
2586 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2587 push( @{$user->addresses}, $user->billing_address );
2591 if( ref $user->mailing_address ) {
2592 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2593 push( @{$user->addresses}, $user->mailing_address );
2599 $user->clear_passwd();
2606 __PACKAGE__->register_method(
2607 method => "user_retrieve_parts",
2608 api_name => "open-ils.actor.user.retrieve.parts",);
2610 sub user_retrieve_parts {
2611 my( $self, $client, $auth, $user_id, $fields ) = @_;
2612 my $e = new_editor(authtoken => $auth);
2613 return $e->event unless $e->checkauth;
2614 if( $e->requestor->id != $user_id ) {
2615 return $e->event unless $e->allowed('VIEW_USER');
2618 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2619 push(@resp, $user->$_()) for(@$fields);