1 package OpenILS::Application::Actor;
2 use base qw/OpenSRF::Application/;
3 use strict; use warnings;
5 $Data::Dumper::Indent = 0;
8 use Digest::MD5 qw(md5_hex);
10 use OpenSRF::EX qw(:try);
13 use OpenILS::Application::AppUtils;
15 use OpenILS::Utils::Fieldmapper;
16 use OpenILS::Utils::ModsParser;
17 use OpenSRF::Utils::Logger qw/$logger/;
18 use OpenSRF::Utils qw/:datetime/;
20 use OpenSRF::Utils::Cache;
24 use DateTime::Format::ISO8601;
25 use OpenILS::Const qw/:const/;
27 use OpenILS::Application::Actor::Container;
28 use OpenILS::Application::Actor::ClosedDates;
30 use OpenILS::Utils::CStoreEditor qw/:funcs/;
32 use OpenILS::Application::Actor::UserGroups;
34 OpenILS::Application::Actor::Container->initialize();
35 OpenILS::Application::Actor::UserGroups->initialize();
36 OpenILS::Application::Actor::ClosedDates->initialize();
39 my $apputils = "OpenILS::Application::AppUtils";
42 sub _d { warn "Patron:\n" . Dumper(shift()); }
47 my $set_user_settings;
50 __PACKAGE__->register_method(
51 method => "set_user_settings",
52 api_name => "open-ils.actor.patron.settings.update",
54 sub set_user_settings {
55 my( $self, $client, $user_session, $uid, $settings ) = @_;
57 $logger->debug("Setting user settings: $user_session, $uid, " . Dumper($settings));
59 my( $staff, $user, $evt ) =
60 $apputils->checkses_requestor( $user_session, $uid, 'UPDATE_USER' );
64 [{ usr => $user->id, name => $_}, {value => $$settings{$_}}] } keys %$settings;
66 $_->[1]->{value} = JSON->perl2JSON($_->[1]->{value}) for @params;
68 $logger->activity("User " . $staff->id . " updating user $uid settings with: " . Dumper(\@params));
70 my $ses = $U->start_db_session();
71 my $stat = $ses->request(
72 'open-ils.storage.direct.actor.user_setting.batch.merge', @params )->gather(1);
73 $U->commit_db_session($ses);
80 __PACKAGE__->register_method(
81 method => "set_ou_settings",
82 api_name => "open-ils.actor.org_unit.settings.update",
85 my( $self, $client, $user_session, $ouid, $settings ) = @_;
87 my( $staff, $evt ) = $apputils->checkses( $user_session );
89 $evt = $apputils->check_perms( $staff->id, $ouid, 'UPDATE_ORG_SETTING' );
93 for my $set (keys %$settings) {
95 my $json = JSON->perl2JSON($$settings{$set});
96 $logger->activity("updating org_unit.setting: $ouid : $set : $json");
99 { org_unit => $ouid, name => $set },
100 { value => $json } );
103 my $ses = $U->start_db_session();
104 my $stat = $ses->request(
105 'open-ils.storage.direct.actor.org_unit_setting.merge', @params )->gather(1);
106 $U->commit_db_session($ses);
112 my $fetch_user_settings;
113 my $fetch_ou_settings;
115 __PACKAGE__->register_method(
116 method => "user_settings",
117 api_name => "open-ils.actor.patron.settings.retrieve",
120 my( $self, $client, $user_session, $uid ) = @_;
122 my( $staff, $user, $evt ) =
123 $apputils->checkses_requestor( $user_session, $uid, 'VIEW_USER' );
126 $logger->debug("User " . $staff->id . " fetching user $uid\n");
127 my $s = $apputils->simplereq(
129 'open-ils.cstore.direct.actor.user_setting.search.atomic', { usr => $uid } );
131 return { map { ( $_->name => JSON->JSON2perl($_->value) ) } @$s };
136 __PACKAGE__->register_method(
137 method => "ou_settings",
138 api_name => "open-ils.actor.org_unit.settings.retrieve",
141 my( $self, $client, $ouid ) = @_;
143 $logger->info("Fetching org unit settings for org $ouid");
145 my $s = $apputils->simplereq(
147 'open-ils.cstore.direct.actor.org_unit_setting.search.atomic', {org_unit => $ouid});
149 return { map { ( $_->name => JSON->JSON2perl($_->value) ) } @$s };
152 __PACKAGE__->register_method (
153 method => "ou_setting_delete",
154 api_name => 'open-ils.actor.org_setting.delete',
156 Deletes a specific org unit setting for a specific location
157 @param authtoken The login session key
158 @param orgid The org unit whose setting we're changing
159 @param setting The name of the setting to delete
160 @return True value on success.
164 sub ou_setting_delete {
165 my( $self, $conn, $authtoken, $orgid, $setting ) = @_;
166 my( $reqr, $evt) = $U->checkses($authtoken);
168 $evt = $U->check_perms($reqr->id, $orgid, 'UPDATE_ORG_SETTING');
171 my $id = $U->cstorereq(
172 'open-ils.cstore.direct.actor.org_unit_setting.id_list',
173 { name => $setting, org_unit => $orgid } );
175 $logger->debug("Retrieved setting $id in org unit setting delete");
177 my $s = $U->cstorereq(
178 'open-ils.cstore.direct.actor.org_unit_setting.delete', $id );
180 $logger->activity("User ".$reqr->id." deleted org unit setting $id") if $s;
186 __PACKAGE__->register_method(
187 method => "update_patron",
188 api_name => "open-ils.actor.patron.update",);
191 my( $self, $client, $user_session, $patron ) = @_;
193 my $session = $apputils->start_db_session();
196 $logger->info("Creating new patron...") if $patron->isnew;
197 $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
199 my( $user_obj, $evt ) = $U->checkses($user_session);
202 # XXX does this user have permission to add/create users. Granularity?
203 # $new_patron is the patron in progress. $patron is the original patron
204 # passed in with the method. new_patron will change as the components
205 # of patron are added/updated.
209 # unflesh the real items on the patron
210 $patron->card( $patron->card->id ) if(ref($patron->card));
211 $patron->billing_address( $patron->billing_address->id )
212 if(ref($patron->billing_address));
213 $patron->mailing_address( $patron->mailing_address->id )
214 if(ref($patron->mailing_address));
216 # create/update the patron first so we can use his id
217 if($patron->isnew()) {
218 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
220 } else { $new_patron = $patron; }
222 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
225 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
228 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
231 # re-update the patron if anything has happened to him during this process
232 if($new_patron->ischanged()) {
233 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
237 #$session = OpenSRF::AppSession->create("open-ils.storage"); # why did i put this here?
239 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
242 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
245 ($new_patron, $evt) = _create_standing_penalties($session, $user_session, $patron, $new_patron, $user_obj);
248 $logger->activity("user ".$user_obj->id." updating/creating user ".$new_patron->id);
249 $apputils->commit_db_session($session);
251 #warn "Patron Update/Create complete\n";
252 return flesh_user($new_patron->id());
258 __PACKAGE__->register_method(
259 method => "user_retrieve_fleshed_by_id",
260 api_name => "open-ils.actor.user.fleshed.retrieve",);
262 sub user_retrieve_fleshed_by_id {
263 my( $self, $client, $user_session, $user_id ) = @_;
265 my( $requestor, $target, $evt ) = $apputils->
266 checkses_requestor( $user_session, $user_id, 'VIEW_USER' );
269 return flesh_user($user_id);
273 # fleshes: card, cards, address, addresses, stat_cat_entries, standing_penalties
282 $session = OpenSRF::AppSession->create("open-ils.storage");
286 # grab the user with the given id
287 my $ureq = $session->request(
288 "open-ils.storage.direct.actor.user.retrieve", $id);
289 my $user = $ureq->gather(1);
291 if(!$user) { return undef; }
294 my $cards_req = $session->request(
295 "open-ils.storage.direct.actor.card.search.usr.atomic",
297 $user->cards( $cards_req->gather(1) );
299 for my $c(@{$user->cards}) {
300 if($c->id == $user->card || $c->id eq $user->card ) {
301 #warn "Setting my card to " . $c->id . "\n";
306 my $add_req = $session->request(
307 "open-ils.storage.direct.actor.user_address.search.usr.atomic",
309 $user->addresses( $add_req->gather(1) );
311 if( @{$user->addresses} ) {
312 if( ! grep { $_->id eq $user->billing_address } @{$user->addresses} ) {
313 my $ba = $session->request(
314 'open-ils.storage.direct.actor.user_address.retrieve',
315 $user->billing_address)->gather(1);
316 push( @{$user->addresses}, $ba );
319 if( ! grep { $_->id eq $user->mailing_address } @{$user->addresses} ) {
320 my $ba = $session->request(
321 'open-ils.storage.direct.actor.user_address.retrieve',
322 $user->mailing_address)->gather(1);
323 push( @{$user->addresses}, $ba );
328 for my $c(@{$user->addresses}) {
329 if($c->id eq $user->billing_address ) { $user->billing_address($c); }
330 if($c->id eq $user->mailing_address ) { $user->mailing_address($c); }
333 my $stat_req = $session->request(
334 "open-ils.storage.direct.actor.stat_cat_entry_user_map.search.target_usr.atomic",
336 $user->stat_cat_entries($stat_req->gather(1));
338 my $standing_penalties_req = $session->request(
339 "open-ils.storage.direct.actor.user_standing_penalty.search.usr.atomic",
341 $user->standing_penalties($standing_penalties_req->gather(1));
343 if($kill) { $session->disconnect(); }
344 $user->clear_passwd();
351 my $e = new_editor();
352 my $user = $e->retrieve_actor_user(
361 "standing_penalties",
370 ) or return $e->event;
372 $user->clear_passwd();
381 # clone and clear stuff that would break the database
385 my $new_patron = $patron->clone;
387 # Using the Fieldmapper clone method
388 #my $new_patron = Fieldmapper::actor::user->new();
390 #my $fmap = $Fieldmapper::fieldmap;
391 #no strict; # shallow clone, may be useful in the fieldmapper
393 # (keys %{$fmap->{"Fieldmapper::actor::user"}->{'fields'}}) {
394 # $new_patron->$field( $patron->$field() );
399 $new_patron->clear_billing_address();
400 $new_patron->clear_mailing_address();
401 $new_patron->clear_addresses();
402 $new_patron->clear_card();
403 $new_patron->clear_cards();
404 $new_patron->clear_id();
405 $new_patron->clear_isnew();
406 $new_patron->clear_ischanged();
407 $new_patron->clear_isdeleted();
408 $new_patron->clear_stat_cat_entries();
409 $new_patron->clear_permissions();
410 $new_patron->clear_standing_penalties();
420 my $user_obj = shift;
422 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
423 return (undef, $evt) if $evt;
425 my $ex = $session->request(
426 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
428 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
431 $evt = _check_dup_ident($session, $patron);
432 return (undef, $evt) if $evt;
434 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
436 my $id = $session->request(
437 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
438 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
440 $logger->info("Successfully created new user [$id] in DB");
442 return ( $session->request(
443 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
448 my( $session, $patron, $user_obj, $noperm) = @_;
450 $logger->info("Updating patron ".$patron->id." in DB");
455 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
456 return (undef, $evt) if $evt;
459 # We can' check for dup idents on update because some existing
460 # users may already have dup idents
461 #$evt = _check_dup_ident($session, $patron);
462 #return (undef, $evt) if $evt;
465 # update the password by itself to avoid the password protection magic
466 if( $patron->passwd ) {
467 my $s = $session->request(
468 'open-ils.storage.direct.actor.user.remote_update',
469 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
470 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
471 $patron->clear_passwd;
474 if(!$patron->ident_type) {
475 $patron->clear_ident_type;
476 $patron->clear_ident_value;
479 if(!$patron->ident_type2) {
480 $patron->clear_ident_type2;
481 $patron->clear_ident_value2;
484 my $stat = $session->request(
485 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
486 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
491 sub _check_dup_ident {
492 my( $session, $patron ) = @_;
494 return undef unless $patron->ident_value;
497 ident_type => $patron->ident_type,
498 ident_value => $patron->ident_value,
501 $logger->debug("patron update searching for dup ident values: " .
502 $patron->ident_type . ':' . $patron->ident_value);
504 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
506 my $dups = $session->request(
507 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
510 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
517 sub _add_update_addresses {
521 my $new_patron = shift;
525 my $current_id; # id of the address before creation
527 for my $address (@{$patron->addresses()}) {
529 next unless ref $address;
530 $current_id = $address->id();
532 if( $patron->billing_address() and
533 $patron->billing_address() == $current_id ) {
534 $logger->info("setting billing addr to $current_id");
535 $new_patron->billing_address($address->id());
536 $new_patron->ischanged(1);
539 if( $patron->mailing_address() and
540 $patron->mailing_address() == $current_id ) {
541 $new_patron->mailing_address($address->id());
542 $logger->info("setting mailing addr to $current_id");
543 $new_patron->ischanged(1);
547 if($address->isnew()) {
549 $address->usr($new_patron->id());
551 ($address, $evt) = _add_address($session,$address);
552 return (undef, $evt) if $evt;
554 # we need to get the new id
555 if( $patron->billing_address() and
556 $patron->billing_address() == $current_id ) {
557 $new_patron->billing_address($address->id());
558 $logger->info("setting billing addr to $current_id");
559 $new_patron->ischanged(1);
562 if( $patron->mailing_address() and
563 $patron->mailing_address() == $current_id ) {
564 $new_patron->mailing_address($address->id());
565 $logger->info("setting mailing addr to $current_id");
566 $new_patron->ischanged(1);
569 } elsif($address->ischanged() ) {
571 ($address, $evt) = _update_address($session, $address);
572 return (undef, $evt) if $evt;
574 } elsif($address->isdeleted() ) {
576 if( $address->id() == $new_patron->mailing_address() ) {
577 $new_patron->clear_mailing_address();
578 ($new_patron, $evt) = _update_patron($session, $new_patron);
579 return (undef, $evt) if $evt;
582 if( $address->id() == $new_patron->billing_address() ) {
583 $new_patron->clear_billing_address();
584 ($new_patron, $evt) = _update_patron($session, $new_patron);
585 return (undef, $evt) if $evt;
588 $evt = _delete_address($session, $address);
589 return (undef, $evt) if $evt;
593 return ( $new_patron, undef );
597 # adds an address to the db and returns the address with new id
599 my($session, $address) = @_;
600 $address->clear_id();
602 $logger->info("Creating new address at street ".$address->street1);
604 # put the address into the database
605 my $id = $session->request(
606 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
607 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
610 return ($address, undef);
614 sub _update_address {
615 my( $session, $address ) = @_;
617 $logger->info("Updating address ".$address->id." in the DB");
619 my $stat = $session->request(
620 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
622 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
623 return ($address, undef);
628 sub _add_update_cards {
632 my $new_patron = shift;
636 my $virtual_id; #id of the card before creation
637 for my $card (@{$patron->cards()}) {
639 $card->usr($new_patron->id());
641 if(ref($card) and $card->isnew()) {
643 $virtual_id = $card->id();
644 ( $card, $evt ) = _add_card($session,$card);
645 return (undef, $evt) if $evt;
647 #if(ref($patron->card)) { $patron->card($patron->card->id); }
648 if($patron->card() == $virtual_id) {
649 $new_patron->card($card->id());
650 $new_patron->ischanged(1);
653 } elsif( ref($card) and $card->ischanged() ) {
654 $evt = _update_card($session, $card);
655 return (undef, $evt) if $evt;
659 return ( $new_patron, undef );
663 # adds an card to the db and returns the card with new id
665 my( $session, $card ) = @_;
668 $logger->info("Adding new patron card ".$card->barcode);
670 my $id = $session->request(
671 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
672 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
673 $logger->info("Successfully created patron card $id");
676 return ( $card, undef );
680 # returns event on error. returns undef otherwise
682 my( $session, $card ) = @_;
683 $logger->info("Updating patron card ".$card->id);
685 my $stat = $session->request(
686 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
687 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
694 # returns event on error. returns undef otherwise
695 sub _delete_address {
696 my( $session, $address ) = @_;
698 $logger->info("Deleting address ".$address->id." from DB");
700 my $stat = $session->request(
701 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
703 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
709 sub _add_survey_responses {
710 my ($session, $patron, $new_patron) = @_;
712 $logger->info( "Updating survey responses for patron ".$new_patron->id );
714 my $responses = $patron->survey_responses;
718 $_->usr($new_patron->id) for (@$responses);
720 my $evt = $U->simplereq( "open-ils.circ",
721 "open-ils.circ.survey.submit.user_id", $responses );
723 return (undef, $evt) if defined($U->event_code($evt));
727 return ( $new_patron, undef );
731 sub _create_stat_maps {
733 my($session, $user_session, $patron, $new_patron) = @_;
735 my $maps = $patron->stat_cat_entries();
737 for my $map (@$maps) {
739 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
741 if ($map->isdeleted()) {
742 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
744 } elsif ($map->isnew()) {
745 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
750 $map->target_usr($new_patron->id);
753 $logger->info("Updating stat entry with method $method and map $map");
755 my $stat = $session->request($method, $map)->gather(1);
756 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
760 return ($new_patron, undef);
763 sub _create_perm_maps {
765 my($session, $user_session, $patron, $new_patron) = @_;
767 my $maps = $patron->permissions;
769 for my $map (@$maps) {
771 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
772 if ($map->isdeleted()) {
773 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
774 } elsif ($map->isnew()) {
775 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
780 $map->usr($new_patron->id);
782 #warn( "Updating permissions with method $method and session $user_session and map $map" );
783 $logger->info( "Updating permissions with method $method and map $map" );
785 my $stat = $session->request($method, $map)->gather(1);
786 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
790 return ($new_patron, undef);
794 sub _create_standing_penalties {
796 my($session, $user_session, $patron, $new_patron) = @_;
798 my $maps = $patron->standing_penalties;
801 for my $map (@$maps) {
803 if ($map->isdeleted()) {
804 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
805 } elsif ($map->isnew()) {
806 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
812 $map->usr($new_patron->id);
814 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
816 my $stat = $session->request($method, $map)->gather(1);
817 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
820 return ($new_patron, undef);
825 __PACKAGE__->register_method(
826 method => "search_username",
827 api_name => "open-ils.actor.user.search.username",
830 sub search_username {
831 my($self, $client, $username) = @_;
832 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
834 "open-ils.cstore.direct.actor.user.search.atomic",
835 { usrname => $username }
843 __PACKAGE__->register_method(
844 method => "user_retrieve_by_barcode",
845 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
847 sub user_retrieve_by_barcode {
848 my($self, $client, $user_session, $barcode) = @_;
850 $logger->debug("Searching for user with barcode $barcode");
851 my ($user_obj, $evt) = $apputils->checkses($user_session);
854 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
856 "open-ils.cstore.direct.actor.card.search.atomic",
857 { barcode => $barcode }
860 if(!$card || !$card->[0]) {
861 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
865 my $user = flesh_user($card->usr());
867 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
870 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
877 __PACKAGE__->register_method(
878 method => "get_user_by_id",
879 api_name => "open-ils.actor.user.retrieve",);
882 my ($self, $client, $auth, $id) = @_;
883 my $e = new_editor(authtoken=>$auth);
884 return $e->event unless $e->checkauth;
885 my $user = $e->retrieve_actor_user($id)
887 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
893 __PACKAGE__->register_method(
894 method => "get_org_types",
895 api_name => "open-ils.actor.org_types.retrieve",);
899 my($self, $client) = @_;
900 return $org_types if $org_types;
901 return $org_types = new_editor()->retrieve_all_actor_org_unit_type();
906 __PACKAGE__->register_method(
907 method => "get_user_ident_types",
908 api_name => "open-ils.actor.user.ident_types.retrieve",
911 sub get_user_ident_types {
912 return $ident_types if $ident_types;
913 return $ident_types =
914 new_editor()->retrieve_all_config_identification_type();
920 __PACKAGE__->register_method(
921 method => "get_org_unit",
922 api_name => "open-ils.actor.org_unit.retrieve",
926 my( $self, $client, $user_session, $org_id ) = @_;
927 my $e = new_editor(authtoken => $user_session);
929 return $e->event unless $e->checkauth;
930 $org_id = $e->requestor->ws_ou;
932 my $o = $e->retrieve_actor_org_unit($org_id)
937 __PACKAGE__->register_method(
938 method => "search_org_unit",
939 api_name => "open-ils.actor.org_unit_list.search",
942 sub search_org_unit {
944 my( $self, $client, $field, $value ) = @_;
946 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
948 "open-ils.cstore.direct.actor.org_unit.search.atomic",
949 { $field => $value } );
957 __PACKAGE__->register_method(
958 method => "get_org_tree",
959 api_name => "open-ils.actor.org_tree.retrieve",
961 note => "Returns the entire org tree structure",
965 my( $self, $client) = @_;
967 $cache = OpenSRF::Utils::Cache->new("global", 0) unless $cache;
968 my $tree = $cache->get_cache('orgtree');
969 return $tree if $tree;
971 $tree = new_editor()->search_actor_org_unit(
973 {"parent_ou" => undef },
976 flesh_fields => { aou => ['children'] },
977 order_by => { aou => 'name'}
982 $cache->put_cache('orgtree', $tree);
987 # turns an org list into an org tree
990 my( $self, $orglist) = @_;
992 return $orglist unless (
993 ref($orglist) and @$orglist > 1 );
996 $a->ou_type <=> $b->ou_type ||
997 $a->name cmp $b->name } @$orglist;
999 for my $org (@list) {
1001 next unless ($org and defined($org->parent_ou));
1002 my ($parent) = grep { $_->id == $org->parent_ou } @list;
1003 next unless $parent;
1005 $parent->children([]) unless defined($parent->children);
1006 push( @{$parent->children}, $org );
1014 __PACKAGE__->register_method(
1015 method => "get_org_descendants",
1016 api_name => "open-ils.actor.org_tree.descendants.retrieve"
1019 # depth is optional. org_unit is the id
1020 sub get_org_descendants {
1021 my( $self, $client, $org_unit, $depth ) = @_;
1022 my $orglist = $apputils->simple_scalar_request(
1024 "open-ils.storage.actor.org_unit.descendants.atomic",
1025 $org_unit, $depth );
1026 return $self->build_org_tree($orglist);
1030 __PACKAGE__->register_method(
1031 method => "get_org_ancestors",
1032 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
1035 # depth is optional. org_unit is the id
1036 sub get_org_ancestors {
1037 my( $self, $client, $org_unit, $depth ) = @_;
1038 my $orglist = $apputils->simple_scalar_request(
1040 "open-ils.storage.actor.org_unit.ancestors.atomic",
1041 $org_unit, $depth );
1042 return $self->build_org_tree($orglist);
1046 __PACKAGE__->register_method(
1047 method => "get_standings",
1048 api_name => "open-ils.actor.standings.retrieve"
1053 return $user_standings if $user_standings;
1054 return $user_standings =
1055 $apputils->simple_scalar_request(
1057 "open-ils.cstore.direct.config.standing.search.atomic",
1058 { id => { "!=" => undef } }
1064 __PACKAGE__->register_method(
1065 method => "get_my_org_path",
1066 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1069 sub get_my_org_path {
1070 my( $self, $client, $user_session, $org_id ) = @_;
1071 my $user_obj = $apputils->check_user_session($user_session);
1072 if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
1074 return $apputils->simple_scalar_request(
1076 "open-ils.storage.actor.org_unit.full_path.atomic",
1081 __PACKAGE__->register_method(
1082 method => "patron_adv_search",
1083 api_name => "open-ils.actor.patron.search.advanced" );
1084 sub patron_adv_search {
1085 my( $self, $client, $auth, $search_hash, $search_limit, $search_sort, $include_inactive ) = @_;
1086 my $e = new_editor(authtoken=>$auth);
1087 return $e->event unless $e->checkauth;
1088 return $e->event unless $e->allowed('VIEW_USER');
1089 return $U->storagereq(
1090 "open-ils.storage.actor.user.crazy_search",
1091 $search_hash, $search_limit, $search_sort, $include_inactive);
1096 sub _verify_password {
1097 my($user_session, $password) = @_;
1098 my $user_obj = $apputils->check_user_session($user_session);
1100 #grab the user with password
1101 $user_obj = $apputils->simple_scalar_request(
1103 "open-ils.cstore.direct.actor.user.retrieve",
1106 if($user_obj->passwd eq $password) {
1114 __PACKAGE__->register_method(
1115 method => "update_password",
1116 api_name => "open-ils.actor.user.password.update");
1118 __PACKAGE__->register_method(
1119 method => "update_password",
1120 api_name => "open-ils.actor.user.username.update");
1122 __PACKAGE__->register_method(
1123 method => "update_password",
1124 api_name => "open-ils.actor.user.email.update");
1126 sub update_password {
1127 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1131 my $user_obj = $apputils->check_user_session($user_session);
1133 if($self->api_name =~ /password/o) {
1135 #make sure they know the current password
1136 if(!_verify_password($user_session, md5_hex($current_password))) {
1137 return OpenILS::Event->new('INCORRECT_PASSWORD');
1140 $logger->debug("update_password setting new password $new_value");
1141 $user_obj->passwd($new_value);
1143 } elsif($self->api_name =~ /username/o) {
1144 my $users = search_username(undef, undef, $new_value);
1145 if( $users and $users->[0] ) {
1146 return OpenILS::Event->new('USERNAME_EXISTS');
1148 $user_obj->usrname($new_value);
1150 } elsif($self->api_name =~ /email/o) {
1151 #warn "Updating email to $new_value\n";
1152 $user_obj->email($new_value);
1155 my $session = $apputils->start_db_session();
1157 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1158 return $evt if $evt;
1160 $apputils->commit_db_session($session);
1162 if($user_obj) { return 1; }
1167 __PACKAGE__->register_method(
1168 method => "check_user_perms",
1169 api_name => "open-ils.actor.user.perm.check",
1170 notes => <<" NOTES");
1171 Takes a login session, user id, an org id, and an array of perm type strings. For each
1172 perm type, if the user does *not* have the given permission it is added
1173 to a list which is returned from the method. If all permissions
1174 are allowed, an empty list is returned
1175 if the logged in user does not match 'user_id', then the logged in user must
1176 have VIEW_PERMISSION priveleges.
1179 sub check_user_perms {
1180 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1182 my( $staff, $evt ) = $apputils->checkses($login_session);
1183 return $evt if $evt;
1185 if($staff->id ne $user_id) {
1186 if( $evt = $apputils->check_perms(
1187 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1193 for my $perm (@$perm_types) {
1194 if($apputils->check_perms($user_id, $org_id, $perm)) {
1195 push @not_allowed, $perm;
1199 return \@not_allowed
1202 __PACKAGE__->register_method(
1203 method => "check_user_perms2",
1204 api_name => "open-ils.actor.user.perm.check.multi_org",
1206 Checks the permissions on a list of perms and orgs for a user
1207 @param authtoken The login session key
1208 @param user_id The id of the user to check
1209 @param orgs The array of org ids
1210 @param perms The array of permission names
1211 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1212 if the logged in user does not match 'user_id', then the logged in user must
1213 have VIEW_PERMISSION priveleges.
1216 sub check_user_perms2 {
1217 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1219 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1220 $authtoken, $user_id, 'VIEW_PERMISSION' );
1221 return $evt if $evt;
1224 for my $org (@$orgs) {
1225 for my $perm (@$perms) {
1226 if($apputils->check_perms($user_id, $org, $perm)) {
1227 push @not_allowed, [ $org, $perm ];
1232 return \@not_allowed
1236 __PACKAGE__->register_method(
1237 method => 'check_user_perms3',
1238 api_name => 'open-ils.actor.user.perm.highest_org',
1240 Returns the highest org unit id at which a user has a given permission
1241 If the requestor does not match the target user, the requestor must have
1242 'VIEW_PERMISSION' rights at the home org unit of the target user
1243 @param authtoken The login session key
1244 @param userid The id of the user in question
1245 @param perm The permission to check
1246 @return The org unit highest in the org tree within which the user has
1247 the requested permission
1250 sub check_user_perms3 {
1251 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1253 my( $staff, $target, $org, $evt );
1255 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1256 $authtoken, $userid, 'VIEW_PERMISSION' );
1257 return $evt if $evt;
1259 my $tree = $self->get_org_tree();
1260 return _find_highest_perm_org( $perm, $userid, $target->ws_ou, $tree );
1264 sub _find_highest_perm_org {
1265 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1266 my $org = $apputils->find_org($org_tree, $start_org );
1270 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1272 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1278 __PACKAGE__->register_method(
1279 method => 'check_user_perms4',
1280 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1282 Returns the highest org unit id at which a user has a given permission
1283 If the requestor does not match the target user, the requestor must have
1284 'VIEW_PERMISSION' rights at the home org unit of the target user
1285 @param authtoken The login session key
1286 @param userid The id of the user in question
1287 @param perms An array of perm names to check
1288 @return An array of orgId's representing the org unit
1289 highest in the org tree within which the user has the requested permission
1290 The arrah of orgId's has matches the order of the perms array
1293 sub check_user_perms4 {
1294 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1296 my( $staff, $target, $org, $evt );
1298 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1299 $authtoken, $userid, 'VIEW_PERMISSION' );
1300 return $evt if $evt;
1303 return [] unless ref($perms);
1304 my $tree = $self->get_org_tree();
1306 for my $p (@$perms) {
1307 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1315 __PACKAGE__->register_method(
1316 method => "user_fines_summary",
1317 api_name => "open-ils.actor.user.fines.summary",
1318 notes => <<" NOTES");
1319 Returns a short summary of the users total open fines, excluding voided fines
1320 Params are login_session, user_id
1321 Returns a 'mous' object.
1324 sub user_fines_summary {
1325 my( $self, $client, $login_session, $user_id ) = @_;
1327 my $user_obj = $apputils->check_user_session($login_session);
1328 if($user_obj->id ne $user_id) {
1329 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1330 return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY");
1334 return $apputils->simple_scalar_request(
1336 "open-ils.cstore.direct.money.open_user_summary.search",
1337 { usr => $user_id } );
1344 __PACKAGE__->register_method(
1345 method => "user_transactions",
1346 api_name => "open-ils.actor.user.transactions",
1347 notes => <<" NOTES");
1348 Returns a list of open user transactions (mbts objects);
1349 Params are login_session, user_id
1350 Optional third parameter is the transactions type. defaults to all
1353 __PACKAGE__->register_method(
1354 method => "user_transactions",
1355 api_name => "open-ils.actor.user.transactions.have_charge",
1356 notes => <<" NOTES");
1357 Returns a list of all open user transactions (mbts objects) that have an initial charge
1358 Params are login_session, user_id
1359 Optional third parameter is the transactions type. defaults to all
1362 __PACKAGE__->register_method(
1363 method => "user_transactions",
1364 api_name => "open-ils.actor.user.transactions.have_balance",
1365 notes => <<" NOTES");
1366 Returns a list of all open user transactions (mbts objects) that have a balance
1367 Params are login_session, user_id
1368 Optional third parameter is the transactions type. defaults to all
1371 __PACKAGE__->register_method(
1372 method => "user_transactions",
1373 api_name => "open-ils.actor.user.transactions.fleshed",
1374 notes => <<" NOTES");
1375 Returns an object/hash of transaction, circ, title where transaction = an open
1376 user transactions (mbts objects), circ is the attached circluation, and title
1377 is the title the circ points to
1378 Params are login_session, user_id
1379 Optional third parameter is the transactions type. defaults to all
1382 __PACKAGE__->register_method(
1383 method => "user_transactions",
1384 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1385 notes => <<" NOTES");
1386 Returns an object/hash of transaction, circ, title where transaction = an open
1387 user transactions that has an initial charge (mbts objects), circ is the
1388 attached circluation, and title is the title the circ points to
1389 Params are login_session, user_id
1390 Optional third parameter is the transactions type. defaults to all
1393 __PACKAGE__->register_method(
1394 method => "user_transactions",
1395 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1396 notes => <<" NOTES");
1397 Returns an object/hash of transaction, circ, title where transaction = an open
1398 user transaction that has a balance (mbts objects), circ is the attached
1399 circluation, and title is the title the circ points to
1400 Params are login_session, user_id
1401 Optional third parameter is the transaction type. defaults to all
1404 __PACKAGE__->register_method(
1405 method => "user_transactions",
1406 api_name => "open-ils.actor.user.transactions.count",
1407 notes => <<" NOTES");
1408 Returns an object/hash of transaction, circ, title where transaction = an open
1409 user transactions (mbts objects), circ is the attached circluation, and title
1410 is the title the circ points to
1411 Params are login_session, user_id
1412 Optional third parameter is the transactions type. defaults to all
1415 __PACKAGE__->register_method(
1416 method => "user_transactions",
1417 api_name => "open-ils.actor.user.transactions.have_charge.count",
1418 notes => <<" NOTES");
1419 Returns an object/hash of transaction, circ, title where transaction = an open
1420 user transactions that has an initial charge (mbts objects), circ is the
1421 attached circluation, and title is the title the circ points to
1422 Params are login_session, user_id
1423 Optional third parameter is the transactions type. defaults to all
1426 __PACKAGE__->register_method(
1427 method => "user_transactions",
1428 api_name => "open-ils.actor.user.transactions.have_balance.count",
1429 notes => <<" NOTES");
1430 Returns an object/hash of transaction, circ, title where transaction = an open
1431 user transaction that has a balance (mbts objects), circ is the attached
1432 circluation, and title is the title the circ points to
1433 Params are login_session, user_id
1434 Optional third parameter is the transaction type. defaults to all
1437 __PACKAGE__->register_method(
1438 method => "user_transactions",
1439 api_name => "open-ils.actor.user.transactions.have_balance.total",
1440 notes => <<" NOTES");
1441 Returns an object/hash of transaction, circ, title where transaction = an open
1442 user transaction that has a balance (mbts objects), circ is the attached
1443 circluation, and title is the title the circ points to
1444 Params are login_session, user_id
1445 Optional third parameter is the transaction type. defaults to all
1450 sub user_transactions {
1451 my( $self, $client, $login_session, $user_id, $type ) = @_;
1453 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1454 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1455 return $evt if $evt;
1457 my $api = $self->api_name();
1461 if(defined($type)) { @xact = (xact_type => $type);
1463 } else { @xact = (); }
1465 if($api =~ /have_charge/o) {
1467 $trans = $apputils->simple_scalar_request(
1469 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1470 { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1472 } elsif($api =~ /have_balance/o) {
1474 $trans = $apputils->simple_scalar_request(
1476 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1477 { usr => $user_id, balance_owed => { "<>" => 0 }, @xact });
1481 $trans = $apputils->simple_scalar_request(
1483 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1484 { usr => $user_id, @xact });
1487 if($api =~ /total/o) {
1489 for my $t (@$trans) {
1490 $total += $t->balance_owed;
1493 $logger->debug("Total balance owed by user $user_id: $total");
1497 if($api =~ /count/o) { return scalar @$trans; }
1498 if($api !~ /fleshed/o) { return $trans; }
1501 for my $t (@$trans) {
1503 if( $t->xact_type ne 'circulation' ) {
1504 push @resp, {transaction => $t};
1508 my $circ = $apputils->simple_scalar_request(
1510 "open-ils.cstore.direct.action.circulation.retrieve",
1515 my $title = $apputils->simple_scalar_request(
1517 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1518 $circ->target_copy );
1522 my $u = OpenILS::Utils::ModsParser->new();
1523 $u->start_mods_batch($title->marc());
1524 my $mods = $u->finish_mods_batch();
1525 $mods->doc_id($title->id) if $mods;
1527 push @resp, {transaction => $t, circ => $circ, record => $mods };
1535 __PACKAGE__->register_method(
1536 method => "user_transaction_retrieve",
1537 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1539 notes => <<" NOTES");
1540 Returns a fleshedtransaction record
1542 __PACKAGE__->register_method(
1543 method => "user_transaction_retrieve",
1544 api_name => "open-ils.actor.user.transaction.retrieve",
1546 notes => <<" NOTES");
1547 Returns a transaction record
1549 sub user_transaction_retrieve {
1550 my( $self, $client, $login_session, $bill_id ) = @_;
1552 my $trans = $apputils->simple_scalar_request(
1554 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1558 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1559 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1560 return $evt if $evt;
1562 my $api = $self->api_name();
1563 if($api !~ /fleshed/o) { return $trans; }
1565 if( $trans->xact_type ne 'circulation' ) {
1566 $logger->debug("Returning non-circ transaction");
1567 return {transaction => $trans};
1570 my $circ = $apputils->simple_scalar_request(
1572 "open-ils..direct.action.circulation.retrieve",
1575 return {transaction => $trans} unless $circ;
1576 $logger->debug("Found the circ transaction");
1578 my $title = $apputils->simple_scalar_request(
1580 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1581 $circ->target_copy );
1583 return {transaction => $trans, circ => $circ } unless $title;
1584 $logger->debug("Found the circ title");
1588 my $u = OpenILS::Utils::ModsParser->new();
1589 $u->start_mods_batch($title->marc());
1590 $mods = $u->finish_mods_batch();
1592 if ($title->id == OILS_PRECAT_RECORD) {
1593 my $copy = $apputils->simple_scalar_request(
1595 "open-ils.cstore.direct.asset.copy.retrieve",
1596 $circ->target_copy );
1598 $mods = new Fieldmapper::metabib::virtual_record;
1599 $mods->doc_id(OILS_PRECAT_RECORD);
1600 $mods->title($copy->dummy_title);
1601 $mods->author($copy->dummy_author);
1605 $logger->debug("MODSized the circ title");
1607 return {transaction => $trans, circ => $circ, record => $mods };
1611 __PACKAGE__->register_method(
1612 method => "hold_request_count",
1613 api_name => "open-ils.actor.user.hold_requests.count",
1615 notes => <<" NOTES");
1616 Returns hold ready/total counts
1618 sub hold_request_count {
1619 my( $self, $client, $login_session, $userid ) = @_;
1621 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1622 $login_session, $userid, 'VIEW_HOLD' );
1623 return $evt if $evt;
1626 my $holds = $apputils->simple_scalar_request(
1628 "open-ils.cstore.direct.action.hold_request.search.atomic",
1631 fulfillment_time => {"=" => undef },
1632 cancel_time => undef,
1637 for my $h (@$holds) {
1638 next unless $h->capture_time and $h->current_copy;
1640 my $copy = $apputils->simple_scalar_request(
1642 "open-ils.cstore.direct.asset.copy.retrieve",
1646 if ($copy and $copy->status == 8) {
1651 return { total => scalar(@$holds), ready => scalar(@ready) };
1655 __PACKAGE__->register_method(
1656 method => "checkedout_count",
1657 api_name => "open-ils.actor.user.checked_out.count__",
1659 notes => <<" NOTES");
1660 Returns a transaction record
1664 sub checkedout_count {
1665 my( $self, $client, $login_session, $userid ) = @_;
1667 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1668 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1669 return $evt if $evt;
1671 my $circs = $apputils->simple_scalar_request(
1673 "open-ils.cstore.direct.action.circulation.search.atomic",
1674 { usr => $userid, stop_fines => undef }
1675 #{ usr => $userid, checkin_time => {"=" => undef } }
1678 my $parser = DateTime::Format::ISO8601->new;
1681 for my $c (@$circs) {
1682 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1683 my $due = $due_dt->epoch;
1685 if ($due < DateTime->today->epoch) {
1690 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1694 __PACKAGE__->register_method(
1695 method => "checked_out",
1696 api_name => "open-ils.actor.user.checked_out",
1699 Returns a structure of circulations objects sorted by
1700 out, overdue, lost, claims_returned, long_overdue.
1701 A list of IDs are returned of each type.
1702 lost, long_overdue, and claims_returned circ will not
1703 be "finished" (there is an outstanding balance or some
1704 other pending action on the circ).
1706 The .count method also includes a 'total' field which
1707 sums all "open" circs
1711 __PACKAGE__->register_method(
1712 method => "checked_out",
1713 api_name => "open-ils.actor.user.checked_out.count",
1715 signature => q/@see open-ils.actor.user.checked_out/
1719 my( $self, $conn, $auth, $userid ) = @_;
1721 my $e = new_editor(authtoken=>$auth);
1722 return $e->event unless $e->checkauth;
1724 if( $userid ne $e->requestor->id ) {
1725 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1728 my $count = $self->api_name =~ /count/;
1729 return _checked_out( $count, $e, $userid );
1733 my( $iscount, $e, $userid ) = @_;
1735 my $circs = $e->search_action_circulation(
1736 { usr => $userid, stop_fines => undef });
1738 my $parser = DateTime::Format::ISO8601->new;
1740 # split the circs up into overdue and not-overdue circs
1742 for my $c (@$circs) {
1743 if( $c->due_date ) {
1744 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1745 my $due = $due_dt->epoch;
1746 if ($due < DateTime->today->epoch) {
1747 push @overdue, $c->id;
1756 # grab all of the lost, claims-returned, and longoverdue circs
1757 #my $open = $e->search_action_circulation(
1758 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1761 # these items have stop_fines, but no xact_finish, so money
1762 # is owed on them and they have not been checked in
1763 my $open = $e->search_action_circulation(
1766 stop_fines => { '!=' => undef },
1767 xact_finish => undef,
1768 checkin_time => undef,
1773 my( @lost, @cr, @lo );
1774 for my $c (@$open) {
1775 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1776 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1777 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1783 total => @$circs + @lost + @cr + @lo,
1784 out => scalar(@out),
1785 overdue => scalar(@overdue),
1786 lost => scalar(@lost),
1787 claims_returned => scalar(@cr),
1788 long_overdue => scalar(@lo)
1794 overdue => \@overdue,
1796 claims_returned => \@cr,
1797 long_overdue => \@lo
1803 __PACKAGE__->register_method(
1804 method => "checked_in_with_fines",
1805 api_name => "open-ils.actor.user.checked_in_with_fines",
1807 signature => q/@see open-ils.actor.user.checked_out/
1809 sub checked_in_with_fines {
1810 my( $self, $conn, $auth, $userid ) = @_;
1812 my $e = new_editor(authtoken=>$auth);
1813 return $e->event unless $e->checkauth;
1815 if( $userid ne $e->requestor->id ) {
1816 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1819 # money is owed on these items and they are checked in
1820 my $open = $e->search_action_circulation(
1823 xact_finish => undef,
1824 checkin_time => { "!=" => undef },
1829 my( @lost, @cr, @lo );
1830 for my $c (@$open) {
1831 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1832 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1833 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1838 claims_returned => \@cr,
1839 long_overdue => \@lo
1851 __PACKAGE__->register_method(
1852 method => "user_transaction_history",
1853 api_name => "open-ils.actor.user.transactions.history",
1855 notes => <<" NOTES");
1856 Returns a list of billable transaction ids for a user, optionally by type
1858 __PACKAGE__->register_method(
1859 method => "user_transaction_history",
1860 api_name => "open-ils.actor.user.transactions.history.have_charge",
1862 notes => <<" NOTES");
1863 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1865 __PACKAGE__->register_method(
1866 method => "user_transaction_history",
1867 api_name => "open-ils.actor.user.transactions.history.have_balance",
1869 notes => <<" NOTES");
1870 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1874 sub _user_transaction_history {
1875 my( $self, $client, $login_session, $user_id, $type ) = @_;
1877 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1878 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1879 return $evt if $evt;
1881 my $api = $self->api_name();
1886 @xact = (xact_type => $type) if(defined($type));
1887 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1888 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1890 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1892 my $trans = $apputils->simple_scalar_request(
1894 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1895 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1897 return [ map { $_->id } @$trans ];
1902 sub user_transaction_history {
1903 my( $self, $conn, $auth, $userid, $type ) = @_;
1904 my $e = new_editor(authtoken=>$auth);
1905 return $e->event unless $e->checkauth;
1906 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1908 my $api = $self->api_name;
1909 my @xact = (xact_type => $type) if(defined($type));
1910 my @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1911 my @charge = (last_billing_ts => { "!=" => undef }) if $api =~ /have_charge/;
1913 return $e->search_money_billable_transaction_summary(
1915 { usr => $userid, @xact, @charge, @balance },
1916 { order_by => 'xact_start DESC' }
1922 __PACKAGE__->register_method(
1923 method => "user_perms",
1924 api_name => "open-ils.actor.permissions.user_perms.retrieve",
1926 notes => <<" NOTES");
1927 Returns a list of permissions
1930 my( $self, $client, $authtoken, $user ) = @_;
1932 my( $staff, $evt ) = $apputils->checkses($authtoken);
1933 return $evt if $evt;
1935 $user ||= $staff->id;
1937 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1941 return $apputils->simple_scalar_request(
1943 "open-ils.storage.permission.user_perms.atomic",
1947 __PACKAGE__->register_method(
1948 method => "retrieve_perms",
1949 api_name => "open-ils.actor.permissions.retrieve",
1950 notes => <<" NOTES");
1951 Returns a list of permissions
1953 sub retrieve_perms {
1954 my( $self, $client ) = @_;
1955 return $apputils->simple_scalar_request(
1957 "open-ils.cstore.direct.permission.perm_list.search.atomic",
1958 { id => { '!=' => undef } }
1962 __PACKAGE__->register_method(
1963 method => "retrieve_groups",
1964 api_name => "open-ils.actor.groups.retrieve",
1965 notes => <<" NOTES");
1966 Returns a list of user groupss
1968 sub retrieve_groups {
1969 my( $self, $client ) = @_;
1970 return new_editor()->retrieve_all_permission_grp_tree();
1973 __PACKAGE__->register_method(
1974 method => "retrieve_org_address",
1975 api_name => "open-ils.actor.org_unit.address.retrieve",
1976 notes => <<' NOTES');
1977 Returns an org_unit address by ID
1978 @param An org_address ID
1980 sub retrieve_org_address {
1981 my( $self, $client, $id ) = @_;
1982 return $apputils->simple_scalar_request(
1984 "open-ils.cstore.direct.actor.org_address.retrieve",
1989 __PACKAGE__->register_method(
1990 method => "retrieve_groups_tree",
1991 api_name => "open-ils.actor.groups.tree.retrieve",
1992 notes => <<" NOTES");
1993 Returns a list of user groups
1995 sub retrieve_groups_tree {
1996 my( $self, $client ) = @_;
1997 return new_editor()->search_permission_grp_tree(
2002 flesh_fields => { pgt => ["children"] },
2003 order_by => { pgt => 'name'}
2010 # turns an org list into an org tree
2012 sub build_group_tree {
2014 my( $self, $grplist) = @_;
2016 return $grplist unless (
2017 ref($grplist) and @$grplist > 1 );
2019 my @list = sort { $a->name cmp $b->name } @$grplist;
2022 for my $grp (@list) {
2024 if ($grp and !defined($grp->parent)) {
2028 my ($parent) = grep { $_->id == $grp->parent} @list;
2030 $parent->children([]) unless defined($parent->children);
2031 push( @{$parent->children}, $grp );
2039 __PACKAGE__->register_method(
2040 method => "add_user_to_groups",
2041 api_name => "open-ils.actor.user.set_groups",
2042 notes => <<" NOTES");
2043 Adds a user to one or more permission groups
2046 sub add_user_to_groups {
2047 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2049 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2050 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2051 return $evt if $evt;
2053 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2054 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2055 return $evt if $evt;
2057 $apputils->simplereq(
2059 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2061 for my $group (@$groups) {
2062 my $link = Fieldmapper::permission::usr_grp_map->new;
2064 $link->usr($userid);
2066 my $id = $apputils->simplereq(
2068 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2074 __PACKAGE__->register_method(
2075 method => "get_user_perm_groups",
2076 api_name => "open-ils.actor.user.get_groups",
2077 notes => <<" NOTES");
2078 Retrieve a user's permission groups.
2082 sub get_user_perm_groups {
2083 my( $self, $client, $authtoken, $userid ) = @_;
2085 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2086 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2087 return $evt if $evt;
2089 return $apputils->simplereq(
2091 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2096 __PACKAGE__->register_method (
2097 method => 'register_workstation',
2098 api_name => 'open-ils.actor.workstation.register.override',
2099 signature => q/@see open-ils.actor.workstation.register/);
2101 __PACKAGE__->register_method (
2102 method => 'register_workstation',
2103 api_name => 'open-ils.actor.workstation.register',
2105 Registers a new workstion in the system
2106 @param authtoken The login session key
2107 @param name The name of the workstation id
2108 @param owner The org unit that owns this workstation
2109 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2110 if the name is already in use.
2113 sub _register_workstation {
2114 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2115 my( $requestor, $evt ) = $U->checkses($authtoken);
2116 return $evt if $evt;
2117 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2118 return $evt if $evt;
2120 my $ws = $U->cstorereq(
2121 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2122 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2124 $ws = Fieldmapper::actor::workstation->new;
2125 $ws->owning_lib($owner);
2128 my $id = $U->storagereq(
2129 'open-ils.storage.direct.actor.workstation.create', $ws );
2130 return $U->DB_UPDATE_FAILED($ws) unless $id;
2136 sub register_workstation {
2137 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2139 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2140 return $e->event unless $e->checkauth;
2141 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2142 my $existing = $e->search_actor_workstation({name => $name});
2145 if( $self->api_name =~ /override/o ) {
2146 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2147 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2149 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2153 my $ws = Fieldmapper::actor::workstation->new;
2154 $ws->owning_lib($owner);
2156 $e->create_actor_workstation($ws) or return $e->event;
2158 return $ws->id; # note: editor sets the id on the new object for us
2162 __PACKAGE__->register_method (
2163 method => 'fetch_patron_note',
2164 api_name => 'open-ils.actor.note.retrieve.all',
2166 Returns a list of notes for a given user
2167 Requestor must have VIEW_USER permission if pub==false and
2168 @param authtoken The login session key
2169 @param args Hash of params including
2170 patronid : the patron's id
2171 pub : true if retrieving only public notes
2175 sub fetch_patron_note {
2176 my( $self, $conn, $authtoken, $args ) = @_;
2177 my $patronid = $$args{patronid};
2179 my($reqr, $evt) = $U->checkses($authtoken);
2182 ($patron, $evt) = $U->fetch_user($patronid);
2183 return $evt if $evt;
2186 if( $patronid ne $reqr->id ) {
2187 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2188 return $evt if $evt;
2190 return $U->cstorereq(
2191 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2192 { usr => $patronid, pub => 't' } );
2195 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2196 return $evt if $evt;
2198 return $U->cstorereq(
2199 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2202 __PACKAGE__->register_method (
2203 method => 'create_user_note',
2204 api_name => 'open-ils.actor.note.create',
2206 Creates a new note for the given user
2207 @param authtoken The login session key
2208 @param note The note object
2211 sub create_user_note {
2212 my( $self, $conn, $authtoken, $note ) = @_;
2213 my( $reqr, $patron, $evt ) =
2214 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2215 return $evt if $evt;
2216 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2218 $note->creator($reqr->id);
2219 my $id = $U->storagereq(
2220 'open-ils.storage.direct.actor.usr_note.create', $note );
2221 return $U->DB_UPDATE_FAILED($note) unless $id;
2226 __PACKAGE__->register_method (
2227 method => 'delete_user_note',
2228 api_name => 'open-ils.actor.note.delete',
2230 Deletes a note for the given user
2231 @param authtoken The login session key
2232 @param noteid The note id
2235 sub delete_user_note {
2236 my( $self, $conn, $authtoken, $noteid ) = @_;
2238 my $note = $U->cstorereq(
2239 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2240 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2242 my( $reqr, $patron, $evt ) =
2243 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2244 return $evt if $evt;
2245 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2247 my $stat = $U->storagereq(
2248 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2249 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2254 __PACKAGE__->register_method (
2255 method => 'update_user_note',
2256 api_name => 'open-ils.actor.note.update',
2258 @param authtoken The login session key
2259 @param note The note
2263 sub update_user_note {
2264 my( $self, $conn, $auth, $note ) = @_;
2265 my $e = new_editor(authtoken=>$auth, xact=>1);
2266 return $e->event unless $e->checkauth;
2267 my $patron = $e->retrieve_actor_user($note->usr)
2268 or return $e->event;
2269 return $e->event unless
2270 $e->allowed('UPDATE_USER', $patron->home_ou);
2271 $e->update_actor_user_note($note)
2272 or return $e->event;
2280 __PACKAGE__->register_method (
2281 method => 'create_closed_date',
2282 api_name => 'open-ils.actor.org_unit.closed_date.create',
2284 Creates a new closing entry for the given org_unit
2285 @param authtoken The login session key
2286 @param note The closed_date object
2289 sub create_closed_date {
2290 my( $self, $conn, $authtoken, $cd ) = @_;
2292 my( $user, $evt ) = $U->checkses($authtoken);
2293 return $evt if $evt;
2295 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2296 return $evt if $evt;
2298 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2300 my $id = $U->storagereq(
2301 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2302 return $U->DB_UPDATE_FAILED($cd) unless $id;
2307 __PACKAGE__->register_method (
2308 method => 'delete_closed_date',
2309 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2311 Deletes a closing entry for the given org_unit
2312 @param authtoken The login session key
2313 @param noteid The close_date id
2316 sub delete_closed_date {
2317 my( $self, $conn, $authtoken, $cd ) = @_;
2319 my( $user, $evt ) = $U->checkses($authtoken);
2320 return $evt if $evt;
2323 ($cd_obj, $evt) = fetch_closed_date($cd);
2324 return $evt if $evt;
2326 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2327 return $evt if $evt;
2329 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2331 my $stat = $U->storagereq(
2332 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2333 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2338 __PACKAGE__->register_method(
2339 method => 'usrname_exists',
2340 api_name => 'open-ils.actor.username.exists',
2342 Returns 1 if the requested username exists, returns 0 otherwise
2346 sub usrname_exists {
2347 my( $self, $conn, $auth, $usrname ) = @_;
2348 my $e = new_editor(authtoken=>$auth);
2349 return $e->event unless $e->checkauth;
2350 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2351 return $$a[0] if $a and @$a;
2355 __PACKAGE__->register_method(
2356 method => 'barcode_exists',
2357 api_name => 'open-ils.actor.barcode.exists',
2359 Returns 1 if the requested barcode exists, returns 0 otherwise
2363 sub barcode_exists {
2364 my( $self, $conn, $auth, $barcode ) = @_;
2365 my $e = new_editor(authtoken=>$auth);
2366 return $e->event unless $e->checkauth;
2367 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2368 return $$a[0] if $a and @$a;
2373 __PACKAGE__->register_method(
2374 method => 'retrieve_net_levels',
2375 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2378 sub retrieve_net_levels {
2379 my( $self, $conn, $auth ) = @_;
2380 my $e = new_editor(authtoken=>$auth);
2381 return $e->event unless $e->checkauth;
2382 return $e->retrieve_all_config_net_access_level();
2386 __PACKAGE__->register_method(
2387 method => 'fetch_org_by_shortname',
2388 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2390 sub fetch_org_by_shortname {
2391 my( $self, $conn, $sname ) = @_;
2392 my $e = new_editor();
2393 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2394 return $e->event unless $org;