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) {
1691 my( @open, @od, @lost, @cr, @lo );
1693 while (my $c = shift(@out)) {
1694 push( @open, $c->id ) if (!$c->stop_fines || $c->stop_fines eq 'MAXFINES' || $c->stop_fines eq 'RENEW');
1695 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1696 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1697 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1700 while (my $c = shift(@overdue)) {
1701 push( @od, $c->id ) if (!$c->stop_fines || $c->stop_fines eq 'MAXFINES' || $c->stop_fines eq 'RENEW');
1702 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1703 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1704 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1709 total => @open + @od + @lost + @cr + @lo,
1710 out => scalar(@open),
1711 overdue => scalar(@od),
1712 lost => scalar(@lost),
1713 claims_returned => scalar(@cr),
1714 long_overdue => scalar(@lo)
1722 claims_returned => \@cr,
1723 long_overdue => \@lo
1728 sub _checked_out_WHAT {
1729 my( $iscount, $e, $userid ) = @_;
1731 my $circs = $e->search_action_circulation(
1732 { usr => $userid, stop_fines => undef });
1734 my $mcircs = $e->search_action_circulation(
1737 checkin_time => undef,
1738 xact_finish => undef,
1742 push( @$circs, @$mcircs );
1744 my $parser = DateTime::Format::ISO8601->new;
1746 # split the circs up into overdue and not-overdue circs
1748 for my $c (@$circs) {
1749 if( $c->due_date ) {
1750 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1751 my $due = $due_dt->epoch;
1752 if ($due < DateTime->today->epoch) {
1753 push @overdue, $c->id;
1762 # grab all of the lost, claims-returned, and longoverdue circs
1763 #my $open = $e->search_action_circulation(
1764 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1767 # these items have stop_fines, but no xact_finish, so money
1768 # is owed on them and they have not been checked in
1769 my $open = $e->search_action_circulation(
1772 stop_fines => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] },
1773 xact_finish => undef,
1774 checkin_time => undef,
1779 my( @lost, @cr, @lo );
1780 for my $c (@$open) {
1781 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1782 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1783 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1789 total => @$circs + @lost + @cr + @lo,
1790 out => scalar(@out),
1791 overdue => scalar(@overdue),
1792 lost => scalar(@lost),
1793 claims_returned => scalar(@cr),
1794 long_overdue => scalar(@lo)
1800 overdue => \@overdue,
1802 claims_returned => \@cr,
1803 long_overdue => \@lo
1809 __PACKAGE__->register_method(
1810 method => "checked_in_with_fines",
1811 api_name => "open-ils.actor.user.checked_in_with_fines",
1813 signature => q/@see open-ils.actor.user.checked_out/
1815 sub checked_in_with_fines {
1816 my( $self, $conn, $auth, $userid ) = @_;
1818 my $e = new_editor(authtoken=>$auth);
1819 return $e->event unless $e->checkauth;
1821 if( $userid ne $e->requestor->id ) {
1822 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1825 # money is owed on these items and they are checked in
1826 my $open = $e->search_action_circulation(
1829 xact_finish => undef,
1830 checkin_time => { "!=" => undef },
1835 my( @lost, @cr, @lo );
1836 for my $c (@$open) {
1837 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1838 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1839 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1844 claims_returned => \@cr,
1845 long_overdue => \@lo
1857 __PACKAGE__->register_method(
1858 method => "user_transaction_history",
1859 api_name => "open-ils.actor.user.transactions.history",
1861 notes => <<" NOTES");
1862 Returns a list of billable transaction ids for a user, optionally by type
1864 __PACKAGE__->register_method(
1865 method => "user_transaction_history",
1866 api_name => "open-ils.actor.user.transactions.history.have_charge",
1868 notes => <<" NOTES");
1869 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1871 __PACKAGE__->register_method(
1872 method => "user_transaction_history",
1873 api_name => "open-ils.actor.user.transactions.history.have_balance",
1875 notes => <<" NOTES");
1876 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1878 __PACKAGE__->register_method(
1879 method => "user_transaction_history",
1880 api_name => "open-ils.actor.user.transactions.history.still_open",
1882 notes => <<" NOTES");
1883 Returns a list of billable transaction ids for a user that are not finished
1885 __PACKAGE__->register_method(
1886 method => "user_transaction_history",
1887 api_name => "open-ils.actor.user.transactions.history.have_bill",
1889 notes => <<" NOTES");
1890 Returns a list of billable transaction ids for a user that has billings
1896 sub _user_transaction_history {
1897 my( $self, $client, $login_session, $user_id, $type ) = @_;
1899 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1900 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1901 return $evt if $evt;
1903 my $api = $self->api_name();
1908 @xact = (xact_type => $type) if(defined($type));
1909 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1910 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1912 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1914 my $trans = $apputils->simple_scalar_request(
1916 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1917 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1919 return [ map { $_->id } @$trans ];
1927 for my $x (@xacts) {
1928 my $s = new Fieldmapper::money::billable_transaction_summary;
1931 $s->xact_start( $x->xact_start );
1932 $s->xact_finish( $x->xact_finish );
1936 for my $b (@{ $x->billings }) {
1937 next if ($U->is_true($b->voided));
1938 $to += int($b->amount * 100);
1939 $lb ||= $b->billing_ts;
1940 if ($b->billing_ts ge $lb) {
1941 $lb = $b->billing_ts;
1942 $s->last_billing_note($b->note);
1943 $s->last_billing_ts($b->billing_ts);
1944 $s->last_billing_type($b->billing_type);
1948 $s->total_owed( sprintf('%0.2f', $to / 100 ) );
1952 for my $p (@{ $x->payments }) {
1953 next if ($U->is_true($p->voided));
1954 $tp += int($p->amount * 100);
1955 $lp ||= $p->payment_ts;
1956 if ($p->payment_ts ge $lp) {
1957 $lp = $p->payment_ts;
1958 $s->last_payment_note($p->note);
1959 $s->last_payment_ts($p->payment_ts);
1960 $s->last_payment_type($p->payment_type);
1963 $s->total_paid( sprintf('%0.2f', $tp / 100 ) );
1965 $s->balance_owed( sprintf('%0.2f', int($to - $tp) / 100) );
1967 $s->xact_type( 'grocery' ) if ($x->grocery);
1968 $s->xact_type( 'circulation' ) if ($x->circulation);
1976 sub user_transaction_history {
1977 my( $self, $conn, $auth, $userid, $type ) = @_;
1978 my $e = new_editor(authtoken=>$auth);
1979 return $e->event unless $e->checkauth;
1980 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1982 my $api = $self->api_name;
1983 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
1985 my @xacts = @{ $e->search_money_billable_transaction(
1986 [ { usr => $userid, @xact_finish },
1988 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
1989 order_by => { mbt => 'xact_start DESC' },
1994 my @mbts = _make_mbts( @xacts );
1996 if(defined($type)) {
1997 @mbts = grep { $_->xact_type eq $type } @mbts;
2000 if($api =~ /have_balance/o) {
2001 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
2004 if($api =~ /have_charge/o) {
2005 @mbts = grep { defined($_->last_billing_ts) } @mbts;
2008 if($api =~ /have_bill/o) {
2009 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
2017 __PACKAGE__->register_method(
2018 method => "user_perms",
2019 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2021 notes => <<" NOTES");
2022 Returns a list of permissions
2025 my( $self, $client, $authtoken, $user ) = @_;
2027 my( $staff, $evt ) = $apputils->checkses($authtoken);
2028 return $evt if $evt;
2030 $user ||= $staff->id;
2032 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2036 return $apputils->simple_scalar_request(
2038 "open-ils.storage.permission.user_perms.atomic",
2042 __PACKAGE__->register_method(
2043 method => "retrieve_perms",
2044 api_name => "open-ils.actor.permissions.retrieve",
2045 notes => <<" NOTES");
2046 Returns a list of permissions
2048 sub retrieve_perms {
2049 my( $self, $client ) = @_;
2050 return $apputils->simple_scalar_request(
2052 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2053 { id => { '!=' => undef } }
2057 __PACKAGE__->register_method(
2058 method => "retrieve_groups",
2059 api_name => "open-ils.actor.groups.retrieve",
2060 notes => <<" NOTES");
2061 Returns a list of user groupss
2063 sub retrieve_groups {
2064 my( $self, $client ) = @_;
2065 return new_editor()->retrieve_all_permission_grp_tree();
2068 __PACKAGE__->register_method(
2069 method => "retrieve_org_address",
2070 api_name => "open-ils.actor.org_unit.address.retrieve",
2071 notes => <<' NOTES');
2072 Returns an org_unit address by ID
2073 @param An org_address ID
2075 sub retrieve_org_address {
2076 my( $self, $client, $id ) = @_;
2077 return $apputils->simple_scalar_request(
2079 "open-ils.cstore.direct.actor.org_address.retrieve",
2084 __PACKAGE__->register_method(
2085 method => "retrieve_groups_tree",
2086 api_name => "open-ils.actor.groups.tree.retrieve",
2087 notes => <<" NOTES");
2088 Returns a list of user groups
2090 sub retrieve_groups_tree {
2091 my( $self, $client ) = @_;
2092 return new_editor()->search_permission_grp_tree(
2097 flesh_fields => { pgt => ["children"] },
2098 order_by => { pgt => 'name'}
2105 # turns an org list into an org tree
2107 sub build_group_tree {
2109 my( $self, $grplist) = @_;
2111 return $grplist unless (
2112 ref($grplist) and @$grplist > 1 );
2114 my @list = sort { $a->name cmp $b->name } @$grplist;
2117 for my $grp (@list) {
2119 if ($grp and !defined($grp->parent)) {
2123 my ($parent) = grep { $_->id == $grp->parent} @list;
2125 $parent->children([]) unless defined($parent->children);
2126 push( @{$parent->children}, $grp );
2134 __PACKAGE__->register_method(
2135 method => "add_user_to_groups",
2136 api_name => "open-ils.actor.user.set_groups",
2137 notes => <<" NOTES");
2138 Adds a user to one or more permission groups
2141 sub add_user_to_groups {
2142 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2144 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2145 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2146 return $evt if $evt;
2148 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2149 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2150 return $evt if $evt;
2152 $apputils->simplereq(
2154 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2156 for my $group (@$groups) {
2157 my $link = Fieldmapper::permission::usr_grp_map->new;
2159 $link->usr($userid);
2161 my $id = $apputils->simplereq(
2163 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2169 __PACKAGE__->register_method(
2170 method => "get_user_perm_groups",
2171 api_name => "open-ils.actor.user.get_groups",
2172 notes => <<" NOTES");
2173 Retrieve a user's permission groups.
2177 sub get_user_perm_groups {
2178 my( $self, $client, $authtoken, $userid ) = @_;
2180 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2181 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2182 return $evt if $evt;
2184 return $apputils->simplereq(
2186 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2191 __PACKAGE__->register_method (
2192 method => 'register_workstation',
2193 api_name => 'open-ils.actor.workstation.register.override',
2194 signature => q/@see open-ils.actor.workstation.register/);
2196 __PACKAGE__->register_method (
2197 method => 'register_workstation',
2198 api_name => 'open-ils.actor.workstation.register',
2200 Registers a new workstion in the system
2201 @param authtoken The login session key
2202 @param name The name of the workstation id
2203 @param owner The org unit that owns this workstation
2204 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2205 if the name is already in use.
2208 sub _register_workstation {
2209 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2210 my( $requestor, $evt ) = $U->checkses($authtoken);
2211 return $evt if $evt;
2212 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2213 return $evt if $evt;
2215 my $ws = $U->cstorereq(
2216 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2217 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2219 $ws = Fieldmapper::actor::workstation->new;
2220 $ws->owning_lib($owner);
2223 my $id = $U->storagereq(
2224 'open-ils.storage.direct.actor.workstation.create', $ws );
2225 return $U->DB_UPDATE_FAILED($ws) unless $id;
2231 sub register_workstation {
2232 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2234 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2235 return $e->event unless $e->checkauth;
2236 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2237 my $existing = $e->search_actor_workstation({name => $name});
2240 if( $self->api_name =~ /override/o ) {
2241 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2242 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2244 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2248 my $ws = Fieldmapper::actor::workstation->new;
2249 $ws->owning_lib($owner);
2251 $e->create_actor_workstation($ws) or return $e->event;
2253 return $ws->id; # note: editor sets the id on the new object for us
2257 __PACKAGE__->register_method (
2258 method => 'fetch_patron_note',
2259 api_name => 'open-ils.actor.note.retrieve.all',
2261 Returns a list of notes for a given user
2262 Requestor must have VIEW_USER permission if pub==false and
2263 @param authtoken The login session key
2264 @param args Hash of params including
2265 patronid : the patron's id
2266 pub : true if retrieving only public notes
2270 sub fetch_patron_note {
2271 my( $self, $conn, $authtoken, $args ) = @_;
2272 my $patronid = $$args{patronid};
2274 my($reqr, $evt) = $U->checkses($authtoken);
2277 ($patron, $evt) = $U->fetch_user($patronid);
2278 return $evt if $evt;
2281 if( $patronid ne $reqr->id ) {
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',
2287 { usr => $patronid, pub => 't' } );
2290 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2291 return $evt if $evt;
2293 return $U->cstorereq(
2294 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2297 __PACKAGE__->register_method (
2298 method => 'create_user_note',
2299 api_name => 'open-ils.actor.note.create',
2301 Creates a new note for the given user
2302 @param authtoken The login session key
2303 @param note The note object
2306 sub create_user_note {
2307 my( $self, $conn, $authtoken, $note ) = @_;
2308 my( $reqr, $patron, $evt ) =
2309 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2310 return $evt if $evt;
2311 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2313 $note->creator($reqr->id);
2314 my $id = $U->storagereq(
2315 'open-ils.storage.direct.actor.usr_note.create', $note );
2316 return $U->DB_UPDATE_FAILED($note) unless $id;
2321 __PACKAGE__->register_method (
2322 method => 'delete_user_note',
2323 api_name => 'open-ils.actor.note.delete',
2325 Deletes a note for the given user
2326 @param authtoken The login session key
2327 @param noteid The note id
2330 sub delete_user_note {
2331 my( $self, $conn, $authtoken, $noteid ) = @_;
2333 my $note = $U->cstorereq(
2334 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2335 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2337 my( $reqr, $patron, $evt ) =
2338 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2339 return $evt if $evt;
2340 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2342 my $stat = $U->storagereq(
2343 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2344 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2349 __PACKAGE__->register_method (
2350 method => 'update_user_note',
2351 api_name => 'open-ils.actor.note.update',
2353 @param authtoken The login session key
2354 @param note The note
2358 sub update_user_note {
2359 my( $self, $conn, $auth, $note ) = @_;
2360 my $e = new_editor(authtoken=>$auth, xact=>1);
2361 return $e->event unless $e->checkauth;
2362 my $patron = $e->retrieve_actor_user($note->usr)
2363 or return $e->event;
2364 return $e->event unless
2365 $e->allowed('UPDATE_USER', $patron->home_ou);
2366 $e->update_actor_user_note($note)
2367 or return $e->event;
2375 __PACKAGE__->register_method (
2376 method => 'create_closed_date',
2377 api_name => 'open-ils.actor.org_unit.closed_date.create',
2379 Creates a new closing entry for the given org_unit
2380 @param authtoken The login session key
2381 @param note The closed_date object
2384 sub create_closed_date {
2385 my( $self, $conn, $authtoken, $cd ) = @_;
2387 my( $user, $evt ) = $U->checkses($authtoken);
2388 return $evt if $evt;
2390 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2391 return $evt if $evt;
2393 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2395 my $id = $U->storagereq(
2396 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2397 return $U->DB_UPDATE_FAILED($cd) unless $id;
2402 __PACKAGE__->register_method (
2403 method => 'delete_closed_date',
2404 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2406 Deletes a closing entry for the given org_unit
2407 @param authtoken The login session key
2408 @param noteid The close_date id
2411 sub delete_closed_date {
2412 my( $self, $conn, $authtoken, $cd ) = @_;
2414 my( $user, $evt ) = $U->checkses($authtoken);
2415 return $evt if $evt;
2418 ($cd_obj, $evt) = fetch_closed_date($cd);
2419 return $evt if $evt;
2421 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2422 return $evt if $evt;
2424 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2426 my $stat = $U->storagereq(
2427 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2428 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2433 __PACKAGE__->register_method(
2434 method => 'usrname_exists',
2435 api_name => 'open-ils.actor.username.exists',
2437 Returns 1 if the requested username exists, returns 0 otherwise
2441 sub usrname_exists {
2442 my( $self, $conn, $auth, $usrname ) = @_;
2443 my $e = new_editor(authtoken=>$auth);
2444 return $e->event unless $e->checkauth;
2445 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2446 return $$a[0] if $a and @$a;
2450 __PACKAGE__->register_method(
2451 method => 'barcode_exists',
2452 api_name => 'open-ils.actor.barcode.exists',
2454 Returns 1 if the requested barcode exists, returns 0 otherwise
2458 sub barcode_exists {
2459 my( $self, $conn, $auth, $barcode ) = @_;
2460 my $e = new_editor(authtoken=>$auth);
2461 return $e->event unless $e->checkauth;
2462 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2463 return $$a[0] if $a and @$a;
2468 __PACKAGE__->register_method(
2469 method => 'retrieve_net_levels',
2470 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2473 sub retrieve_net_levels {
2474 my( $self, $conn, $auth ) = @_;
2475 my $e = new_editor(authtoken=>$auth);
2476 return $e->event unless $e->checkauth;
2477 return $e->retrieve_all_config_net_access_level();
2481 __PACKAGE__->register_method(
2482 method => 'fetch_org_by_shortname',
2483 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2485 sub fetch_org_by_shortname {
2486 my( $self, $conn, $sname ) = @_;
2487 my $e = new_editor();
2488 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2489 return $e->event unless $org;
2494 __PACKAGE__->register_method(
2495 method => 'session_home_lib',
2496 api_name => 'open-ils.actor.session.home_lib',
2499 sub session_home_lib {
2500 my( $self, $conn, $auth ) = @_;
2501 my $e = new_editor(authtoken=>$auth);
2502 return undef unless $e->checkauth;
2503 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2504 return $org->shortname;
2509 __PACKAGE__->register_method(
2510 method => 'slim_tree',
2511 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2514 my $tree = new_editor()->search_actor_org_unit(
2516 {"parent_ou" => undef },
2519 flesh_fields => { aou => ['children'] },
2520 order_by => { aou => 'name'},
2521 select => { aou => ["id","shortname", "name"]},
2526 return trim_tree($tree);
2532 return undef unless $tree;
2534 code => $tree->shortname,
2535 name => $tree->name,
2537 if( $tree->children and @{$tree->children} ) {
2538 $htree->{children} = [];
2539 for my $c (@{$tree->children}) {
2540 push( @{$htree->{children}}, trim_tree($c) );
2549 __PACKAGE__->register_method(
2550 method => "user_retrieve_fleshed_by_id",
2551 api_name => "open-ils.actor.user.fleshed.retrieve",);
2553 sub user_retrieve_fleshed_by_id {
2554 my( $self, $client, $auth, $user_id, $fields ) = @_;
2555 my $e = new_editor(authtoken => $auth);
2556 return $e->event unless $e->checkauth;
2557 if( $e->requestor->id != $user_id ) {
2558 return $e->event unless $e->allowed('VIEW_USER');
2563 "standing_penalties",
2567 "stat_cat_entries" ];
2568 return new_flesh_user($user_id, $fields, $e);
2572 sub new_flesh_user {
2575 my $fields = shift || [];
2576 my $e = shift || new_editor(xact=>1);
2578 my $user = $e->retrieve_actor_user(
2583 "flesh_fields" => { "au" => $fields }
2586 ) or return $e->event;
2589 if( grep { $_ eq 'addresses' } @$fields ) {
2591 $user->addresses([]) unless @{$user->addresses};
2593 if( ref $user->billing_address ) {
2594 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2595 push( @{$user->addresses}, $user->billing_address );
2599 if( ref $user->mailing_address ) {
2600 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2601 push( @{$user->addresses}, $user->mailing_address );
2607 $user->clear_passwd();
2614 __PACKAGE__->register_method(
2615 method => "user_retrieve_parts",
2616 api_name => "open-ils.actor.user.retrieve.parts",);
2618 sub user_retrieve_parts {
2619 my( $self, $client, $auth, $user_id, $fields ) = @_;
2620 my $e = new_editor(authtoken => $auth);
2621 return $e->event unless $e->checkauth;
2622 if( $e->requestor->id != $user_id ) {
2623 return $e->event unless $e->allowed('VIEW_USER');
2626 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2627 push(@resp, $user->$_()) for(@$fields);