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, stop_fines => undef });
1673 my $mcircs = $e->search_action_circulation(
1676 checkin_time => undef,
1677 xact_finish => undef,
1678 stop_fines => OILS_STOP_FINES_MAX_FINES
1682 push( @$circs, @$mcircs );
1684 my $parser = DateTime::Format::ISO8601->new;
1686 # split the circs up into overdue and not-overdue circs
1688 for my $c (@$circs) {
1689 if( $c->due_date ) {
1690 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1691 my $due = $due_dt->epoch;
1692 if ($due < DateTime->today->epoch) {
1693 push @overdue, $c->id;
1702 # grab all of the lost, claims-returned, and longoverdue circs
1703 #my $open = $e->search_action_circulation(
1704 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1707 # these items have stop_fines, but no xact_finish, so money
1708 # is owed on them and they have not been checked in
1709 my $open = $e->search_action_circulation(
1712 stop_fines => { '!=' => undef },
1713 xact_finish => undef,
1714 checkin_time => undef,
1719 my( @lost, @cr, @lo );
1720 for my $c (@$open) {
1721 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1722 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1723 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1729 total => @$circs + @lost + @cr + @lo,
1730 out => scalar(@out),
1731 overdue => scalar(@overdue),
1732 lost => scalar(@lost),
1733 claims_returned => scalar(@cr),
1734 long_overdue => scalar(@lo)
1740 overdue => \@overdue,
1742 claims_returned => \@cr,
1743 long_overdue => \@lo
1749 __PACKAGE__->register_method(
1750 method => "checked_in_with_fines",
1751 api_name => "open-ils.actor.user.checked_in_with_fines",
1753 signature => q/@see open-ils.actor.user.checked_out/
1755 sub checked_in_with_fines {
1756 my( $self, $conn, $auth, $userid ) = @_;
1758 my $e = new_editor(authtoken=>$auth);
1759 return $e->event unless $e->checkauth;
1761 if( $userid ne $e->requestor->id ) {
1762 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1765 # money is owed on these items and they are checked in
1766 my $open = $e->search_action_circulation(
1769 xact_finish => undef,
1770 checkin_time => { "!=" => undef },
1775 my( @lost, @cr, @lo );
1776 for my $c (@$open) {
1777 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1778 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1779 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1784 claims_returned => \@cr,
1785 long_overdue => \@lo
1797 __PACKAGE__->register_method(
1798 method => "user_transaction_history",
1799 api_name => "open-ils.actor.user.transactions.history",
1801 notes => <<" NOTES");
1802 Returns a list of billable transaction ids for a user, optionally by type
1804 __PACKAGE__->register_method(
1805 method => "user_transaction_history",
1806 api_name => "open-ils.actor.user.transactions.history.have_charge",
1808 notes => <<" NOTES");
1809 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1811 __PACKAGE__->register_method(
1812 method => "user_transaction_history",
1813 api_name => "open-ils.actor.user.transactions.history.have_balance",
1815 notes => <<" NOTES");
1816 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1818 __PACKAGE__->register_method(
1819 method => "user_transaction_history",
1820 api_name => "open-ils.actor.user.transactions.history.still_open",
1822 notes => <<" NOTES");
1823 Returns a list of billable transaction ids for a user that are not finished
1825 __PACKAGE__->register_method(
1826 method => "user_transaction_history",
1827 api_name => "open-ils.actor.user.transactions.history.have_bill",
1829 notes => <<" NOTES");
1830 Returns a list of billable transaction ids for a user that has billings
1836 sub _user_transaction_history {
1837 my( $self, $client, $login_session, $user_id, $type ) = @_;
1839 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1840 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1841 return $evt if $evt;
1843 my $api = $self->api_name();
1848 @xact = (xact_type => $type) if(defined($type));
1849 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1850 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1852 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1854 my $trans = $apputils->simple_scalar_request(
1856 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1857 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1859 return [ map { $_->id } @$trans ];
1867 for my $x (@xacts) {
1868 my $s = new Fieldmapper::money::billable_transaction_summary;
1871 $s->xact_start( $x->xact_start );
1872 $s->xact_finish( $x->xact_finish );
1876 for my $b (@{ $x->billings }) {
1877 next if ($U->is_true($b->voided));
1878 $to += int($b->amount * 100);
1879 $lb ||= $b->billing_ts;
1880 if ($b->billing_ts ge $lb) {
1881 $lb = $b->billing_ts;
1882 $s->last_billing_note($b->note);
1883 $s->last_billing_ts($b->billing_ts);
1884 $s->last_billing_type($b->billing_type);
1888 $s->total_owed( sprintf('%0.2f', $to / 100 ) );
1892 for my $p (@{ $x->payments }) {
1893 next if ($U->is_true($p->voided));
1894 $tp += int($p->amount * 100);
1895 $lp ||= $p->payment_ts;
1896 if ($p->payment_ts ge $lp) {
1897 $lp = $p->payment_ts;
1898 $s->last_payment_note($p->note);
1899 $s->last_payment_ts($p->payment_ts);
1900 $s->last_payment_type($p->payment_type);
1903 $s->total_paid( sprintf('%0.2f', $tp / 100 ) );
1905 $s->balance_owed( sprintf('%0.2f', int($to - $tp) / 100) );
1907 $s->xact_type( 'grocery' ) if ($x->grocery);
1908 $s->xact_type( 'circulation' ) if ($x->circulation);
1916 sub user_transaction_history {
1917 my( $self, $conn, $auth, $userid, $type ) = @_;
1918 my $e = new_editor(authtoken=>$auth);
1919 return $e->event unless $e->checkauth;
1920 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1922 my $api = $self->api_name;
1923 my @xact_finish = (xact_finish => undef ) if ($api !~ /history$/);
1925 my @xacts = @{ $e->search_money_billable_transaction(
1926 [ { usr => $userid, @xact_finish },
1928 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
1929 order_by => { mbt => 'xact_start DESC' },
1934 my @mbts = _make_mbts( @xacts );
1936 if(defined($type)) {
1937 @mbts = grep { $_->xact_type eq $type } @mbts;
1940 if($api =~ /have_balance/o) {
1941 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
1944 if($api =~ /have_charge/o) {
1945 @mbts = grep { defined($_->last_billing_ts) } @mbts;
1948 if($api =~ /have_bill/o) {
1949 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
1957 __PACKAGE__->register_method(
1958 method => "user_perms",
1959 api_name => "open-ils.actor.permissions.user_perms.retrieve",
1961 notes => <<" NOTES");
1962 Returns a list of permissions
1965 my( $self, $client, $authtoken, $user ) = @_;
1967 my( $staff, $evt ) = $apputils->checkses($authtoken);
1968 return $evt if $evt;
1970 $user ||= $staff->id;
1972 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1976 return $apputils->simple_scalar_request(
1978 "open-ils.storage.permission.user_perms.atomic",
1982 __PACKAGE__->register_method(
1983 method => "retrieve_perms",
1984 api_name => "open-ils.actor.permissions.retrieve",
1985 notes => <<" NOTES");
1986 Returns a list of permissions
1988 sub retrieve_perms {
1989 my( $self, $client ) = @_;
1990 return $apputils->simple_scalar_request(
1992 "open-ils.cstore.direct.permission.perm_list.search.atomic",
1993 { id => { '!=' => undef } }
1997 __PACKAGE__->register_method(
1998 method => "retrieve_groups",
1999 api_name => "open-ils.actor.groups.retrieve",
2000 notes => <<" NOTES");
2001 Returns a list of user groupss
2003 sub retrieve_groups {
2004 my( $self, $client ) = @_;
2005 return new_editor()->retrieve_all_permission_grp_tree();
2008 __PACKAGE__->register_method(
2009 method => "retrieve_org_address",
2010 api_name => "open-ils.actor.org_unit.address.retrieve",
2011 notes => <<' NOTES');
2012 Returns an org_unit address by ID
2013 @param An org_address ID
2015 sub retrieve_org_address {
2016 my( $self, $client, $id ) = @_;
2017 return $apputils->simple_scalar_request(
2019 "open-ils.cstore.direct.actor.org_address.retrieve",
2024 __PACKAGE__->register_method(
2025 method => "retrieve_groups_tree",
2026 api_name => "open-ils.actor.groups.tree.retrieve",
2027 notes => <<" NOTES");
2028 Returns a list of user groups
2030 sub retrieve_groups_tree {
2031 my( $self, $client ) = @_;
2032 return new_editor()->search_permission_grp_tree(
2037 flesh_fields => { pgt => ["children"] },
2038 order_by => { pgt => 'name'}
2045 # turns an org list into an org tree
2047 sub build_group_tree {
2049 my( $self, $grplist) = @_;
2051 return $grplist unless (
2052 ref($grplist) and @$grplist > 1 );
2054 my @list = sort { $a->name cmp $b->name } @$grplist;
2057 for my $grp (@list) {
2059 if ($grp and !defined($grp->parent)) {
2063 my ($parent) = grep { $_->id == $grp->parent} @list;
2065 $parent->children([]) unless defined($parent->children);
2066 push( @{$parent->children}, $grp );
2074 __PACKAGE__->register_method(
2075 method => "add_user_to_groups",
2076 api_name => "open-ils.actor.user.set_groups",
2077 notes => <<" NOTES");
2078 Adds a user to one or more permission groups
2081 sub add_user_to_groups {
2082 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2084 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2085 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2086 return $evt if $evt;
2088 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2089 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2090 return $evt if $evt;
2092 $apputils->simplereq(
2094 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2096 for my $group (@$groups) {
2097 my $link = Fieldmapper::permission::usr_grp_map->new;
2099 $link->usr($userid);
2101 my $id = $apputils->simplereq(
2103 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2109 __PACKAGE__->register_method(
2110 method => "get_user_perm_groups",
2111 api_name => "open-ils.actor.user.get_groups",
2112 notes => <<" NOTES");
2113 Retrieve a user's permission groups.
2117 sub get_user_perm_groups {
2118 my( $self, $client, $authtoken, $userid ) = @_;
2120 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2121 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2122 return $evt if $evt;
2124 return $apputils->simplereq(
2126 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2131 __PACKAGE__->register_method (
2132 method => 'register_workstation',
2133 api_name => 'open-ils.actor.workstation.register.override',
2134 signature => q/@see open-ils.actor.workstation.register/);
2136 __PACKAGE__->register_method (
2137 method => 'register_workstation',
2138 api_name => 'open-ils.actor.workstation.register',
2140 Registers a new workstion in the system
2141 @param authtoken The login session key
2142 @param name The name of the workstation id
2143 @param owner The org unit that owns this workstation
2144 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2145 if the name is already in use.
2148 sub _register_workstation {
2149 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2150 my( $requestor, $evt ) = $U->checkses($authtoken);
2151 return $evt if $evt;
2152 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2153 return $evt if $evt;
2155 my $ws = $U->cstorereq(
2156 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2157 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2159 $ws = Fieldmapper::actor::workstation->new;
2160 $ws->owning_lib($owner);
2163 my $id = $U->storagereq(
2164 'open-ils.storage.direct.actor.workstation.create', $ws );
2165 return $U->DB_UPDATE_FAILED($ws) unless $id;
2171 sub register_workstation {
2172 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2174 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2175 return $e->event unless $e->checkauth;
2176 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2177 my $existing = $e->search_actor_workstation({name => $name});
2180 if( $self->api_name =~ /override/o ) {
2181 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2182 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2184 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2188 my $ws = Fieldmapper::actor::workstation->new;
2189 $ws->owning_lib($owner);
2191 $e->create_actor_workstation($ws) or return $e->event;
2193 return $ws->id; # note: editor sets the id on the new object for us
2197 __PACKAGE__->register_method (
2198 method => 'fetch_patron_note',
2199 api_name => 'open-ils.actor.note.retrieve.all',
2201 Returns a list of notes for a given user
2202 Requestor must have VIEW_USER permission if pub==false and
2203 @param authtoken The login session key
2204 @param args Hash of params including
2205 patronid : the patron's id
2206 pub : true if retrieving only public notes
2210 sub fetch_patron_note {
2211 my( $self, $conn, $authtoken, $args ) = @_;
2212 my $patronid = $$args{patronid};
2214 my($reqr, $evt) = $U->checkses($authtoken);
2217 ($patron, $evt) = $U->fetch_user($patronid);
2218 return $evt if $evt;
2221 if( $patronid ne $reqr->id ) {
2222 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2223 return $evt if $evt;
2225 return $U->cstorereq(
2226 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2227 { usr => $patronid, pub => 't' } );
2230 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2231 return $evt if $evt;
2233 return $U->cstorereq(
2234 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2237 __PACKAGE__->register_method (
2238 method => 'create_user_note',
2239 api_name => 'open-ils.actor.note.create',
2241 Creates a new note for the given user
2242 @param authtoken The login session key
2243 @param note The note object
2246 sub create_user_note {
2247 my( $self, $conn, $authtoken, $note ) = @_;
2248 my( $reqr, $patron, $evt ) =
2249 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2250 return $evt if $evt;
2251 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2253 $note->creator($reqr->id);
2254 my $id = $U->storagereq(
2255 'open-ils.storage.direct.actor.usr_note.create', $note );
2256 return $U->DB_UPDATE_FAILED($note) unless $id;
2261 __PACKAGE__->register_method (
2262 method => 'delete_user_note',
2263 api_name => 'open-ils.actor.note.delete',
2265 Deletes a note for the given user
2266 @param authtoken The login session key
2267 @param noteid The note id
2270 sub delete_user_note {
2271 my( $self, $conn, $authtoken, $noteid ) = @_;
2273 my $note = $U->cstorereq(
2274 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2275 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2277 my( $reqr, $patron, $evt ) =
2278 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2279 return $evt if $evt;
2280 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2282 my $stat = $U->storagereq(
2283 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2284 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2289 __PACKAGE__->register_method (
2290 method => 'update_user_note',
2291 api_name => 'open-ils.actor.note.update',
2293 @param authtoken The login session key
2294 @param note The note
2298 sub update_user_note {
2299 my( $self, $conn, $auth, $note ) = @_;
2300 my $e = new_editor(authtoken=>$auth, xact=>1);
2301 return $e->event unless $e->checkauth;
2302 my $patron = $e->retrieve_actor_user($note->usr)
2303 or return $e->event;
2304 return $e->event unless
2305 $e->allowed('UPDATE_USER', $patron->home_ou);
2306 $e->update_actor_user_note($note)
2307 or return $e->event;
2315 __PACKAGE__->register_method (
2316 method => 'create_closed_date',
2317 api_name => 'open-ils.actor.org_unit.closed_date.create',
2319 Creates a new closing entry for the given org_unit
2320 @param authtoken The login session key
2321 @param note The closed_date object
2324 sub create_closed_date {
2325 my( $self, $conn, $authtoken, $cd ) = @_;
2327 my( $user, $evt ) = $U->checkses($authtoken);
2328 return $evt if $evt;
2330 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2331 return $evt if $evt;
2333 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2335 my $id = $U->storagereq(
2336 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2337 return $U->DB_UPDATE_FAILED($cd) unless $id;
2342 __PACKAGE__->register_method (
2343 method => 'delete_closed_date',
2344 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2346 Deletes a closing entry for the given org_unit
2347 @param authtoken The login session key
2348 @param noteid The close_date id
2351 sub delete_closed_date {
2352 my( $self, $conn, $authtoken, $cd ) = @_;
2354 my( $user, $evt ) = $U->checkses($authtoken);
2355 return $evt if $evt;
2358 ($cd_obj, $evt) = fetch_closed_date($cd);
2359 return $evt if $evt;
2361 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2362 return $evt if $evt;
2364 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2366 my $stat = $U->storagereq(
2367 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2368 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2373 __PACKAGE__->register_method(
2374 method => 'usrname_exists',
2375 api_name => 'open-ils.actor.username.exists',
2377 Returns 1 if the requested username exists, returns 0 otherwise
2381 sub usrname_exists {
2382 my( $self, $conn, $auth, $usrname ) = @_;
2383 my $e = new_editor(authtoken=>$auth);
2384 return $e->event unless $e->checkauth;
2385 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2386 return $$a[0] if $a and @$a;
2390 __PACKAGE__->register_method(
2391 method => 'barcode_exists',
2392 api_name => 'open-ils.actor.barcode.exists',
2394 Returns 1 if the requested barcode exists, returns 0 otherwise
2398 sub barcode_exists {
2399 my( $self, $conn, $auth, $barcode ) = @_;
2400 my $e = new_editor(authtoken=>$auth);
2401 return $e->event unless $e->checkauth;
2402 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2403 return $$a[0] if $a and @$a;
2408 __PACKAGE__->register_method(
2409 method => 'retrieve_net_levels',
2410 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2413 sub retrieve_net_levels {
2414 my( $self, $conn, $auth ) = @_;
2415 my $e = new_editor(authtoken=>$auth);
2416 return $e->event unless $e->checkauth;
2417 return $e->retrieve_all_config_net_access_level();
2421 __PACKAGE__->register_method(
2422 method => 'fetch_org_by_shortname',
2423 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2425 sub fetch_org_by_shortname {
2426 my( $self, $conn, $sname ) = @_;
2427 my $e = new_editor();
2428 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2429 return $e->event unless $org;
2434 __PACKAGE__->register_method(
2435 method => 'session_home_lib',
2436 api_name => 'open-ils.actor.session.home_lib',
2439 sub session_home_lib {
2440 my( $self, $conn, $auth ) = @_;
2441 my $e = new_editor(authtoken=>$auth);
2442 return undef unless $e->checkauth;
2443 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2444 return $org->shortname;
2449 __PACKAGE__->register_method(
2450 method => 'slim_tree',
2451 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2454 my $tree = new_editor()->search_actor_org_unit(
2456 {"parent_ou" => undef },
2459 flesh_fields => { aou => ['children'] },
2460 order_by => { aou => 'name'},
2461 select => { aou => ["id","shortname", "name"]},
2466 return trim_tree($tree);
2472 return undef unless $tree;
2474 code => $tree->shortname,
2475 name => $tree->name,
2477 if( $tree->children and @{$tree->children} ) {
2478 $htree->{children} = [];
2479 for my $c (@{$tree->children}) {
2480 push( @{$htree->{children}}, trim_tree($c) );
2489 __PACKAGE__->register_method(
2490 method => "user_retrieve_fleshed_by_id",
2491 api_name => "open-ils.actor.user.fleshed.retrieve",);
2493 sub user_retrieve_fleshed_by_id {
2494 my( $self, $client, $auth, $user_id, $fields ) = @_;
2495 my $e = new_editor(authtoken => $auth);
2496 return $e->event unless $e->checkauth;
2497 if( $e->requestor->id != $user_id ) {
2498 return $e->event unless $e->allowed('VIEW_USER');
2503 "standing_penalties",
2507 "stat_cat_entries" ];
2508 return new_flesh_user($user_id, $fields, $e);
2512 sub new_flesh_user {
2515 my $fields = shift || [];
2516 my $e = shift || new_editor(xact=>1);
2518 my $user = $e->retrieve_actor_user(
2523 "flesh_fields" => { "au" => $fields }
2526 ) or return $e->event;
2529 if( grep { $_ eq 'addresses' } @$fields ) {
2531 $user->addresses([]) unless @{$user->addresses};
2533 if( ref $user->billing_address ) {
2534 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2535 push( @{$user->addresses}, $user->billing_address );
2539 if( ref $user->mailing_address ) {
2540 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2541 push( @{$user->addresses}, $user->mailing_address );
2547 $user->clear_passwd();
2554 __PACKAGE__->register_method(
2555 method => "user_retrieve_parts",
2556 api_name => "open-ils.actor.user.retrieve.parts",);
2558 sub user_retrieve_parts {
2559 my( $self, $client, $auth, $user_id, $fields ) = @_;
2560 my $e = new_editor(authtoken => $auth);
2561 return $e->event unless $e->checkauth;
2562 if( $e->requestor->id != $user_id ) {
2563 return $e->event unless $e->allowed('VIEW_USER');
2566 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2567 push(@resp, $user->$_()) for(@$fields);