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,
1681 push( @$circs, @$mcircs );
1683 my $parser = DateTime::Format::ISO8601->new;
1685 # split the circs up into overdue and not-overdue circs
1687 for my $c (@$circs) {
1688 if( $c->due_date ) {
1689 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1690 my $due = $due_dt->epoch;
1691 if ($due < DateTime->today->epoch) {
1692 push @overdue, $c->id;
1701 # grab all of the lost, claims-returned, and longoverdue circs
1702 #my $open = $e->search_action_circulation(
1703 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1706 # these items have stop_fines, but no xact_finish, so money
1707 # is owed on them and they have not been checked in
1708 my $open = $e->search_action_circulation(
1711 stop_fines => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] },
1712 xact_finish => undef,
1713 checkin_time => undef,
1718 my( @lost, @cr, @lo );
1719 for my $c (@$open) {
1720 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1721 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1722 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1728 total => @$circs + @lost + @cr + @lo,
1729 out => scalar(@out),
1730 overdue => scalar(@overdue),
1731 lost => scalar(@lost),
1732 claims_returned => scalar(@cr),
1733 long_overdue => scalar(@lo)
1739 overdue => \@overdue,
1741 claims_returned => \@cr,
1742 long_overdue => \@lo
1748 __PACKAGE__->register_method(
1749 method => "checked_in_with_fines",
1750 api_name => "open-ils.actor.user.checked_in_with_fines",
1752 signature => q/@see open-ils.actor.user.checked_out/
1754 sub checked_in_with_fines {
1755 my( $self, $conn, $auth, $userid ) = @_;
1757 my $e = new_editor(authtoken=>$auth);
1758 return $e->event unless $e->checkauth;
1760 if( $userid ne $e->requestor->id ) {
1761 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1764 # money is owed on these items and they are checked in
1765 my $open = $e->search_action_circulation(
1768 xact_finish => undef,
1769 checkin_time => { "!=" => undef },
1774 my( @lost, @cr, @lo );
1775 for my $c (@$open) {
1776 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1777 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1778 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1783 claims_returned => \@cr,
1784 long_overdue => \@lo
1796 __PACKAGE__->register_method(
1797 method => "user_transaction_history",
1798 api_name => "open-ils.actor.user.transactions.history",
1800 notes => <<" NOTES");
1801 Returns a list of billable transaction ids for a user, optionally by type
1803 __PACKAGE__->register_method(
1804 method => "user_transaction_history",
1805 api_name => "open-ils.actor.user.transactions.history.have_charge",
1807 notes => <<" NOTES");
1808 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1810 __PACKAGE__->register_method(
1811 method => "user_transaction_history",
1812 api_name => "open-ils.actor.user.transactions.history.have_balance",
1814 notes => <<" NOTES");
1815 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1817 __PACKAGE__->register_method(
1818 method => "user_transaction_history",
1819 api_name => "open-ils.actor.user.transactions.history.still_open",
1821 notes => <<" NOTES");
1822 Returns a list of billable transaction ids for a user that are not finished
1824 __PACKAGE__->register_method(
1825 method => "user_transaction_history",
1826 api_name => "open-ils.actor.user.transactions.history.have_bill",
1828 notes => <<" NOTES");
1829 Returns a list of billable transaction ids for a user that has billings
1835 sub _user_transaction_history {
1836 my( $self, $client, $login_session, $user_id, $type ) = @_;
1838 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1839 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1840 return $evt if $evt;
1842 my $api = $self->api_name();
1847 @xact = (xact_type => $type) if(defined($type));
1848 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1849 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1851 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1853 my $trans = $apputils->simple_scalar_request(
1855 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1856 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1858 return [ map { $_->id } @$trans ];
1866 for my $x (@xacts) {
1867 my $s = new Fieldmapper::money::billable_transaction_summary;
1870 $s->xact_start( $x->xact_start );
1871 $s->xact_finish( $x->xact_finish );
1875 for my $b (@{ $x->billings }) {
1876 next if ($U->is_true($b->voided));
1877 $to += int($b->amount * 100);
1878 $lb ||= $b->billing_ts;
1879 if ($b->billing_ts ge $lb) {
1880 $lb = $b->billing_ts;
1881 $s->last_billing_note($b->note);
1882 $s->last_billing_ts($b->billing_ts);
1883 $s->last_billing_type($b->billing_type);
1887 $s->total_owed( sprintf('%0.2f', $to / 100 ) );
1891 for my $p (@{ $x->payments }) {
1892 next if ($U->is_true($p->voided));
1893 $tp += int($p->amount * 100);
1894 $lp ||= $p->payment_ts;
1895 if ($p->payment_ts ge $lp) {
1896 $lp = $p->payment_ts;
1897 $s->last_payment_note($p->note);
1898 $s->last_payment_ts($p->payment_ts);
1899 $s->last_payment_type($p->payment_type);
1902 $s->total_paid( sprintf('%0.2f', $tp / 100 ) );
1904 $s->balance_owed( sprintf('%0.2f', int($to - $tp) / 100) );
1906 $s->xact_type( 'grocery' ) if ($x->grocery);
1907 $s->xact_type( 'circulation' ) if ($x->circulation);
1915 sub user_transaction_history {
1916 my( $self, $conn, $auth, $userid, $type ) = @_;
1917 my $e = new_editor(authtoken=>$auth);
1918 return $e->event unless $e->checkauth;
1919 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1921 my $api = $self->api_name;
1922 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
1924 my @xacts = @{ $e->search_money_billable_transaction(
1925 [ { usr => $userid, @xact_finish },
1927 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
1928 order_by => { mbt => 'xact_start DESC' },
1933 my @mbts = _make_mbts( @xacts );
1935 if(defined($type)) {
1936 @mbts = grep { $_->xact_type eq $type } @mbts;
1939 if($api =~ /have_balance/o) {
1940 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
1943 if($api =~ /have_charge/o) {
1944 @mbts = grep { defined($_->last_billing_ts) } @mbts;
1947 if($api =~ /have_bill/o) {
1948 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
1956 __PACKAGE__->register_method(
1957 method => "user_perms",
1958 api_name => "open-ils.actor.permissions.user_perms.retrieve",
1960 notes => <<" NOTES");
1961 Returns a list of permissions
1964 my( $self, $client, $authtoken, $user ) = @_;
1966 my( $staff, $evt ) = $apputils->checkses($authtoken);
1967 return $evt if $evt;
1969 $user ||= $staff->id;
1971 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1975 return $apputils->simple_scalar_request(
1977 "open-ils.storage.permission.user_perms.atomic",
1981 __PACKAGE__->register_method(
1982 method => "retrieve_perms",
1983 api_name => "open-ils.actor.permissions.retrieve",
1984 notes => <<" NOTES");
1985 Returns a list of permissions
1987 sub retrieve_perms {
1988 my( $self, $client ) = @_;
1989 return $apputils->simple_scalar_request(
1991 "open-ils.cstore.direct.permission.perm_list.search.atomic",
1992 { id => { '!=' => undef } }
1996 __PACKAGE__->register_method(
1997 method => "retrieve_groups",
1998 api_name => "open-ils.actor.groups.retrieve",
1999 notes => <<" NOTES");
2000 Returns a list of user groupss
2002 sub retrieve_groups {
2003 my( $self, $client ) = @_;
2004 return new_editor()->retrieve_all_permission_grp_tree();
2007 __PACKAGE__->register_method(
2008 method => "retrieve_org_address",
2009 api_name => "open-ils.actor.org_unit.address.retrieve",
2010 notes => <<' NOTES');
2011 Returns an org_unit address by ID
2012 @param An org_address ID
2014 sub retrieve_org_address {
2015 my( $self, $client, $id ) = @_;
2016 return $apputils->simple_scalar_request(
2018 "open-ils.cstore.direct.actor.org_address.retrieve",
2023 __PACKAGE__->register_method(
2024 method => "retrieve_groups_tree",
2025 api_name => "open-ils.actor.groups.tree.retrieve",
2026 notes => <<" NOTES");
2027 Returns a list of user groups
2029 sub retrieve_groups_tree {
2030 my( $self, $client ) = @_;
2031 return new_editor()->search_permission_grp_tree(
2036 flesh_fields => { pgt => ["children"] },
2037 order_by => { pgt => 'name'}
2044 # turns an org list into an org tree
2046 sub build_group_tree {
2048 my( $self, $grplist) = @_;
2050 return $grplist unless (
2051 ref($grplist) and @$grplist > 1 );
2053 my @list = sort { $a->name cmp $b->name } @$grplist;
2056 for my $grp (@list) {
2058 if ($grp and !defined($grp->parent)) {
2062 my ($parent) = grep { $_->id == $grp->parent} @list;
2064 $parent->children([]) unless defined($parent->children);
2065 push( @{$parent->children}, $grp );
2073 __PACKAGE__->register_method(
2074 method => "add_user_to_groups",
2075 api_name => "open-ils.actor.user.set_groups",
2076 notes => <<" NOTES");
2077 Adds a user to one or more permission groups
2080 sub add_user_to_groups {
2081 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2083 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2084 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2085 return $evt if $evt;
2087 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2088 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2089 return $evt if $evt;
2091 $apputils->simplereq(
2093 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2095 for my $group (@$groups) {
2096 my $link = Fieldmapper::permission::usr_grp_map->new;
2098 $link->usr($userid);
2100 my $id = $apputils->simplereq(
2102 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2108 __PACKAGE__->register_method(
2109 method => "get_user_perm_groups",
2110 api_name => "open-ils.actor.user.get_groups",
2111 notes => <<" NOTES");
2112 Retrieve a user's permission groups.
2116 sub get_user_perm_groups {
2117 my( $self, $client, $authtoken, $userid ) = @_;
2119 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2120 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2121 return $evt if $evt;
2123 return $apputils->simplereq(
2125 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2130 __PACKAGE__->register_method (
2131 method => 'register_workstation',
2132 api_name => 'open-ils.actor.workstation.register.override',
2133 signature => q/@see open-ils.actor.workstation.register/);
2135 __PACKAGE__->register_method (
2136 method => 'register_workstation',
2137 api_name => 'open-ils.actor.workstation.register',
2139 Registers a new workstion in the system
2140 @param authtoken The login session key
2141 @param name The name of the workstation id
2142 @param owner The org unit that owns this workstation
2143 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2144 if the name is already in use.
2147 sub _register_workstation {
2148 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2149 my( $requestor, $evt ) = $U->checkses($authtoken);
2150 return $evt if $evt;
2151 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2152 return $evt if $evt;
2154 my $ws = $U->cstorereq(
2155 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2156 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2158 $ws = Fieldmapper::actor::workstation->new;
2159 $ws->owning_lib($owner);
2162 my $id = $U->storagereq(
2163 'open-ils.storage.direct.actor.workstation.create', $ws );
2164 return $U->DB_UPDATE_FAILED($ws) unless $id;
2170 sub register_workstation {
2171 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2173 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2174 return $e->event unless $e->checkauth;
2175 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2176 my $existing = $e->search_actor_workstation({name => $name});
2179 if( $self->api_name =~ /override/o ) {
2180 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2181 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2183 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2187 my $ws = Fieldmapper::actor::workstation->new;
2188 $ws->owning_lib($owner);
2190 $e->create_actor_workstation($ws) or return $e->event;
2192 return $ws->id; # note: editor sets the id on the new object for us
2196 __PACKAGE__->register_method (
2197 method => 'fetch_patron_note',
2198 api_name => 'open-ils.actor.note.retrieve.all',
2200 Returns a list of notes for a given user
2201 Requestor must have VIEW_USER permission if pub==false and
2202 @param authtoken The login session key
2203 @param args Hash of params including
2204 patronid : the patron's id
2205 pub : true if retrieving only public notes
2209 sub fetch_patron_note {
2210 my( $self, $conn, $authtoken, $args ) = @_;
2211 my $patronid = $$args{patronid};
2213 my($reqr, $evt) = $U->checkses($authtoken);
2216 ($patron, $evt) = $U->fetch_user($patronid);
2217 return $evt if $evt;
2220 if( $patronid ne $reqr->id ) {
2221 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2222 return $evt if $evt;
2224 return $U->cstorereq(
2225 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2226 { usr => $patronid, pub => 't' } );
2229 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2230 return $evt if $evt;
2232 return $U->cstorereq(
2233 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2236 __PACKAGE__->register_method (
2237 method => 'create_user_note',
2238 api_name => 'open-ils.actor.note.create',
2240 Creates a new note for the given user
2241 @param authtoken The login session key
2242 @param note The note object
2245 sub create_user_note {
2246 my( $self, $conn, $authtoken, $note ) = @_;
2247 my( $reqr, $patron, $evt ) =
2248 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2249 return $evt if $evt;
2250 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2252 $note->creator($reqr->id);
2253 my $id = $U->storagereq(
2254 'open-ils.storage.direct.actor.usr_note.create', $note );
2255 return $U->DB_UPDATE_FAILED($note) unless $id;
2260 __PACKAGE__->register_method (
2261 method => 'delete_user_note',
2262 api_name => 'open-ils.actor.note.delete',
2264 Deletes a note for the given user
2265 @param authtoken The login session key
2266 @param noteid The note id
2269 sub delete_user_note {
2270 my( $self, $conn, $authtoken, $noteid ) = @_;
2272 my $note = $U->cstorereq(
2273 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2274 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2276 my( $reqr, $patron, $evt ) =
2277 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2278 return $evt if $evt;
2279 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2281 my $stat = $U->storagereq(
2282 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2283 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2288 __PACKAGE__->register_method (
2289 method => 'update_user_note',
2290 api_name => 'open-ils.actor.note.update',
2292 @param authtoken The login session key
2293 @param note The note
2297 sub update_user_note {
2298 my( $self, $conn, $auth, $note ) = @_;
2299 my $e = new_editor(authtoken=>$auth, xact=>1);
2300 return $e->event unless $e->checkauth;
2301 my $patron = $e->retrieve_actor_user($note->usr)
2302 or return $e->event;
2303 return $e->event unless
2304 $e->allowed('UPDATE_USER', $patron->home_ou);
2305 $e->update_actor_user_note($note)
2306 or return $e->event;
2314 __PACKAGE__->register_method (
2315 method => 'create_closed_date',
2316 api_name => 'open-ils.actor.org_unit.closed_date.create',
2318 Creates a new closing entry for the given org_unit
2319 @param authtoken The login session key
2320 @param note The closed_date object
2323 sub create_closed_date {
2324 my( $self, $conn, $authtoken, $cd ) = @_;
2326 my( $user, $evt ) = $U->checkses($authtoken);
2327 return $evt if $evt;
2329 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2330 return $evt if $evt;
2332 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2334 my $id = $U->storagereq(
2335 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2336 return $U->DB_UPDATE_FAILED($cd) unless $id;
2341 __PACKAGE__->register_method (
2342 method => 'delete_closed_date',
2343 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2345 Deletes a closing entry for the given org_unit
2346 @param authtoken The login session key
2347 @param noteid The close_date id
2350 sub delete_closed_date {
2351 my( $self, $conn, $authtoken, $cd ) = @_;
2353 my( $user, $evt ) = $U->checkses($authtoken);
2354 return $evt if $evt;
2357 ($cd_obj, $evt) = fetch_closed_date($cd);
2358 return $evt if $evt;
2360 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2361 return $evt if $evt;
2363 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2365 my $stat = $U->storagereq(
2366 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2367 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2372 __PACKAGE__->register_method(
2373 method => 'usrname_exists',
2374 api_name => 'open-ils.actor.username.exists',
2376 Returns 1 if the requested username exists, returns 0 otherwise
2380 sub usrname_exists {
2381 my( $self, $conn, $auth, $usrname ) = @_;
2382 my $e = new_editor(authtoken=>$auth);
2383 return $e->event unless $e->checkauth;
2384 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2385 return $$a[0] if $a and @$a;
2389 __PACKAGE__->register_method(
2390 method => 'barcode_exists',
2391 api_name => 'open-ils.actor.barcode.exists',
2393 Returns 1 if the requested barcode exists, returns 0 otherwise
2397 sub barcode_exists {
2398 my( $self, $conn, $auth, $barcode ) = @_;
2399 my $e = new_editor(authtoken=>$auth);
2400 return $e->event unless $e->checkauth;
2401 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2402 return $$a[0] if $a and @$a;
2407 __PACKAGE__->register_method(
2408 method => 'retrieve_net_levels',
2409 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2412 sub retrieve_net_levels {
2413 my( $self, $conn, $auth ) = @_;
2414 my $e = new_editor(authtoken=>$auth);
2415 return $e->event unless $e->checkauth;
2416 return $e->retrieve_all_config_net_access_level();
2420 __PACKAGE__->register_method(
2421 method => 'fetch_org_by_shortname',
2422 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2424 sub fetch_org_by_shortname {
2425 my( $self, $conn, $sname ) = @_;
2426 my $e = new_editor();
2427 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2428 return $e->event unless $org;
2433 __PACKAGE__->register_method(
2434 method => 'session_home_lib',
2435 api_name => 'open-ils.actor.session.home_lib',
2438 sub session_home_lib {
2439 my( $self, $conn, $auth ) = @_;
2440 my $e = new_editor(authtoken=>$auth);
2441 return undef unless $e->checkauth;
2442 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2443 return $org->shortname;
2448 __PACKAGE__->register_method(
2449 method => 'slim_tree',
2450 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2453 my $tree = new_editor()->search_actor_org_unit(
2455 {"parent_ou" => undef },
2458 flesh_fields => { aou => ['children'] },
2459 order_by => { aou => 'name'},
2460 select => { aou => ["id","shortname", "name"]},
2465 return trim_tree($tree);
2471 return undef unless $tree;
2473 code => $tree->shortname,
2474 name => $tree->name,
2476 if( $tree->children and @{$tree->children} ) {
2477 $htree->{children} = [];
2478 for my $c (@{$tree->children}) {
2479 push( @{$htree->{children}}, trim_tree($c) );
2488 __PACKAGE__->register_method(
2489 method => "user_retrieve_fleshed_by_id",
2490 api_name => "open-ils.actor.user.fleshed.retrieve",);
2492 sub user_retrieve_fleshed_by_id {
2493 my( $self, $client, $auth, $user_id, $fields ) = @_;
2494 my $e = new_editor(authtoken => $auth);
2495 return $e->event unless $e->checkauth;
2496 if( $e->requestor->id != $user_id ) {
2497 return $e->event unless $e->allowed('VIEW_USER');
2502 "standing_penalties",
2506 "stat_cat_entries" ];
2507 return new_flesh_user($user_id, $fields, $e);
2511 sub new_flesh_user {
2514 my $fields = shift || [];
2515 my $e = shift || new_editor(xact=>1);
2517 my $user = $e->retrieve_actor_user(
2522 "flesh_fields" => { "au" => $fields }
2525 ) or return $e->event;
2528 if( grep { $_ eq 'addresses' } @$fields ) {
2530 $user->addresses([]) unless @{$user->addresses};
2532 if( ref $user->billing_address ) {
2533 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2534 push( @{$user->addresses}, $user->billing_address );
2538 if( ref $user->mailing_address ) {
2539 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2540 push( @{$user->addresses}, $user->mailing_address );
2546 $user->clear_passwd();
2553 __PACKAGE__->register_method(
2554 method => "user_retrieve_parts",
2555 api_name => "open-ils.actor.user.retrieve.parts",);
2557 sub user_retrieve_parts {
2558 my( $self, $client, $auth, $user_id, $fields ) = @_;
2559 my $e = new_editor(authtoken => $auth);
2560 return $e->event unless $e->checkauth;
2561 if( $e->requestor->id != $user_id ) {
2562 return $e->event unless $e->allowed('VIEW_USER');
2565 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2566 push(@resp, $user->$_()) for(@$fields);