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, $include_inactive ) = @_;
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, $include_inactive);
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 });
1770 # these items have stop_fines, but no xact_finish, so money
1771 # is owed on them and they have not been checked in
1772 my $open = $e->search_action_circulation(
1775 stop_fines => { '!=' => undef },
1776 xact_finish => undef,
1777 checkin_time => undef,
1782 my( @lost, @cr, @lo );
1783 for my $c (@$open) {
1784 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1785 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1786 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1792 total => @$circs + @lost + @cr + @lo,
1793 out => scalar(@out),
1794 overdue => scalar(@overdue),
1795 lost => scalar(@lost),
1796 claims_returned => scalar(@cr),
1797 long_overdue => scalar(@lo)
1803 overdue => \@overdue,
1805 claims_returned => \@cr,
1806 long_overdue => \@lo
1812 __PACKAGE__->register_method(
1813 method => "checked_in_with_fines",
1814 api_name => "open-ils.actor.user.checked_in_with_fines",
1816 signature => q/@see open-ils.actor.user.checked_out/
1818 sub checked_in_with_fines {
1819 my( $self, $conn, $auth, $userid ) = @_;
1821 my $e = new_editor(authtoken=>$auth);
1822 return $e->event unless $e->checkauth;
1824 if( $userid ne $e->requestor->id ) {
1825 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1828 # money is owed on these items and they are checked in
1829 my $open = $e->search_action_circulation(
1832 xact_finish => undef,
1833 checkin_time => { "!=" => undef },
1838 my( @lost, @cr, @lo );
1839 for my $c (@$open) {
1840 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1841 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1842 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1847 claims_returned => \@cr,
1848 long_overdue => \@lo
1860 __PACKAGE__->register_method(
1861 method => "user_transaction_history",
1862 api_name => "open-ils.actor.user.transactions.history",
1864 notes => <<" NOTES");
1865 Returns a list of billable transaction ids for a user, optionally by type
1867 __PACKAGE__->register_method(
1868 method => "user_transaction_history",
1869 api_name => "open-ils.actor.user.transactions.history.have_charge",
1871 notes => <<" NOTES");
1872 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1874 __PACKAGE__->register_method(
1875 method => "user_transaction_history",
1876 api_name => "open-ils.actor.user.transactions.history.have_balance",
1878 notes => <<" NOTES");
1879 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1883 sub _user_transaction_history {
1884 my( $self, $client, $login_session, $user_id, $type ) = @_;
1886 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1887 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1888 return $evt if $evt;
1890 my $api = $self->api_name();
1895 @xact = (xact_type => $type) if(defined($type));
1896 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1897 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1899 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1901 my $trans = $apputils->simple_scalar_request(
1903 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1904 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1906 return [ map { $_->id } @$trans ];
1911 sub user_transaction_history {
1912 my( $self, $conn, $auth, $userid, $type ) = @_;
1913 my $e = new_editor(authtoken=>$auth);
1914 return $e->event unless $e->checkauth;
1915 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1917 my $api = $self->api_name;
1918 my @xact = (xact_type => $type) if(defined($type));
1919 my @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1920 my @charge = (last_billing_ts => { "!=" => undef }) if $api =~ /have_charge/;
1922 return $e->search_money_billable_transaction_summary(
1924 { usr => $userid, @xact, @charge, @balance },
1925 { order_by => 'xact_start DESC' }
1931 __PACKAGE__->register_method(
1932 method => "user_perms",
1933 api_name => "open-ils.actor.permissions.user_perms.retrieve",
1935 notes => <<" NOTES");
1936 Returns a list of permissions
1939 my( $self, $client, $authtoken, $user ) = @_;
1941 my( $staff, $evt ) = $apputils->checkses($authtoken);
1942 return $evt if $evt;
1944 $user ||= $staff->id;
1946 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1950 return $apputils->simple_scalar_request(
1952 "open-ils.storage.permission.user_perms.atomic",
1956 __PACKAGE__->register_method(
1957 method => "retrieve_perms",
1958 api_name => "open-ils.actor.permissions.retrieve",
1959 notes => <<" NOTES");
1960 Returns a list of permissions
1962 sub retrieve_perms {
1963 my( $self, $client ) = @_;
1964 return $apputils->simple_scalar_request(
1966 "open-ils.cstore.direct.permission.perm_list.search.atomic",
1967 { id => { '!=' => undef } }
1971 __PACKAGE__->register_method(
1972 method => "retrieve_groups",
1973 api_name => "open-ils.actor.groups.retrieve",
1974 notes => <<" NOTES");
1975 Returns a list of user groupss
1977 sub retrieve_groups {
1978 my( $self, $client ) = @_;
1979 return new_editor()->retrieve_all_permission_grp_tree();
1982 __PACKAGE__->register_method(
1983 method => "retrieve_org_address",
1984 api_name => "open-ils.actor.org_unit.address.retrieve",
1985 notes => <<' NOTES');
1986 Returns an org_unit address by ID
1987 @param An org_address ID
1989 sub retrieve_org_address {
1990 my( $self, $client, $id ) = @_;
1991 return $apputils->simple_scalar_request(
1993 "open-ils.cstore.direct.actor.org_address.retrieve",
1998 __PACKAGE__->register_method(
1999 method => "retrieve_groups_tree",
2000 api_name => "open-ils.actor.groups.tree.retrieve",
2001 notes => <<" NOTES");
2002 Returns a list of user groups
2004 sub retrieve_groups_tree {
2005 my( $self, $client ) = @_;
2006 return new_editor()->search_permission_grp_tree(
2011 flesh_fields => { pgt => ["children"] },
2012 order_by => { pgt => 'name'}
2019 # turns an org list into an org tree
2021 sub build_group_tree {
2023 my( $self, $grplist) = @_;
2025 return $grplist unless (
2026 ref($grplist) and @$grplist > 1 );
2028 my @list = sort { $a->name cmp $b->name } @$grplist;
2031 for my $grp (@list) {
2033 if ($grp and !defined($grp->parent)) {
2037 my ($parent) = grep { $_->id == $grp->parent} @list;
2039 $parent->children([]) unless defined($parent->children);
2040 push( @{$parent->children}, $grp );
2048 __PACKAGE__->register_method(
2049 method => "add_user_to_groups",
2050 api_name => "open-ils.actor.user.set_groups",
2051 notes => <<" NOTES");
2052 Adds a user to one or more permission groups
2055 sub add_user_to_groups {
2056 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2058 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2059 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2060 return $evt if $evt;
2062 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2063 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2064 return $evt if $evt;
2066 $apputils->simplereq(
2068 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2070 for my $group (@$groups) {
2071 my $link = Fieldmapper::permission::usr_grp_map->new;
2073 $link->usr($userid);
2075 my $id = $apputils->simplereq(
2077 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2083 __PACKAGE__->register_method(
2084 method => "get_user_perm_groups",
2085 api_name => "open-ils.actor.user.get_groups",
2086 notes => <<" NOTES");
2087 Retrieve a user's permission groups.
2091 sub get_user_perm_groups {
2092 my( $self, $client, $authtoken, $userid ) = @_;
2094 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2095 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2096 return $evt if $evt;
2098 return $apputils->simplereq(
2100 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2105 __PACKAGE__->register_method (
2106 method => 'register_workstation',
2107 api_name => 'open-ils.actor.workstation.register.override',
2108 signature => q/@see open-ils.actor.workstation.register/);
2110 __PACKAGE__->register_method (
2111 method => 'register_workstation',
2112 api_name => 'open-ils.actor.workstation.register',
2114 Registers a new workstion in the system
2115 @param authtoken The login session key
2116 @param name The name of the workstation id
2117 @param owner The org unit that owns this workstation
2118 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2119 if the name is already in use.
2122 sub _register_workstation {
2123 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2124 my( $requestor, $evt ) = $U->checkses($authtoken);
2125 return $evt if $evt;
2126 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2127 return $evt if $evt;
2129 my $ws = $U->cstorereq(
2130 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2131 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2133 $ws = Fieldmapper::actor::workstation->new;
2134 $ws->owning_lib($owner);
2137 my $id = $U->storagereq(
2138 'open-ils.storage.direct.actor.workstation.create', $ws );
2139 return $U->DB_UPDATE_FAILED($ws) unless $id;
2145 sub register_workstation {
2146 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2148 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2149 return $e->event unless $e->checkauth;
2150 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2151 my $existing = $e->search_actor_workstation({name => $name});
2154 if( $self->api_name =~ /override/o ) {
2155 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2156 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2158 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2162 my $ws = Fieldmapper::actor::workstation->new;
2163 $ws->owning_lib($owner);
2165 $e->create_actor_workstation($ws) or return $e->event;
2167 return $ws->id; # note: editor sets the id on the new object for us
2171 __PACKAGE__->register_method (
2172 method => 'fetch_patron_note',
2173 api_name => 'open-ils.actor.note.retrieve.all',
2175 Returns a list of notes for a given user
2176 Requestor must have VIEW_USER permission if pub==false and
2177 @param authtoken The login session key
2178 @param args Hash of params including
2179 patronid : the patron's id
2180 pub : true if retrieving only public notes
2184 sub fetch_patron_note {
2185 my( $self, $conn, $authtoken, $args ) = @_;
2186 my $patronid = $$args{patronid};
2188 my($reqr, $evt) = $U->checkses($authtoken);
2191 ($patron, $evt) = $U->fetch_user($patronid);
2192 return $evt if $evt;
2195 if( $patronid ne $reqr->id ) {
2196 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2197 return $evt if $evt;
2199 return $U->cstorereq(
2200 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2201 { usr => $patronid, pub => 't' } );
2204 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2205 return $evt if $evt;
2207 return $U->cstorereq(
2208 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2211 __PACKAGE__->register_method (
2212 method => 'create_user_note',
2213 api_name => 'open-ils.actor.note.create',
2215 Creates a new note for the given user
2216 @param authtoken The login session key
2217 @param note The note object
2220 sub create_user_note {
2221 my( $self, $conn, $authtoken, $note ) = @_;
2222 my( $reqr, $patron, $evt ) =
2223 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2224 return $evt if $evt;
2225 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2227 $note->creator($reqr->id);
2228 my $id = $U->storagereq(
2229 'open-ils.storage.direct.actor.usr_note.create', $note );
2230 return $U->DB_UPDATE_FAILED($note) unless $id;
2235 __PACKAGE__->register_method (
2236 method => 'delete_user_note',
2237 api_name => 'open-ils.actor.note.delete',
2239 Deletes a note for the given user
2240 @param authtoken The login session key
2241 @param noteid The note id
2244 sub delete_user_note {
2245 my( $self, $conn, $authtoken, $noteid ) = @_;
2247 my $note = $U->cstorereq(
2248 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2249 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2251 my( $reqr, $patron, $evt ) =
2252 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2253 return $evt if $evt;
2254 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2256 my $stat = $U->storagereq(
2257 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2258 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2263 __PACKAGE__->register_method (
2264 method => 'update_user_note',
2265 api_name => 'open-ils.actor.note.update',
2267 @param authtoken The login session key
2268 @param note The note
2272 sub update_user_note {
2273 my( $self, $conn, $auth, $note ) = @_;
2274 my $e = new_editor(authtoken=>$auth, xact=>1);
2275 return $e->event unless $e->checkauth;
2276 my $patron = $e->retrieve_actor_user($note->usr)
2277 or return $e->event;
2278 return $e->event unless
2279 $e->allowed('UPDATE_USER', $patron->home_ou);
2280 $e->update_actor_user_note($note)
2281 or return $e->event;
2289 __PACKAGE__->register_method (
2290 method => 'create_closed_date',
2291 api_name => 'open-ils.actor.org_unit.closed_date.create',
2293 Creates a new closing entry for the given org_unit
2294 @param authtoken The login session key
2295 @param note The closed_date object
2298 sub create_closed_date {
2299 my( $self, $conn, $authtoken, $cd ) = @_;
2301 my( $user, $evt ) = $U->checkses($authtoken);
2302 return $evt if $evt;
2304 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2305 return $evt if $evt;
2307 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2309 my $id = $U->storagereq(
2310 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2311 return $U->DB_UPDATE_FAILED($cd) unless $id;
2316 __PACKAGE__->register_method (
2317 method => 'delete_closed_date',
2318 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2320 Deletes a closing entry for the given org_unit
2321 @param authtoken The login session key
2322 @param noteid The close_date id
2325 sub delete_closed_date {
2326 my( $self, $conn, $authtoken, $cd ) = @_;
2328 my( $user, $evt ) = $U->checkses($authtoken);
2329 return $evt if $evt;
2332 ($cd_obj, $evt) = fetch_closed_date($cd);
2333 return $evt if $evt;
2335 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2336 return $evt if $evt;
2338 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2340 my $stat = $U->storagereq(
2341 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2342 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2347 __PACKAGE__->register_method(
2348 method => 'usrname_exists',
2349 api_name => 'open-ils.actor.username.exists',
2351 Returns 1 if the requested username exists, returns 0 otherwise
2355 sub usrname_exists {
2356 my( $self, $conn, $auth, $usrname ) = @_;
2357 my $e = new_editor(authtoken=>$auth);
2358 return $e->event unless $e->checkauth;
2359 my $a = $e->search_actor_user({usrname => $usrname}, {idlist=>1});
2360 return $$a[0] if $a and @$a;
2364 __PACKAGE__->register_method(
2365 method => 'barcode_exists',
2366 api_name => 'open-ils.actor.barcode.exists',
2368 Returns 1 if the requested barcode exists, returns 0 otherwise
2372 sub barcode_exists {
2373 my( $self, $conn, $auth, $barcode ) = @_;
2374 my $e = new_editor(authtoken=>$auth);
2375 return $e->event unless $e->checkauth;
2376 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2377 return $$a[0] if $a and @$a;
2382 __PACKAGE__->register_method(
2383 method => 'retrieve_net_levels',
2384 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2387 sub retrieve_net_levels {
2388 my( $self, $conn, $auth ) = @_;
2389 my $e = new_editor(authtoken=>$auth);
2390 return $e->event unless $e->checkauth;
2391 return $e->retrieve_all_config_net_access_level();