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;
26 use OpenILS::Application::Actor::Container;
27 use OpenILS::Application::Actor::ClosedDates;
29 use OpenILS::Utils::CStoreEditor qw/:funcs/;
31 use OpenILS::Application::Actor::UserGroups;
33 OpenILS::Application::Actor::Container->initialize();
34 OpenILS::Application::Actor::UserGroups->initialize();
35 OpenILS::Application::Actor::ClosedDates->initialize();
38 my $apputils = "OpenILS::Application::AppUtils";
41 sub _d { warn "Patron:\n" . Dumper(shift()); }
46 my $set_user_settings;
49 __PACKAGE__->register_method(
50 method => "set_user_settings",
51 api_name => "open-ils.actor.patron.settings.update",
53 sub set_user_settings {
54 my( $self, $client, $user_session, $uid, $settings ) = @_;
56 $logger->debug("Setting user settings: $user_session, $uid, " . Dumper($settings));
58 my( $staff, $user, $evt ) =
59 $apputils->checkses_requestor( $user_session, $uid, 'UPDATE_USER' );
63 [{ usr => $user->id, name => $_}, {value => $$settings{$_}}] } keys %$settings;
65 $_->[1]->{value} = JSON->perl2JSON($_->[1]->{value}) for @params;
67 $logger->activity("User " . $staff->id . " updating user $uid settings with: " . Dumper(\@params));
69 my $ses = $U->start_db_session();
70 my $stat = $ses->request(
71 'open-ils.storage.direct.actor.user_setting.batch.merge', @params )->gather(1);
72 $U->commit_db_session($ses);
79 __PACKAGE__->register_method(
80 method => "set_ou_settings",
81 api_name => "open-ils.actor.org_unit.settings.update",
84 my( $self, $client, $user_session, $ouid, $settings ) = @_;
86 my( $staff, $evt ) = $apputils->checkses( $user_session );
88 $evt = $apputils->check_perms( $staff->id, $ouid, 'UPDATE_ORG_SETTING' );
92 for my $set (keys %$settings) {
94 my $json = JSON->perl2JSON($$settings{$set});
95 $logger->activity("updating org_unit.setting: $ouid : $set : $json");
98 { org_unit => $ouid, name => $set },
102 my $ses = $U->start_db_session();
103 my $stat = $ses->request(
104 'open-ils.storage.direct.actor.org_unit_setting.merge', @params )->gather(1);
105 $U->commit_db_session($ses);
111 my $fetch_user_settings;
112 my $fetch_ou_settings;
114 __PACKAGE__->register_method(
115 method => "user_settings",
116 api_name => "open-ils.actor.patron.settings.retrieve",
119 my( $self, $client, $user_session, $uid ) = @_;
121 my( $staff, $user, $evt ) =
122 $apputils->checkses_requestor( $user_session, $uid, 'VIEW_USER' );
125 $logger->debug("User " . $staff->id . " fetching user $uid\n");
126 my $s = $apputils->simplereq(
128 'open-ils.cstore.direct.actor.user_setting.search.atomic', { usr => $uid } );
130 return { map { ( $_->name => JSON->JSON2perl($_->value) ) } @$s };
135 __PACKAGE__->register_method(
136 method => "ou_settings",
137 api_name => "open-ils.actor.org_unit.settings.retrieve",
140 my( $self, $client, $ouid ) = @_;
142 $logger->info("Fetching org unit settings for org $ouid");
144 my $s = $apputils->simplereq(
146 'open-ils.cstore.direct.actor.org_unit_setting.search.atomic', {org_unit => $ouid});
148 return { map { ( $_->name => JSON->JSON2perl($_->value) ) } @$s };
151 __PACKAGE__->register_method (
152 method => "ou_setting_delete",
153 api_name => 'open-ils.actor.org_setting.delete',
155 Deletes a specific org unit setting for a specific location
156 @param authtoken The login session key
157 @param orgid The org unit whose setting we're changing
158 @param setting The name of the setting to delete
159 @return True value on success.
163 sub ou_setting_delete {
164 my( $self, $conn, $authtoken, $orgid, $setting ) = @_;
165 my( $reqr, $evt) = $U->checkses($authtoken);
167 $evt = $U->check_perms($reqr->id, $orgid, 'UPDATE_ORG_SETTING');
170 my $id = $U->cstorereq(
171 'open-ils.cstore.direct.actor.org_unit_setting.id_list',
172 { name => $setting, org_unit => $orgid } );
174 $logger->debug("Retrieved setting $id in org unit setting delete");
176 my $s = $U->cstorereq(
177 'open-ils.cstore.direct.actor.org_unit_setting.delete', $id );
179 $logger->activity("User ".$reqr->id." deleted org unit setting $id") if $s;
185 __PACKAGE__->register_method(
186 method => "update_patron",
187 api_name => "open-ils.actor.patron.update",);
190 my( $self, $client, $user_session, $patron ) = @_;
192 my $session = $apputils->start_db_session();
195 $logger->info("Creating new patron...") if $patron->isnew;
196 $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
198 my( $user_obj, $evt ) = $U->checkses($user_session);
201 # XXX does this user have permission to add/create users. Granularity?
202 # $new_patron is the patron in progress. $patron is the original patron
203 # passed in with the method. new_patron will change as the components
204 # of patron are added/updated.
208 # unflesh the real items on the patron
209 $patron->card( $patron->card->id ) if(ref($patron->card));
210 $patron->billing_address( $patron->billing_address->id )
211 if(ref($patron->billing_address));
212 $patron->mailing_address( $patron->mailing_address->id )
213 if(ref($patron->mailing_address));
215 # create/update the patron first so we can use his id
216 if($patron->isnew()) {
217 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
219 } else { $new_patron = $patron; }
221 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
224 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
227 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
230 # re-update the patron if anything has happened to him during this process
231 if($new_patron->ischanged()) {
232 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
236 #$session = OpenSRF::AppSession->create("open-ils.storage"); # why did i put this here?
238 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
241 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
244 ($new_patron, $evt) = _create_standing_penalties($session, $user_session, $patron, $new_patron, $user_obj);
247 $logger->activity("user ".$user_obj->id." updating/creating user ".$new_patron->id);
248 $apputils->commit_db_session($session);
250 #warn "Patron Update/Create complete\n";
251 return flesh_user($new_patron->id());
257 __PACKAGE__->register_method(
258 method => "user_retrieve_fleshed_by_id",
259 api_name => "open-ils.actor.user.fleshed.retrieve",);
261 sub user_retrieve_fleshed_by_id {
262 my( $self, $client, $user_session, $user_id ) = @_;
264 my( $requestor, $target, $evt ) = $apputils->
265 checkses_requestor( $user_session, $user_id, 'VIEW_USER' );
268 return flesh_user($user_id);
272 # fleshes: card, cards, address, addresses, stat_cat_entries, standing_penalties
281 $session = OpenSRF::AppSession->create("open-ils.storage");
285 # grab the user with the given id
286 my $ureq = $session->request(
287 "open-ils.storage.direct.actor.user.retrieve", $id);
288 my $user = $ureq->gather(1);
290 if(!$user) { return undef; }
293 my $cards_req = $session->request(
294 "open-ils.storage.direct.actor.card.search.usr.atomic",
296 $user->cards( $cards_req->gather(1) );
298 for my $c(@{$user->cards}) {
299 if($c->id == $user->card || $c->id eq $user->card ) {
300 #warn "Setting my card to " . $c->id . "\n";
305 my $add_req = $session->request(
306 "open-ils.storage.direct.actor.user_address.search.usr.atomic",
308 $user->addresses( $add_req->gather(1) );
310 if( @{$user->addresses} ) {
311 if( ! grep { $_->id eq $user->billing_address } @{$user->addresses} ) {
312 my $ba = $session->request(
313 'open-ils.storage.direct.actor.user_address.retrieve',
314 $user->billing_address)->gather(1);
315 push( @{$user->addresses}, $ba );
318 if( ! grep { $_->id eq $user->mailing_address } @{$user->addresses} ) {
319 my $ba = $session->request(
320 'open-ils.storage.direct.actor.user_address.retrieve',
321 $user->mailing_address)->gather(1);
322 push( @{$user->addresses}, $ba );
327 for my $c(@{$user->addresses}) {
328 if($c->id eq $user->billing_address ) { $user->billing_address($c); }
329 if($c->id eq $user->mailing_address ) { $user->mailing_address($c); }
332 my $stat_req = $session->request(
333 "open-ils.storage.direct.actor.stat_cat_entry_user_map.search.target_usr.atomic",
335 $user->stat_cat_entries($stat_req->gather(1));
337 my $standing_penalties_req = $session->request(
338 "open-ils.storage.direct.actor.user_standing_penalty.search.usr.atomic",
340 $user->standing_penalties($standing_penalties_req->gather(1));
342 if($kill) { $session->disconnect(); }
343 $user->clear_passwd();
350 my $e = new_editor();
351 my $user = $e->retrieve_actor_user(
360 "standing_penalties",
369 ) or return $e->event;
371 $user->clear_passwd();
380 # clone and clear stuff that would break the database
384 my $new_patron = $patron->clone;
386 # Using the Fieldmapper clone method
387 #my $new_patron = Fieldmapper::actor::user->new();
389 #my $fmap = $Fieldmapper::fieldmap;
390 #no strict; # shallow clone, may be useful in the fieldmapper
392 # (keys %{$fmap->{"Fieldmapper::actor::user"}->{'fields'}}) {
393 # $new_patron->$field( $patron->$field() );
398 $new_patron->clear_billing_address();
399 $new_patron->clear_mailing_address();
400 $new_patron->clear_addresses();
401 $new_patron->clear_card();
402 $new_patron->clear_cards();
403 $new_patron->clear_id();
404 $new_patron->clear_isnew();
405 $new_patron->clear_ischanged();
406 $new_patron->clear_isdeleted();
407 $new_patron->clear_stat_cat_entries();
408 $new_patron->clear_permissions();
409 $new_patron->clear_standing_penalties();
419 my $user_obj = shift;
421 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
422 return (undef, $evt) if $evt;
424 my $ex = $session->request(
425 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
427 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
430 $evt = _check_dup_ident($session, $patron);
431 return (undef, $evt) if $evt;
433 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
435 my $id = $session->request(
436 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
437 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
439 $logger->info("Successfully created new user [$id] in DB");
441 return ( $session->request(
442 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
447 my( $session, $patron, $user_obj, $noperm) = @_;
449 $logger->info("Updating patron ".$patron->id." in DB");
454 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
455 return (undef, $evt) if $evt;
458 # We can' check for dup idents on update because some existing
459 # users may already have dup idents
460 #$evt = _check_dup_ident($session, $patron);
461 #return (undef, $evt) if $evt;
464 # update the password by itself to avoid the password protection magic
465 if( $patron->passwd ) {
466 my $s = $session->request(
467 'open-ils.storage.direct.actor.user.remote_update',
468 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
469 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
470 $patron->clear_passwd;
473 if(!$patron->ident_type) {
474 $patron->clear_ident_type;
475 $patron->clear_ident_value;
478 if(!$patron->ident_type2) {
479 $patron->clear_ident_type2;
480 $patron->clear_ident_value2;
483 my $stat = $session->request(
484 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
485 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
490 sub _check_dup_ident {
491 my( $session, $patron ) = @_;
493 return undef unless $patron->ident_value;
496 ident_type => $patron->ident_type,
497 ident_value => $patron->ident_value,
500 $logger->debug("patron update searching for dup ident values: " .
501 $patron->ident_type . ':' . $patron->ident_value);
503 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
505 my $dups = $session->request(
506 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
509 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
516 sub _add_update_addresses {
520 my $new_patron = shift;
524 my $current_id; # id of the address before creation
526 for my $address (@{$patron->addresses()}) {
528 next unless ref $address;
529 $current_id = $address->id();
531 if( $patron->billing_address() and
532 $patron->billing_address() == $current_id ) {
533 $logger->info("setting billing addr to $current_id");
534 $new_patron->billing_address($address->id());
535 $new_patron->ischanged(1);
538 if( $patron->mailing_address() and
539 $patron->mailing_address() == $current_id ) {
540 $new_patron->mailing_address($address->id());
541 $logger->info("setting mailing addr to $current_id");
542 $new_patron->ischanged(1);
546 if($address->isnew()) {
548 $address->usr($new_patron->id());
550 ($address, $evt) = _add_address($session,$address);
551 return (undef, $evt) if $evt;
553 # we need to get the new id
554 if( $patron->billing_address() and
555 $patron->billing_address() == $current_id ) {
556 $new_patron->billing_address($address->id());
557 $logger->info("setting billing addr to $current_id");
558 $new_patron->ischanged(1);
561 if( $patron->mailing_address() and
562 $patron->mailing_address() == $current_id ) {
563 $new_patron->mailing_address($address->id());
564 $logger->info("setting mailing addr to $current_id");
565 $new_patron->ischanged(1);
568 } elsif($address->ischanged() ) {
570 ($address, $evt) = _update_address($session, $address);
571 return (undef, $evt) if $evt;
573 } elsif($address->isdeleted() ) {
575 if( $address->id() == $new_patron->mailing_address() ) {
576 $new_patron->clear_mailing_address();
577 ($new_patron, $evt) = _update_patron($session, $new_patron);
578 return (undef, $evt) if $evt;
581 if( $address->id() == $new_patron->billing_address() ) {
582 $new_patron->clear_billing_address();
583 ($new_patron, $evt) = _update_patron($session, $new_patron);
584 return (undef, $evt) if $evt;
587 $evt = _delete_address($session, $address);
588 return (undef, $evt) if $evt;
592 return ( $new_patron, undef );
596 # adds an address to the db and returns the address with new id
598 my($session, $address) = @_;
599 $address->clear_id();
601 $logger->info("Creating new address at street ".$address->street1);
603 # put the address into the database
604 my $id = $session->request(
605 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
606 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
609 return ($address, undef);
613 sub _update_address {
614 my( $session, $address ) = @_;
616 $logger->info("Updating address ".$address->id." in the DB");
618 my $stat = $session->request(
619 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
621 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
622 return ($address, undef);
627 sub _add_update_cards {
631 my $new_patron = shift;
635 my $virtual_id; #id of the card before creation
636 for my $card (@{$patron->cards()}) {
638 $card->usr($new_patron->id());
640 if(ref($card) and $card->isnew()) {
642 $virtual_id = $card->id();
643 ( $card, $evt ) = _add_card($session,$card);
644 return (undef, $evt) if $evt;
646 #if(ref($patron->card)) { $patron->card($patron->card->id); }
647 if($patron->card() == $virtual_id) {
648 $new_patron->card($card->id());
649 $new_patron->ischanged(1);
652 } elsif( ref($card) and $card->ischanged() ) {
653 $evt = _update_card($session, $card);
654 return (undef, $evt) if $evt;
658 return ( $new_patron, undef );
662 # adds an card to the db and returns the card with new id
664 my( $session, $card ) = @_;
667 $logger->info("Adding new patron card ".$card->barcode);
669 my $id = $session->request(
670 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
671 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
672 $logger->info("Successfully created patron card $id");
675 return ( $card, undef );
679 # returns event on error. returns undef otherwise
681 my( $session, $card ) = @_;
682 $logger->info("Updating patron card ".$card->id);
684 my $stat = $session->request(
685 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
686 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
693 # returns event on error. returns undef otherwise
694 sub _delete_address {
695 my( $session, $address ) = @_;
697 $logger->info("Deleting address ".$address->id." from DB");
699 my $stat = $session->request(
700 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
702 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
708 sub _add_survey_responses {
709 my ($session, $patron, $new_patron) = @_;
711 $logger->info( "Updating survey responses for patron ".$new_patron->id );
713 my $responses = $patron->survey_responses;
717 $_->usr($new_patron->id) for (@$responses);
719 my $evt = $U->simplereq( "open-ils.circ",
720 "open-ils.circ.survey.submit.user_id", $responses );
722 return (undef, $evt) if defined($U->event_code($evt));
726 return ( $new_patron, undef );
730 sub _create_stat_maps {
732 my($session, $user_session, $patron, $new_patron) = @_;
734 my $maps = $patron->stat_cat_entries();
736 for my $map (@$maps) {
738 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
740 if ($map->isdeleted()) {
741 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
743 } elsif ($map->isnew()) {
744 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
749 $map->target_usr($new_patron->id);
752 $logger->info("Updating stat entry with method $method and map $map");
754 my $stat = $session->request($method, $map)->gather(1);
755 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
759 return ($new_patron, undef);
762 sub _create_perm_maps {
764 my($session, $user_session, $patron, $new_patron) = @_;
766 my $maps = $patron->permissions;
768 for my $map (@$maps) {
770 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
771 if ($map->isdeleted()) {
772 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
773 } elsif ($map->isnew()) {
774 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
779 $map->usr($new_patron->id);
781 #warn( "Updating permissions with method $method and session $user_session and map $map" );
782 $logger->info( "Updating permissions with method $method and map $map" );
784 my $stat = $session->request($method, $map)->gather(1);
785 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
789 return ($new_patron, undef);
793 sub _create_standing_penalties {
795 my($session, $user_session, $patron, $new_patron) = @_;
797 my $maps = $patron->standing_penalties;
800 for my $map (@$maps) {
802 if ($map->isdeleted()) {
803 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
804 } elsif ($map->isnew()) {
805 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
811 $map->usr($new_patron->id);
813 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
815 my $stat = $session->request($method, $map)->gather(1);
816 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
819 return ($new_patron, undef);
824 __PACKAGE__->register_method(
825 method => "search_username",
826 api_name => "open-ils.actor.user.search.username",
829 sub search_username {
830 my($self, $client, $username) = @_;
831 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
833 "open-ils.cstore.direct.actor.user.search.atomic",
834 { usrname => $username }
842 __PACKAGE__->register_method(
843 method => "user_retrieve_by_barcode",
844 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
846 sub user_retrieve_by_barcode {
847 my($self, $client, $user_session, $barcode) = @_;
849 $logger->debug("Searching for user with barcode $barcode");
850 my ($user_obj, $evt) = $apputils->checkses($user_session);
853 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
855 "open-ils.cstore.direct.actor.card.search.atomic",
856 { barcode => $barcode }
859 if(!$card || !$card->[0]) {
860 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
864 my $user = flesh_user($card->usr());
866 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
869 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
876 __PACKAGE__->register_method(
877 method => "get_user_by_id",
878 api_name => "open-ils.actor.user.retrieve",);
881 my ($self, $client, $auth, $id) = @_;
882 my $e = new_editor(authtoken=>$auth);
883 return $e->event unless $e->checkauth;
884 my $user = $e->retrieve_actor_user($id)
886 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
892 __PACKAGE__->register_method(
893 method => "get_org_types",
894 api_name => "open-ils.actor.org_types.retrieve",);
898 my($self, $client) = @_;
899 return $org_types if $org_types;
900 return $org_types = new_editor()->retrieve_all_actor_org_unit_type();
905 __PACKAGE__->register_method(
906 method => "get_user_profiles",
907 api_name => "open-ils.actor.user.profiles.retrieve",
911 sub get_user_profiles {
912 return $user_profiles if $user_profiles;
914 return $user_profiles =
915 $apputils->simple_scalar_request(
917 "open-ils.cstore.direct.actor.profile.search.atomic", { id => { "!=" => undef }});
922 __PACKAGE__->register_method(
923 method => "get_user_ident_types",
924 api_name => "open-ils.actor.user.ident_types.retrieve",
927 sub get_user_ident_types {
928 return $ident_types if $ident_types;
929 return $ident_types =
930 new_editor()->retrieve_all_config_identification_type();
936 __PACKAGE__->register_method(
937 method => "get_org_unit",
938 api_name => "open-ils.actor.org_unit.retrieve",
942 my( $self, $client, $user_session, $org_id ) = @_;
943 my $e = new_editor(authtoken => $user_session);
945 return $e->event unless $e->checkauth;
946 $org_id = $e->requestor->ws_ou;
948 my $o = $e->retrieve_actor_org_unit($org_id)
953 __PACKAGE__->register_method(
954 method => "search_org_unit",
955 api_name => "open-ils.actor.org_unit_list.search",
958 sub search_org_unit {
960 my( $self, $client, $field, $value ) = @_;
962 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
964 "open-ils.cstore.direct.actor.org_unit.search.atomic",
965 { $field => $value } );
973 __PACKAGE__->register_method(
974 method => "get_org_tree",
975 api_name => "open-ils.actor.org_tree.retrieve",
977 note => "Returns the entire org tree structure",
981 my( $self, $client) = @_;
983 $cache = OpenSRF::Utils::Cache->new("global", 0) unless $cache;
984 my $tree = $cache->get_cache('orgtree');
985 return $tree if $tree;
987 $tree = new_editor()->search_actor_org_unit(
989 {"parent_ou" => undef },
992 flesh_fields => { aou => ['children'] },
993 order_by => { aou => 'name'}
998 $cache->put_cache('orgtree', $tree);
1003 # turns an org list into an org tree
1004 sub build_org_tree {
1006 my( $self, $orglist) = @_;
1008 return $orglist unless (
1009 ref($orglist) and @$orglist > 1 );
1012 $a->ou_type <=> $b->ou_type ||
1013 $a->name cmp $b->name } @$orglist;
1015 for my $org (@list) {
1017 next unless ($org and defined($org->parent_ou));
1018 my ($parent) = grep { $_->id == $org->parent_ou } @list;
1019 next unless $parent;
1021 $parent->children([]) unless defined($parent->children);
1022 push( @{$parent->children}, $org );
1030 __PACKAGE__->register_method(
1031 method => "get_org_descendants",
1032 api_name => "open-ils.actor.org_tree.descendants.retrieve"
1035 # depth is optional. org_unit is the id
1036 sub get_org_descendants {
1037 my( $self, $client, $org_unit, $depth ) = @_;
1038 my $orglist = $apputils->simple_scalar_request(
1040 "open-ils.storage.actor.org_unit.descendants.atomic",
1041 $org_unit, $depth );
1042 return $self->build_org_tree($orglist);
1046 __PACKAGE__->register_method(
1047 method => "get_org_ancestors",
1048 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
1051 # depth is optional. org_unit is the id
1052 sub get_org_ancestors {
1053 my( $self, $client, $org_unit, $depth ) = @_;
1054 my $orglist = $apputils->simple_scalar_request(
1056 "open-ils.storage.actor.org_unit.ancestors.atomic",
1057 $org_unit, $depth );
1058 return $self->build_org_tree($orglist);
1062 __PACKAGE__->register_method(
1063 method => "get_standings",
1064 api_name => "open-ils.actor.standings.retrieve"
1069 return $user_standings if $user_standings;
1070 return $user_standings =
1071 $apputils->simple_scalar_request(
1073 "open-ils.cstore.direct.config.standing.search.atomic",
1074 { id => { "!=" => undef } }
1080 __PACKAGE__->register_method(
1081 method => "get_my_org_path",
1082 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1085 sub get_my_org_path {
1086 my( $self, $client, $user_session, $org_id ) = @_;
1087 my $user_obj = $apputils->check_user_session($user_session);
1088 if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
1090 return $apputils->simple_scalar_request(
1092 "open-ils.storage.actor.org_unit.full_path.atomic",
1097 __PACKAGE__->register_method(
1098 method => "patron_adv_search",
1099 api_name => "open-ils.actor.patron.search.advanced" );
1100 sub patron_adv_search {
1101 my( $self, $client, $auth, $search_hash, $search_limit, $search_sort ) = @_;
1102 my $e = new_editor(authtoken=>$auth);
1103 return $e->event unless $e->checkauth;
1104 return $e->event unless $e->allowed('VIEW_USER');
1105 return $U->storagereq(
1106 "open-ils.storage.actor.user.crazy_search",
1107 $search_hash, $search_limit, $search_sort);
1112 sub _verify_password {
1113 my($user_session, $password) = @_;
1114 my $user_obj = $apputils->check_user_session($user_session);
1116 #grab the user with password
1117 $user_obj = $apputils->simple_scalar_request(
1119 "open-ils.cstore.direct.actor.user.retrieve",
1122 if($user_obj->passwd eq $password) {
1130 __PACKAGE__->register_method(
1131 method => "update_password",
1132 api_name => "open-ils.actor.user.password.update");
1134 __PACKAGE__->register_method(
1135 method => "update_password",
1136 api_name => "open-ils.actor.user.username.update");
1138 __PACKAGE__->register_method(
1139 method => "update_password",
1140 api_name => "open-ils.actor.user.email.update");
1142 sub update_password {
1143 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1147 my $user_obj = $apputils->check_user_session($user_session);
1149 if($self->api_name =~ /password/o) {
1151 #make sure they know the current password
1152 if(!_verify_password($user_session, md5_hex($current_password))) {
1153 return OpenILS::Event->new('INCORRECT_PASSWORD');
1156 $logger->debug("update_password setting new password $new_value");
1157 $user_obj->passwd($new_value);
1159 } elsif($self->api_name =~ /username/o) {
1160 my $users = search_username(undef, undef, $new_value);
1161 if( $users and $users->[0] ) {
1162 return OpenILS::Event->new('USERNAME_EXISTS');
1164 $user_obj->usrname($new_value);
1166 } elsif($self->api_name =~ /email/o) {
1167 #warn "Updating email to $new_value\n";
1168 $user_obj->email($new_value);
1171 my $session = $apputils->start_db_session();
1173 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1174 return $evt if $evt;
1176 $apputils->commit_db_session($session);
1178 if($user_obj) { return 1; }
1183 __PACKAGE__->register_method(
1184 method => "check_user_perms",
1185 api_name => "open-ils.actor.user.perm.check",
1186 notes => <<" NOTES");
1187 Takes a login session, user id, an org id, and an array of perm type strings. For each
1188 perm type, if the user does *not* have the given permission it is added
1189 to a list which is returned from the method. If all permissions
1190 are allowed, an empty list is returned
1191 if the logged in user does not match 'user_id', then the logged in user must
1192 have VIEW_PERMISSION priveleges.
1195 sub check_user_perms {
1196 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1198 my( $staff, $evt ) = $apputils->checkses($login_session);
1199 return $evt if $evt;
1201 if($staff->id ne $user_id) {
1202 if( $evt = $apputils->check_perms(
1203 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1209 for my $perm (@$perm_types) {
1210 if($apputils->check_perms($user_id, $org_id, $perm)) {
1211 push @not_allowed, $perm;
1215 return \@not_allowed
1218 __PACKAGE__->register_method(
1219 method => "check_user_perms2",
1220 api_name => "open-ils.actor.user.perm.check.multi_org",
1222 Checks the permissions on a list of perms and orgs for a user
1223 @param authtoken The login session key
1224 @param user_id The id of the user to check
1225 @param orgs The array of org ids
1226 @param perms The array of permission names
1227 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1228 if the logged in user does not match 'user_id', then the logged in user must
1229 have VIEW_PERMISSION priveleges.
1232 sub check_user_perms2 {
1233 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1235 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1236 $authtoken, $user_id, 'VIEW_PERMISSION' );
1237 return $evt if $evt;
1240 for my $org (@$orgs) {
1241 for my $perm (@$perms) {
1242 if($apputils->check_perms($user_id, $org, $perm)) {
1243 push @not_allowed, [ $org, $perm ];
1248 return \@not_allowed
1252 __PACKAGE__->register_method(
1253 method => 'check_user_perms3',
1254 api_name => 'open-ils.actor.user.perm.highest_org',
1256 Returns the highest org unit id at which a user has a given permission
1257 If the requestor does not match the target user, the requestor must have
1258 'VIEW_PERMISSION' rights at the home org unit of the target user
1259 @param authtoken The login session key
1260 @param userid The id of the user in question
1261 @param perm The permission to check
1262 @return The org unit highest in the org tree within which the user has
1263 the requested permission
1266 sub check_user_perms3 {
1267 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1269 my( $staff, $target, $org, $evt );
1271 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1272 $authtoken, $userid, 'VIEW_PERMISSION' );
1273 return $evt if $evt;
1275 my $tree = $self->get_org_tree();
1276 return _find_highest_perm_org( $perm, $userid, $target->ws_ou, $tree );
1280 sub _find_highest_perm_org {
1281 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1282 my $org = $apputils->find_org($org_tree, $start_org );
1286 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1288 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1294 __PACKAGE__->register_method(
1295 method => 'check_user_perms4',
1296 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1298 Returns the highest org unit id at which a user has a given permission
1299 If the requestor does not match the target user, the requestor must have
1300 'VIEW_PERMISSION' rights at the home org unit of the target user
1301 @param authtoken The login session key
1302 @param userid The id of the user in question
1303 @param perms An array of perm names to check
1304 @return An array of orgId's representing the org unit
1305 highest in the org tree within which the user has the requested permission
1306 The arrah of orgId's has matches the order of the perms array
1309 sub check_user_perms4 {
1310 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1312 my( $staff, $target, $org, $evt );
1314 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1315 $authtoken, $userid, 'VIEW_PERMISSION' );
1316 return $evt if $evt;
1319 return [] unless ref($perms);
1320 my $tree = $self->get_org_tree();
1322 for my $p (@$perms) {
1323 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1331 __PACKAGE__->register_method(
1332 method => "user_fines_summary",
1333 api_name => "open-ils.actor.user.fines.summary",
1334 notes => <<" NOTES");
1335 Returns a short summary of the users total open fines, excluding voided fines
1336 Params are login_session, user_id
1337 Returns a 'mous' object.
1340 sub user_fines_summary {
1341 my( $self, $client, $login_session, $user_id ) = @_;
1343 my $user_obj = $apputils->check_user_session($login_session);
1344 if($user_obj->id ne $user_id) {
1345 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1346 return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY");
1350 return $apputils->simple_scalar_request(
1352 "open-ils.cstore.direct.money.open_user_summary.search",
1353 { usr => $user_id } );
1360 __PACKAGE__->register_method(
1361 method => "user_transactions",
1362 api_name => "open-ils.actor.user.transactions",
1363 notes => <<" NOTES");
1364 Returns a list of open user transactions (mbts objects);
1365 Params are login_session, user_id
1366 Optional third parameter is the transactions type. defaults to all
1369 __PACKAGE__->register_method(
1370 method => "user_transactions",
1371 api_name => "open-ils.actor.user.transactions.have_charge",
1372 notes => <<" NOTES");
1373 Returns a list of all open user transactions (mbts objects) that have an initial charge
1374 Params are login_session, user_id
1375 Optional third parameter is the transactions type. defaults to all
1378 __PACKAGE__->register_method(
1379 method => "user_transactions",
1380 api_name => "open-ils.actor.user.transactions.have_balance",
1381 notes => <<" NOTES");
1382 Returns a list of all open user transactions (mbts objects) that have a balance
1383 Params are login_session, user_id
1384 Optional third parameter is the transactions type. defaults to all
1387 __PACKAGE__->register_method(
1388 method => "user_transactions",
1389 api_name => "open-ils.actor.user.transactions.fleshed",
1390 notes => <<" NOTES");
1391 Returns an object/hash of transaction, circ, title where transaction = an open
1392 user transactions (mbts objects), circ is the attached circluation, and title
1393 is the title the circ points to
1394 Params are login_session, user_id
1395 Optional third parameter is the transactions type. defaults to all
1398 __PACKAGE__->register_method(
1399 method => "user_transactions",
1400 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1401 notes => <<" NOTES");
1402 Returns an object/hash of transaction, circ, title where transaction = an open
1403 user transactions that has an initial charge (mbts objects), circ is the
1404 attached circluation, and title is the title the circ points to
1405 Params are login_session, user_id
1406 Optional third parameter is the transactions type. defaults to all
1409 __PACKAGE__->register_method(
1410 method => "user_transactions",
1411 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1412 notes => <<" NOTES");
1413 Returns an object/hash of transaction, circ, title where transaction = an open
1414 user transaction that has a balance (mbts objects), circ is the attached
1415 circluation, and title is the title the circ points to
1416 Params are login_session, user_id
1417 Optional third parameter is the transaction type. defaults to all
1420 __PACKAGE__->register_method(
1421 method => "user_transactions",
1422 api_name => "open-ils.actor.user.transactions.count",
1423 notes => <<" NOTES");
1424 Returns an object/hash of transaction, circ, title where transaction = an open
1425 user transactions (mbts objects), circ is the attached circluation, and title
1426 is the title the circ points to
1427 Params are login_session, user_id
1428 Optional third parameter is the transactions type. defaults to all
1431 __PACKAGE__->register_method(
1432 method => "user_transactions",
1433 api_name => "open-ils.actor.user.transactions.have_charge.count",
1434 notes => <<" NOTES");
1435 Returns an object/hash of transaction, circ, title where transaction = an open
1436 user transactions that has an initial charge (mbts objects), circ is the
1437 attached circluation, and title is the title the circ points to
1438 Params are login_session, user_id
1439 Optional third parameter is the transactions type. defaults to all
1442 __PACKAGE__->register_method(
1443 method => "user_transactions",
1444 api_name => "open-ils.actor.user.transactions.have_balance.count",
1445 notes => <<" NOTES");
1446 Returns an object/hash of transaction, circ, title where transaction = an open
1447 user transaction that has a balance (mbts objects), circ is the attached
1448 circluation, and title is the title the circ points to
1449 Params are login_session, user_id
1450 Optional third parameter is the transaction type. defaults to all
1453 __PACKAGE__->register_method(
1454 method => "user_transactions",
1455 api_name => "open-ils.actor.user.transactions.have_balance.total",
1456 notes => <<" NOTES");
1457 Returns an object/hash of transaction, circ, title where transaction = an open
1458 user transaction that has a balance (mbts objects), circ is the attached
1459 circluation, and title is the title the circ points to
1460 Params are login_session, user_id
1461 Optional third parameter is the transaction type. defaults to all
1466 sub user_transactions {
1467 my( $self, $client, $login_session, $user_id, $type ) = @_;
1469 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1470 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1471 return $evt if $evt;
1473 my $api = $self->api_name();
1477 if(defined($type)) { @xact = (xact_type => $type);
1479 } else { @xact = (); }
1481 if($api =~ /have_charge/o) {
1483 $trans = $apputils->simple_scalar_request(
1485 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1486 { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1488 } elsif($api =~ /have_balance/o) {
1490 $trans = $apputils->simple_scalar_request(
1492 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1493 { usr => $user_id, balance_owed => { "<>" => 0 }, @xact });
1497 $trans = $apputils->simple_scalar_request(
1499 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1500 { usr => $user_id, @xact });
1503 if($api =~ /total/o) {
1505 for my $t (@$trans) {
1506 $total += $t->balance_owed;
1509 $logger->debug("Total balance owed by user $user_id: $total");
1513 if($api =~ /count/o) { return scalar @$trans; }
1514 if($api !~ /fleshed/o) { return $trans; }
1517 for my $t (@$trans) {
1519 if( $t->xact_type ne 'circulation' ) {
1520 push @resp, {transaction => $t};
1524 my $circ = $apputils->simple_scalar_request(
1526 "open-ils.cstore.direct.action.circulation.retrieve",
1531 my $title = $apputils->simple_scalar_request(
1533 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1534 $circ->target_copy );
1538 my $u = OpenILS::Utils::ModsParser->new();
1539 $u->start_mods_batch($title->marc());
1540 my $mods = $u->finish_mods_batch();
1541 $mods->doc_id($title->id) if $mods;
1543 push @resp, {transaction => $t, circ => $circ, record => $mods };
1551 __PACKAGE__->register_method(
1552 method => "user_transaction_retrieve",
1553 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1555 notes => <<" NOTES");
1556 Returns a fleshedtransaction record
1558 __PACKAGE__->register_method(
1559 method => "user_transaction_retrieve",
1560 api_name => "open-ils.actor.user.transaction.retrieve",
1562 notes => <<" NOTES");
1563 Returns a transaction record
1565 sub user_transaction_retrieve {
1566 my( $self, $client, $login_session, $bill_id ) = @_;
1568 my $trans = $apputils->simple_scalar_request(
1570 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1574 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1575 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1576 return $evt if $evt;
1578 my $api = $self->api_name();
1579 if($api !~ /fleshed/o) { return $trans; }
1581 if( $trans->xact_type ne 'circulation' ) {
1582 $logger->debug("Returning non-circ transaction");
1583 return {transaction => $trans};
1586 my $circ = $apputils->simple_scalar_request(
1588 "open-ils..direct.action.circulation.retrieve",
1591 return {transaction => $trans} unless $circ;
1592 $logger->debug("Found the circ transaction");
1594 my $title = $apputils->simple_scalar_request(
1596 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1597 $circ->target_copy );
1599 return {transaction => $trans, circ => $circ } unless $title;
1600 $logger->debug("Found the circ title");
1604 my $u = OpenILS::Utils::ModsParser->new();
1605 $u->start_mods_batch($title->marc());
1606 $mods = $u->finish_mods_batch();
1608 if ($title->id == -1) {
1609 my $copy = $apputils->simple_scalar_request(
1611 "open-ils.cstore.direct.asset.copy.retrieve",
1612 $circ->target_copy );
1614 $mods = new Fieldmapper::metabib::virtual_record;
1616 $mods->title($copy->dummy_title);
1617 $mods->author($copy->dummy_author);
1621 $logger->debug("MODSized the circ title");
1623 return {transaction => $trans, circ => $circ, record => $mods };
1627 __PACKAGE__->register_method(
1628 method => "hold_request_count",
1629 api_name => "open-ils.actor.user.hold_requests.count",
1631 notes => <<" NOTES");
1632 Returns hold ready/total counts
1634 sub hold_request_count {
1635 my( $self, $client, $login_session, $userid ) = @_;
1637 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1638 $login_session, $userid, 'VIEW_HOLD' );
1639 return $evt if $evt;
1642 my $holds = $apputils->simple_scalar_request(
1644 "open-ils.cstore.direct.action.hold_request.search.atomic",
1646 fulfillment_time => {"=" => undef } }
1650 for my $h (@$holds) {
1651 next unless $h->capture_time and $h->current_copy;
1653 my $copy = $apputils->simple_scalar_request(
1655 "open-ils.cstore.direct.asset.copy.retrieve",
1659 if ($copy and $copy->status == 8) {
1664 return { total => scalar(@$holds), ready => scalar(@ready) };
1668 __PACKAGE__->register_method(
1669 method => "checkedout_count",
1670 api_name => "open-ils.actor.user.checked_out.count__",
1672 notes => <<" NOTES");
1673 Returns a transaction record
1677 sub checkedout_count {
1678 my( $self, $client, $login_session, $userid ) = @_;
1680 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1681 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1682 return $evt if $evt;
1684 my $circs = $apputils->simple_scalar_request(
1686 "open-ils.cstore.direct.action.circulation.search.atomic",
1687 { usr => $userid, stop_fines => undef }
1688 #{ usr => $userid, checkin_time => {"=" => undef } }
1691 my $parser = DateTime::Format::ISO8601->new;
1694 for my $c (@$circs) {
1695 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1696 my $due = $due_dt->epoch;
1698 if ($due < DateTime->today->epoch) {
1703 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1707 __PACKAGE__->register_method(
1708 method => "checked_out",
1709 api_name => "open-ils.actor.user.checked_out",
1712 Returns a structure of circulations objects sorted by
1713 out, overdue, lost, claims_returned, long_overdue.
1714 A list of IDs are returned of each type.
1715 lost, long_overdue, and claims_returned circ will not
1716 be "finished" (there is an outstanding balance or some
1717 other pending action on the circ).
1719 The .count method also includes a 'total' field which
1720 sums all "open" circs
1724 __PACKAGE__->register_method(
1725 method => "checked_out",
1726 api_name => "open-ils.actor.user.checked_out.count",
1728 signature => q/@see open-ils.actor.user.checked_out/
1732 my( $self, $conn, $auth, $userid ) = @_;
1734 my $e = new_editor(authtoken=>$auth);
1735 return $e->event unless $e->checkauth;
1737 if( $userid ne $e->requestor->id ) {
1738 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1741 my $count = $self->api_name =~ /count/;
1742 return _checked_out( $count, $e, $userid );
1746 my( $iscount, $e, $userid ) = @_;
1748 my $circs = $e->search_action_circulation(
1749 { usr => $userid, stop_fines => undef });
1751 my $parser = DateTime::Format::ISO8601->new;
1753 # split the circs up into overdue and not-overdue circs
1755 for my $c (@$circs) {
1756 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1757 my $due = $due_dt->epoch;
1758 if ($due < DateTime->today->epoch) {
1759 push @overdue, $c->id;
1765 # grab all of the lost, claims-returned, and longoverdue circs
1766 my $open = $e->search_action_circulation(
1767 {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1769 my( @lost, @cr, @lo );
1770 for my $c (@$open) {
1771 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1772 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1773 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1779 total => @$circs + @lost + @cr + @lo,
1780 out => scalar(@out),
1781 overdue => scalar(@overdue),
1782 lost => scalar(@lost),
1783 claims_returned => scalar(@cr),
1784 long_overdue => scalar(@lo)
1790 overdue => \@overdue,
1792 claims_returned => \@cr,
1793 long_overdue => \@lo
1801 __PACKAGE__->register_method(
1802 method => "user_transaction_history",
1803 api_name => "open-ils.actor.user.transactions.history",
1805 notes => <<" NOTES");
1806 Returns a list of billable transaction ids for a user, optionally by type
1808 __PACKAGE__->register_method(
1809 method => "user_transaction_history",
1810 api_name => "open-ils.actor.user.transactions.history.have_charge",
1812 notes => <<" NOTES");
1813 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1815 __PACKAGE__->register_method(
1816 method => "user_transaction_history",
1817 api_name => "open-ils.actor.user.transactions.history.have_balance",
1819 notes => <<" NOTES");
1820 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1824 sub _user_transaction_history {
1825 my( $self, $client, $login_session, $user_id, $type ) = @_;
1827 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1828 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1829 return $evt if $evt;
1831 my $api = $self->api_name();
1836 @xact = (xact_type => $type) if(defined($type));
1837 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1838 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1840 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1842 my $trans = $apputils->simple_scalar_request(
1844 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1845 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1847 return [ map { $_->id } @$trans ];
1852 sub user_transaction_history {
1853 my( $self, $conn, $auth, $userid, $type ) = @_;
1854 my $e = new_editor(authtoken=>$auth);
1855 return $e->event unless $e->checkauth;
1856 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1858 my $api = $self->api_name;
1859 my @xact = (xact_type => $type) if(defined($type));
1860 my @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1861 my @charge = (last_billing_ts => { "!=" => undef }) if $api =~ /have_charge/;
1863 return $e->search_money_billable_transaction_summary(
1865 { usr => $userid, @xact, @charge, @balance },
1866 { order_by => 'xact_start DESC' }
1872 __PACKAGE__->register_method(
1873 method => "user_perms",
1874 api_name => "open-ils.actor.permissions.user_perms.retrieve",
1876 notes => <<" NOTES");
1877 Returns a list of permissions
1880 my( $self, $client, $authtoken, $user ) = @_;
1882 my( $staff, $evt ) = $apputils->checkses($authtoken);
1883 return $evt if $evt;
1885 $user ||= $staff->id;
1887 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1891 return $apputils->simple_scalar_request(
1893 "open-ils.storage.permission.user_perms.atomic",
1897 __PACKAGE__->register_method(
1898 method => "retrieve_perms",
1899 api_name => "open-ils.actor.permissions.retrieve",
1900 notes => <<" NOTES");
1901 Returns a list of permissions
1903 sub retrieve_perms {
1904 my( $self, $client ) = @_;
1905 return $apputils->simple_scalar_request(
1907 "open-ils.cstore.direct.permission.perm_list.search.atomic",
1908 { id => { '!=' => undef } }
1912 __PACKAGE__->register_method(
1913 method => "retrieve_groups",
1914 api_name => "open-ils.actor.groups.retrieve",
1915 notes => <<" NOTES");
1916 Returns a list of user groupss
1918 sub retrieve_groups {
1919 my( $self, $client ) = @_;
1920 return new_editor()->retrieve_all_permission_grp_tree();
1923 __PACKAGE__->register_method(
1924 method => "retrieve_org_address",
1925 api_name => "open-ils.actor.org_unit.address.retrieve",
1926 notes => <<' NOTES');
1927 Returns an org_unit address by ID
1928 @param An org_address ID
1930 sub retrieve_org_address {
1931 my( $self, $client, $id ) = @_;
1932 return $apputils->simple_scalar_request(
1934 "open-ils.cstore.direct.actor.org_address.retrieve",
1939 __PACKAGE__->register_method(
1940 method => "retrieve_groups_tree",
1941 api_name => "open-ils.actor.groups.tree.retrieve",
1942 notes => <<" NOTES");
1943 Returns a list of user groups
1945 sub retrieve_groups_tree {
1946 my( $self, $client ) = @_;
1947 return new_editor()->search_permission_grp_tree(
1952 flesh_fields => { pgt => ["children"] },
1953 order_by => { pgt => 'name'}
1960 # turns an org list into an org tree
1962 sub build_group_tree {
1964 my( $self, $grplist) = @_;
1966 return $grplist unless (
1967 ref($grplist) and @$grplist > 1 );
1969 my @list = sort { $a->name cmp $b->name } @$grplist;
1972 for my $grp (@list) {
1974 if ($grp and !defined($grp->parent)) {
1978 my ($parent) = grep { $_->id == $grp->parent} @list;
1980 $parent->children([]) unless defined($parent->children);
1981 push( @{$parent->children}, $grp );
1989 __PACKAGE__->register_method(
1990 method => "add_user_to_groups",
1991 api_name => "open-ils.actor.user.set_groups",
1992 notes => <<" NOTES");
1993 Adds a user to one or more permission groups
1996 sub add_user_to_groups {
1997 my( $self, $client, $authtoken, $userid, $groups ) = @_;
1999 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2000 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2001 return $evt if $evt;
2003 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2004 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2005 return $evt if $evt;
2007 $apputils->simplereq(
2009 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2011 for my $group (@$groups) {
2012 my $link = Fieldmapper::permission::usr_grp_map->new;
2014 $link->usr($userid);
2016 my $id = $apputils->simplereq(
2018 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2024 __PACKAGE__->register_method(
2025 method => "get_user_perm_groups",
2026 api_name => "open-ils.actor.user.get_groups",
2027 notes => <<" NOTES");
2028 Retrieve a user's permission groups.
2032 sub get_user_perm_groups {
2033 my( $self, $client, $authtoken, $userid ) = @_;
2035 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2036 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2037 return $evt if $evt;
2039 return $apputils->simplereq(
2041 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2046 __PACKAGE__->register_method (
2047 method => 'register_workstation',
2048 api_name => 'open-ils.actor.workstation.register.override',
2049 signature => q/@see open-ils.actor.workstation.register/);
2051 __PACKAGE__->register_method (
2052 method => 'register_workstation',
2053 api_name => 'open-ils.actor.workstation.register',
2055 Registers a new workstion in the system
2056 @param authtoken The login session key
2057 @param name The name of the workstation id
2058 @param owner The org unit that owns this workstation
2059 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2060 if the name is already in use.
2063 sub _register_workstation {
2064 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2065 my( $requestor, $evt ) = $U->checkses($authtoken);
2066 return $evt if $evt;
2067 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2068 return $evt if $evt;
2070 my $ws = $U->cstorereq(
2071 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2072 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2074 $ws = Fieldmapper::actor::workstation->new;
2075 $ws->owning_lib($owner);
2078 my $id = $U->storagereq(
2079 'open-ils.storage.direct.actor.workstation.create', $ws );
2080 return $U->DB_UPDATE_FAILED($ws) unless $id;
2086 sub register_workstation {
2087 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2089 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2090 return $e->event unless $e->checkauth;
2091 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2092 my $existing = $e->search_actor_workstation({name => $name});
2095 if( $self->api_name =~ /override/o ) {
2096 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2097 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2099 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2103 my $ws = Fieldmapper::actor::workstation->new;
2104 $ws->owning_lib($owner);
2106 $e->create_actor_workstation($ws) or return $e->event;
2108 return $ws->id; # note: editor sets the id on the new object for us
2112 __PACKAGE__->register_method (
2113 method => 'fetch_patron_note',
2114 api_name => 'open-ils.actor.note.retrieve.all',
2116 Returns a list of notes for a given user
2117 Requestor must have VIEW_USER permission if pub==false and
2118 @param authtoken The login session key
2119 @param args Hash of params including
2120 patronid : the patron's id
2121 pub : true if retrieving only public notes
2125 sub fetch_patron_note {
2126 my( $self, $conn, $authtoken, $args ) = @_;
2127 my $patronid = $$args{patronid};
2129 my($reqr, $evt) = $U->checkses($authtoken);
2132 ($patron, $evt) = $U->fetch_user($patronid);
2133 return $evt if $evt;
2136 if( $patronid ne $reqr->id ) {
2137 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2138 return $evt if $evt;
2140 return $U->cstorereq(
2141 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2142 { usr => $patronid, pub => 't' } );
2145 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2146 return $evt if $evt;
2148 return $U->cstorereq(
2149 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2152 __PACKAGE__->register_method (
2153 method => 'create_user_note',
2154 api_name => 'open-ils.actor.note.create',
2156 Creates a new note for the given user
2157 @param authtoken The login session key
2158 @param note The note object
2161 sub create_user_note {
2162 my( $self, $conn, $authtoken, $note ) = @_;
2163 my( $reqr, $patron, $evt ) =
2164 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2165 return $evt if $evt;
2166 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2168 $note->creator($reqr->id);
2169 my $id = $U->storagereq(
2170 'open-ils.storage.direct.actor.usr_note.create', $note );
2171 return $U->DB_UPDATE_FAILED($note) unless $id;
2176 __PACKAGE__->register_method (
2177 method => 'delete_user_note',
2178 api_name => 'open-ils.actor.note.delete',
2180 Deletes a note for the given user
2181 @param authtoken The login session key
2182 @param noteid The note id
2185 sub delete_user_note {
2186 my( $self, $conn, $authtoken, $noteid ) = @_;
2188 my $note = $U->cstorereq(
2189 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2190 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2192 my( $reqr, $patron, $evt ) =
2193 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2194 return $evt if $evt;
2195 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2197 my $stat = $U->storagereq(
2198 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2199 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2204 __PACKAGE__->register_method (
2205 method => 'update_user_note',
2206 api_name => 'open-ils.actor.note.update',
2208 @param authtoken The login session key
2209 @param note The note
2213 sub update_user_note {
2214 my( $self, $conn, $auth, $note ) = @_;
2215 my $e = new_editor(authtoken=>$auth, xact=>1);
2216 return $e->event unless $e->checkauth;
2217 my $patron = $e->retrieve_actor_user($note->usr)
2218 or return $e->event;
2219 return $e->event unless
2220 $e->allowed('UPDATE_USER', $patron->home_ou);
2221 $e->update_actor_user_note($note)
2222 or return $e->event;
2230 __PACKAGE__->register_method (
2231 method => 'create_closed_date',
2232 api_name => 'open-ils.actor.org_unit.closed_date.create',
2234 Creates a new closing entry for the given org_unit
2235 @param authtoken The login session key
2236 @param note The closed_date object
2239 sub create_closed_date {
2240 my( $self, $conn, $authtoken, $cd ) = @_;
2242 my( $user, $evt ) = $U->checkses($authtoken);
2243 return $evt if $evt;
2245 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2246 return $evt if $evt;
2248 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2250 my $id = $U->storagereq(
2251 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2252 return $U->DB_UPDATE_FAILED($cd) unless $id;
2257 __PACKAGE__->register_method (
2258 method => 'delete_closed_date',
2259 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2261 Deletes a closing entry for the given org_unit
2262 @param authtoken The login session key
2263 @param noteid The close_date id
2266 sub delete_closed_date {
2267 my( $self, $conn, $authtoken, $cd ) = @_;
2269 my( $user, $evt ) = $U->checkses($authtoken);
2270 return $evt if $evt;
2273 ($cd_obj, $evt) = fetch_closed_date($cd);
2274 return $evt if $evt;
2276 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2277 return $evt if $evt;
2279 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2281 my $stat = $U->storagereq(
2282 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2283 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2288 __PACKAGE__->register_method(
2289 method => 'usrname_exists',
2290 api_name => 'open-ils.actor.username.exists',
2292 Returns 1 if the requested username exists, returns 0 otherwise
2296 sub usrname_exists {
2297 my( $self, $conn, $auth, $usrname ) = @_;
2298 my $e = new_editor(authtoken=>$auth);
2299 return $e->event unless $e->checkauth;
2300 my $a = $e->search_actor_user({usrname => $usrname}, {idlist=>1});
2301 return $$a[0] if $a and @$a;
2305 __PACKAGE__->register_method(
2306 method => 'barcode_exists',
2307 api_name => 'open-ils.actor.barcode.exists',
2309 Returns 1 if the requested barcode exists, returns 0 otherwise
2313 sub barcode_exists {
2314 my( $self, $conn, $auth, $barcode ) = @_;
2315 my $e = new_editor(authtoken=>$auth);
2316 return $e->event unless $e->checkauth;
2317 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2318 return $$a[0] if $a and @$a;
2323 __PACKAGE__->register_method(
2324 method => 'retrieve_net_levels',
2325 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2328 sub retrieve_net_levels {
2329 my( $self, $conn, $auth ) = @_;
2330 my $e = new_editor(authtoken=>$auth);
2331 return $e->event unless $e->checkauth;
2332 return $e->retrieve_all_config_net_access_level();