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 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1744 my $due = $due_dt->epoch;
1745 if ($due < DateTime->today->epoch) {
1746 push @overdue, $c->id;
1752 # grab all of the lost, claims-returned, and longoverdue circs
1753 #my $open = $e->search_action_circulation(
1754 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1757 # these items have stop_fines, but no xact_finish, so money
1758 # is owed on them and they have not been checked in
1759 my $open = $e->search_action_circulation(
1762 stop_fines => { '!=' => undef },
1763 xact_finish => undef,
1764 checkin_time => undef,
1769 my( @lost, @cr, @lo );
1770 for my $c (@$open) {
1771 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1772 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1773 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1779 total => @$circs + @lost + @cr + @lo,
1780 out => scalar(@out),
1781 overdue => scalar(@overdue),
1782 lost => scalar(@lost),
1783 claims_returned => scalar(@cr),
1784 long_overdue => scalar(@lo)
1790 overdue => \@overdue,
1792 claims_returned => \@cr,
1793 long_overdue => \@lo
1799 __PACKAGE__->register_method(
1800 method => "checked_in_with_fines",
1801 api_name => "open-ils.actor.user.checked_in_with_fines",
1803 signature => q/@see open-ils.actor.user.checked_out/
1805 sub checked_in_with_fines {
1806 my( $self, $conn, $auth, $userid ) = @_;
1808 my $e = new_editor(authtoken=>$auth);
1809 return $e->event unless $e->checkauth;
1811 if( $userid ne $e->requestor->id ) {
1812 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1815 # money is owed on these items and they are checked in
1816 my $open = $e->search_action_circulation(
1819 xact_finish => undef,
1820 checkin_time => { "!=" => undef },
1825 my( @lost, @cr, @lo );
1826 for my $c (@$open) {
1827 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1828 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1829 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1834 claims_returned => \@cr,
1835 long_overdue => \@lo
1847 __PACKAGE__->register_method(
1848 method => "user_transaction_history",
1849 api_name => "open-ils.actor.user.transactions.history",
1851 notes => <<" NOTES");
1852 Returns a list of billable transaction ids for a user, optionally by type
1854 __PACKAGE__->register_method(
1855 method => "user_transaction_history",
1856 api_name => "open-ils.actor.user.transactions.history.have_charge",
1858 notes => <<" NOTES");
1859 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1861 __PACKAGE__->register_method(
1862 method => "user_transaction_history",
1863 api_name => "open-ils.actor.user.transactions.history.have_balance",
1865 notes => <<" NOTES");
1866 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1870 sub _user_transaction_history {
1871 my( $self, $client, $login_session, $user_id, $type ) = @_;
1873 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1874 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1875 return $evt if $evt;
1877 my $api = $self->api_name();
1882 @xact = (xact_type => $type) if(defined($type));
1883 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1884 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1886 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1888 my $trans = $apputils->simple_scalar_request(
1890 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1891 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1893 return [ map { $_->id } @$trans ];
1898 sub user_transaction_history {
1899 my( $self, $conn, $auth, $userid, $type ) = @_;
1900 my $e = new_editor(authtoken=>$auth);
1901 return $e->event unless $e->checkauth;
1902 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1904 my $api = $self->api_name;
1905 my @xact = (xact_type => $type) if(defined($type));
1906 my @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1907 my @charge = (last_billing_ts => { "!=" => undef }) if $api =~ /have_charge/;
1909 return $e->search_money_billable_transaction_summary(
1911 { usr => $userid, @xact, @charge, @balance },
1912 { order_by => 'xact_start DESC' }
1918 __PACKAGE__->register_method(
1919 method => "user_perms",
1920 api_name => "open-ils.actor.permissions.user_perms.retrieve",
1922 notes => <<" NOTES");
1923 Returns a list of permissions
1926 my( $self, $client, $authtoken, $user ) = @_;
1928 my( $staff, $evt ) = $apputils->checkses($authtoken);
1929 return $evt if $evt;
1931 $user ||= $staff->id;
1933 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1937 return $apputils->simple_scalar_request(
1939 "open-ils.storage.permission.user_perms.atomic",
1943 __PACKAGE__->register_method(
1944 method => "retrieve_perms",
1945 api_name => "open-ils.actor.permissions.retrieve",
1946 notes => <<" NOTES");
1947 Returns a list of permissions
1949 sub retrieve_perms {
1950 my( $self, $client ) = @_;
1951 return $apputils->simple_scalar_request(
1953 "open-ils.cstore.direct.permission.perm_list.search.atomic",
1954 { id => { '!=' => undef } }
1958 __PACKAGE__->register_method(
1959 method => "retrieve_groups",
1960 api_name => "open-ils.actor.groups.retrieve",
1961 notes => <<" NOTES");
1962 Returns a list of user groupss
1964 sub retrieve_groups {
1965 my( $self, $client ) = @_;
1966 return new_editor()->retrieve_all_permission_grp_tree();
1969 __PACKAGE__->register_method(
1970 method => "retrieve_org_address",
1971 api_name => "open-ils.actor.org_unit.address.retrieve",
1972 notes => <<' NOTES');
1973 Returns an org_unit address by ID
1974 @param An org_address ID
1976 sub retrieve_org_address {
1977 my( $self, $client, $id ) = @_;
1978 return $apputils->simple_scalar_request(
1980 "open-ils.cstore.direct.actor.org_address.retrieve",
1985 __PACKAGE__->register_method(
1986 method => "retrieve_groups_tree",
1987 api_name => "open-ils.actor.groups.tree.retrieve",
1988 notes => <<" NOTES");
1989 Returns a list of user groups
1991 sub retrieve_groups_tree {
1992 my( $self, $client ) = @_;
1993 return new_editor()->search_permission_grp_tree(
1998 flesh_fields => { pgt => ["children"] },
1999 order_by => { pgt => 'name'}
2006 # turns an org list into an org tree
2008 sub build_group_tree {
2010 my( $self, $grplist) = @_;
2012 return $grplist unless (
2013 ref($grplist) and @$grplist > 1 );
2015 my @list = sort { $a->name cmp $b->name } @$grplist;
2018 for my $grp (@list) {
2020 if ($grp and !defined($grp->parent)) {
2024 my ($parent) = grep { $_->id == $grp->parent} @list;
2026 $parent->children([]) unless defined($parent->children);
2027 push( @{$parent->children}, $grp );
2035 __PACKAGE__->register_method(
2036 method => "add_user_to_groups",
2037 api_name => "open-ils.actor.user.set_groups",
2038 notes => <<" NOTES");
2039 Adds a user to one or more permission groups
2042 sub add_user_to_groups {
2043 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2045 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2046 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2047 return $evt if $evt;
2049 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2050 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2051 return $evt if $evt;
2053 $apputils->simplereq(
2055 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2057 for my $group (@$groups) {
2058 my $link = Fieldmapper::permission::usr_grp_map->new;
2060 $link->usr($userid);
2062 my $id = $apputils->simplereq(
2064 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2070 __PACKAGE__->register_method(
2071 method => "get_user_perm_groups",
2072 api_name => "open-ils.actor.user.get_groups",
2073 notes => <<" NOTES");
2074 Retrieve a user's permission groups.
2078 sub get_user_perm_groups {
2079 my( $self, $client, $authtoken, $userid ) = @_;
2081 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2082 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2083 return $evt if $evt;
2085 return $apputils->simplereq(
2087 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2092 __PACKAGE__->register_method (
2093 method => 'register_workstation',
2094 api_name => 'open-ils.actor.workstation.register.override',
2095 signature => q/@see open-ils.actor.workstation.register/);
2097 __PACKAGE__->register_method (
2098 method => 'register_workstation',
2099 api_name => 'open-ils.actor.workstation.register',
2101 Registers a new workstion in the system
2102 @param authtoken The login session key
2103 @param name The name of the workstation id
2104 @param owner The org unit that owns this workstation
2105 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2106 if the name is already in use.
2109 sub _register_workstation {
2110 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2111 my( $requestor, $evt ) = $U->checkses($authtoken);
2112 return $evt if $evt;
2113 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2114 return $evt if $evt;
2116 my $ws = $U->cstorereq(
2117 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2118 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2120 $ws = Fieldmapper::actor::workstation->new;
2121 $ws->owning_lib($owner);
2124 my $id = $U->storagereq(
2125 'open-ils.storage.direct.actor.workstation.create', $ws );
2126 return $U->DB_UPDATE_FAILED($ws) unless $id;
2132 sub register_workstation {
2133 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2135 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2136 return $e->event unless $e->checkauth;
2137 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2138 my $existing = $e->search_actor_workstation({name => $name});
2141 if( $self->api_name =~ /override/o ) {
2142 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2143 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2145 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2149 my $ws = Fieldmapper::actor::workstation->new;
2150 $ws->owning_lib($owner);
2152 $e->create_actor_workstation($ws) or return $e->event;
2154 return $ws->id; # note: editor sets the id on the new object for us
2158 __PACKAGE__->register_method (
2159 method => 'fetch_patron_note',
2160 api_name => 'open-ils.actor.note.retrieve.all',
2162 Returns a list of notes for a given user
2163 Requestor must have VIEW_USER permission if pub==false and
2164 @param authtoken The login session key
2165 @param args Hash of params including
2166 patronid : the patron's id
2167 pub : true if retrieving only public notes
2171 sub fetch_patron_note {
2172 my( $self, $conn, $authtoken, $args ) = @_;
2173 my $patronid = $$args{patronid};
2175 my($reqr, $evt) = $U->checkses($authtoken);
2178 ($patron, $evt) = $U->fetch_user($patronid);
2179 return $evt if $evt;
2182 if( $patronid ne $reqr->id ) {
2183 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2184 return $evt if $evt;
2186 return $U->cstorereq(
2187 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2188 { usr => $patronid, pub => 't' } );
2191 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2192 return $evt if $evt;
2194 return $U->cstorereq(
2195 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2198 __PACKAGE__->register_method (
2199 method => 'create_user_note',
2200 api_name => 'open-ils.actor.note.create',
2202 Creates a new note for the given user
2203 @param authtoken The login session key
2204 @param note The note object
2207 sub create_user_note {
2208 my( $self, $conn, $authtoken, $note ) = @_;
2209 my( $reqr, $patron, $evt ) =
2210 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2211 return $evt if $evt;
2212 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2214 $note->creator($reqr->id);
2215 my $id = $U->storagereq(
2216 'open-ils.storage.direct.actor.usr_note.create', $note );
2217 return $U->DB_UPDATE_FAILED($note) unless $id;
2222 __PACKAGE__->register_method (
2223 method => 'delete_user_note',
2224 api_name => 'open-ils.actor.note.delete',
2226 Deletes a note for the given user
2227 @param authtoken The login session key
2228 @param noteid The note id
2231 sub delete_user_note {
2232 my( $self, $conn, $authtoken, $noteid ) = @_;
2234 my $note = $U->cstorereq(
2235 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2236 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2238 my( $reqr, $patron, $evt ) =
2239 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2240 return $evt if $evt;
2241 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2243 my $stat = $U->storagereq(
2244 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2245 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2250 __PACKAGE__->register_method (
2251 method => 'update_user_note',
2252 api_name => 'open-ils.actor.note.update',
2254 @param authtoken The login session key
2255 @param note The note
2259 sub update_user_note {
2260 my( $self, $conn, $auth, $note ) = @_;
2261 my $e = new_editor(authtoken=>$auth, xact=>1);
2262 return $e->event unless $e->checkauth;
2263 my $patron = $e->retrieve_actor_user($note->usr)
2264 or return $e->event;
2265 return $e->event unless
2266 $e->allowed('UPDATE_USER', $patron->home_ou);
2267 $e->update_actor_user_note($note)
2268 or return $e->event;
2276 __PACKAGE__->register_method (
2277 method => 'create_closed_date',
2278 api_name => 'open-ils.actor.org_unit.closed_date.create',
2280 Creates a new closing entry for the given org_unit
2281 @param authtoken The login session key
2282 @param note The closed_date object
2285 sub create_closed_date {
2286 my( $self, $conn, $authtoken, $cd ) = @_;
2288 my( $user, $evt ) = $U->checkses($authtoken);
2289 return $evt if $evt;
2291 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2292 return $evt if $evt;
2294 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2296 my $id = $U->storagereq(
2297 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2298 return $U->DB_UPDATE_FAILED($cd) unless $id;
2303 __PACKAGE__->register_method (
2304 method => 'delete_closed_date',
2305 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2307 Deletes a closing entry for the given org_unit
2308 @param authtoken The login session key
2309 @param noteid The close_date id
2312 sub delete_closed_date {
2313 my( $self, $conn, $authtoken, $cd ) = @_;
2315 my( $user, $evt ) = $U->checkses($authtoken);
2316 return $evt if $evt;
2319 ($cd_obj, $evt) = fetch_closed_date($cd);
2320 return $evt if $evt;
2322 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2323 return $evt if $evt;
2325 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2327 my $stat = $U->storagereq(
2328 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2329 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2334 __PACKAGE__->register_method(
2335 method => 'usrname_exists',
2336 api_name => 'open-ils.actor.username.exists',
2338 Returns 1 if the requested username exists, returns 0 otherwise
2342 sub usrname_exists {
2343 my( $self, $conn, $auth, $usrname ) = @_;
2344 my $e = new_editor(authtoken=>$auth);
2345 return $e->event unless $e->checkauth;
2346 my $a = $e->search_actor_user({usrname => $usrname}, {idlist=>1});
2347 return $$a[0] if $a and @$a;
2351 __PACKAGE__->register_method(
2352 method => 'barcode_exists',
2353 api_name => 'open-ils.actor.barcode.exists',
2355 Returns 1 if the requested barcode exists, returns 0 otherwise
2359 sub barcode_exists {
2360 my( $self, $conn, $auth, $barcode ) = @_;
2361 my $e = new_editor(authtoken=>$auth);
2362 return $e->event unless $e->checkauth;
2363 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2364 return $$a[0] if $a and @$a;
2369 __PACKAGE__->register_method(
2370 method => 'retrieve_net_levels',
2371 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2374 sub retrieve_net_levels {
2375 my( $self, $conn, $auth ) = @_;
2376 my $e = new_editor(authtoken=>$auth);
2377 return $e->event unless $e->checkauth;
2378 return $e->retrieve_all_config_net_access_level();