1 package OpenILS::Application::Actor;
2 use base qw/OpenSRF::Application/;
3 use strict; use warnings;
5 $Data::Dumper::Indent = 0;
8 use Digest::MD5 qw(md5_hex);
10 use OpenSRF::EX qw(:try);
13 use OpenILS::Application::AppUtils;
15 use OpenILS::Utils::Fieldmapper;
16 use OpenILS::Utils::ModsParser;
17 use OpenSRF::Utils::Logger qw/$logger/;
18 use OpenSRF::Utils qw/:datetime/;
20 use OpenSRF::Utils::Cache;
24 use DateTime::Format::ISO8601;
26 use OpenILS::Application::Actor::Container;
27 use OpenILS::Application::Actor::ClosedDates;
29 use OpenILS::Utils::CStoreEditor qw/:funcs/;
31 use OpenILS::Application::Actor::UserGroups;
33 OpenILS::Application::Actor::Container->initialize();
34 OpenILS::Application::Actor::UserGroups->initialize();
35 OpenILS::Application::Actor::ClosedDates->initialize();
38 my $apputils = "OpenILS::Application::AppUtils";
41 sub _d { warn "Patron:\n" . Dumper(shift()); }
46 my $set_user_settings;
49 __PACKAGE__->register_method(
50 method => "set_user_settings",
51 api_name => "open-ils.actor.patron.settings.update",
53 sub set_user_settings {
54 my( $self, $client, $user_session, $uid, $settings ) = @_;
56 $logger->debug("Setting user settings: $user_session, $uid, " . Dumper($settings));
58 my( $staff, $user, $evt ) =
59 $apputils->checkses_requestor( $user_session, $uid, 'UPDATE_USER' );
63 [{ usr => $user->id, name => $_}, {value => $$settings{$_}}] } keys %$settings;
65 $_->[1]->{value} = JSON->perl2JSON($_->[1]->{value}) for @params;
67 $logger->activity("User " . $staff->id . " updating user $uid settings with: " . Dumper(\@params));
69 my $ses = $U->start_db_session();
70 my $stat = $ses->request(
71 'open-ils.storage.direct.actor.user_setting.batch.merge', @params )->gather(1);
72 $U->commit_db_session($ses);
79 __PACKAGE__->register_method(
80 method => "set_ou_settings",
81 api_name => "open-ils.actor.org_unit.settings.update",
84 my( $self, $client, $user_session, $ouid, $settings ) = @_;
86 my( $staff, $evt ) = $apputils->checkses( $user_session );
88 $evt = $apputils->check_perms( $staff->id, $ouid, 'UPDATE_ORG_SETTING' );
92 for my $set (keys %$settings) {
94 my $json = JSON->perl2JSON($$settings{$set});
95 $logger->activity("updating org_unit.setting: $ouid : $set : $json");
98 { org_unit => $ouid, name => $set },
102 my $ses = $U->start_db_session();
103 my $stat = $ses->request(
104 'open-ils.storage.direct.actor.org_unit_setting.merge', @params )->gather(1);
105 $U->commit_db_session($ses);
111 my $fetch_user_settings;
112 my $fetch_ou_settings;
114 __PACKAGE__->register_method(
115 method => "user_settings",
116 api_name => "open-ils.actor.patron.settings.retrieve",
119 my( $self, $client, $user_session, $uid ) = @_;
121 my( $staff, $user, $evt ) =
122 $apputils->checkses_requestor( $user_session, $uid, 'VIEW_USER' );
125 $logger->debug("User " . $staff->id . " fetching user $uid\n");
126 my $s = $apputils->simplereq(
128 'open-ils.cstore.direct.actor.user_setting.search.atomic', { usr => $uid } );
130 return { map { ( $_->name => JSON->JSON2perl($_->value) ) } @$s };
135 __PACKAGE__->register_method(
136 method => "ou_settings",
137 api_name => "open-ils.actor.org_unit.settings.retrieve",
140 my( $self, $client, $ouid ) = @_;
142 $logger->info("Fetching org unit settings for org $ouid");
144 my $s = $apputils->simplereq(
146 'open-ils.cstore.direct.actor.org_unit_setting.search.atomic', {org_unit => $ouid});
148 return { map { ( $_->name => JSON->JSON2perl($_->value) ) } @$s };
151 __PACKAGE__->register_method (
152 method => "ou_setting_delete",
153 api_name => 'open-ils.actor.org_setting.delete',
155 Deletes a specific org unit setting for a specific location
156 @param authtoken The login session key
157 @param orgid The org unit whose setting we're changing
158 @param setting The name of the setting to delete
159 @return True value on success.
163 sub ou_setting_delete {
164 my( $self, $conn, $authtoken, $orgid, $setting ) = @_;
165 my( $reqr, $evt) = $U->checkses($authtoken);
167 $evt = $U->check_perms($reqr->id, $orgid, 'UPDATE_ORG_SETTING');
170 my $id = $U->cstorereq(
171 'open-ils.cstore.direct.actor.org_unit_setting.id_list',
172 { name => $setting, org_unit => $orgid } );
174 $logger->debug("Retrieved setting $id in org unit setting delete");
176 my $s = $U->cstorereq(
177 'open-ils.cstore.direct.actor.org_unit_setting.delete', $id );
179 $logger->activity("User ".$reqr->id." deleted org unit setting $id") if $s;
185 __PACKAGE__->register_method(
186 method => "update_patron",
187 api_name => "open-ils.actor.patron.update",);
190 my( $self, $client, $user_session, $patron ) = @_;
192 my $session = $apputils->start_db_session();
195 $logger->info("Creating new patron...") if $patron->isnew;
196 $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
198 my( $user_obj, $evt ) = $U->checkses($user_session);
201 # XXX does this user have permission to add/create users. Granularity?
202 # $new_patron is the patron in progress. $patron is the original patron
203 # passed in with the method. new_patron will change as the components
204 # of patron are added/updated.
208 # unflesh the real items on the patron
209 $patron->card( $patron->card->id ) if(ref($patron->card));
210 $patron->billing_address( $patron->billing_address->id )
211 if(ref($patron->billing_address));
212 $patron->mailing_address( $patron->mailing_address->id )
213 if(ref($patron->mailing_address));
215 # create/update the patron first so we can use his id
216 if($patron->isnew()) {
217 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
219 } else { $new_patron = $patron; }
221 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
224 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
227 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
230 # re-update the patron if anything has happened to him during this process
231 if($new_patron->ischanged()) {
232 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
236 #$session = OpenSRF::AppSession->create("open-ils.storage"); # why did i put this here?
238 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
241 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
244 ($new_patron, $evt) = _create_standing_penalties($session, $user_session, $patron, $new_patron, $user_obj);
247 $logger->activity("user ".$user_obj->id." updating/creating user ".$new_patron->id);
248 $apputils->commit_db_session($session);
250 #warn "Patron Update/Create complete\n";
251 return flesh_user($new_patron->id());
257 __PACKAGE__->register_method(
258 method => "user_retrieve_fleshed_by_id",
259 api_name => "open-ils.actor.user.fleshed.retrieve",);
261 sub user_retrieve_fleshed_by_id {
262 my( $self, $client, $user_session, $user_id ) = @_;
264 my( $requestor, $target, $evt ) = $apputils->
265 checkses_requestor( $user_session, $user_id, 'VIEW_USER' );
268 return flesh_user($user_id);
272 # fleshes: card, cards, address, addresses, stat_cat_entries, standing_penalties
281 $session = OpenSRF::AppSession->create("open-ils.storage");
285 # grab the user with the given id
286 my $ureq = $session->request(
287 "open-ils.storage.direct.actor.user.retrieve", $id);
288 my $user = $ureq->gather(1);
290 if(!$user) { return undef; }
293 my $cards_req = $session->request(
294 "open-ils.storage.direct.actor.card.search.usr.atomic",
296 $user->cards( $cards_req->gather(1) );
298 for my $c(@{$user->cards}) {
299 if($c->id == $user->card || $c->id eq $user->card ) {
300 #warn "Setting my card to " . $c->id . "\n";
305 my $add_req = $session->request(
306 "open-ils.storage.direct.actor.user_address.search.usr.atomic",
308 $user->addresses( $add_req->gather(1) );
310 if( @{$user->addresses} ) {
311 if( ! grep { $_->id eq $user->billing_address } @{$user->addresses} ) {
312 my $ba = $session->request(
313 'open-ils.storage.direct.actor.user_address.retrieve',
314 $user->billing_address)->gather(1);
315 push( @{$user->addresses}, $ba );
318 if( ! grep { $_->id eq $user->mailing_address } @{$user->addresses} ) {
319 my $ba = $session->request(
320 'open-ils.storage.direct.actor.user_address.retrieve',
321 $user->mailing_address)->gather(1);
322 push( @{$user->addresses}, $ba );
327 for my $c(@{$user->addresses}) {
328 if($c->id eq $user->billing_address ) { $user->billing_address($c); }
329 if($c->id eq $user->mailing_address ) { $user->mailing_address($c); }
332 my $stat_req = $session->request(
333 "open-ils.storage.direct.actor.stat_cat_entry_user_map.search.target_usr.atomic",
335 $user->stat_cat_entries($stat_req->gather(1));
337 my $standing_penalties_req = $session->request(
338 "open-ils.storage.direct.actor.user_standing_penalty.search.usr.atomic",
340 $user->standing_penalties($standing_penalties_req->gather(1));
342 if($kill) { $session->disconnect(); }
343 $user->clear_passwd();
350 my $e = new_editor();
351 my $user = $e->retrieve_actor_user(
360 "standing_penalties",
369 ) or return $e->event;
371 $user->clear_passwd();
380 # clone and clear stuff that would break the database
384 my $new_patron = $patron->clone;
386 # Using the Fieldmapper clone method
387 #my $new_patron = Fieldmapper::actor::user->new();
389 #my $fmap = $Fieldmapper::fieldmap;
390 #no strict; # shallow clone, may be useful in the fieldmapper
392 # (keys %{$fmap->{"Fieldmapper::actor::user"}->{'fields'}}) {
393 # $new_patron->$field( $patron->$field() );
398 $new_patron->clear_billing_address();
399 $new_patron->clear_mailing_address();
400 $new_patron->clear_addresses();
401 $new_patron->clear_card();
402 $new_patron->clear_cards();
403 $new_patron->clear_id();
404 $new_patron->clear_isnew();
405 $new_patron->clear_ischanged();
406 $new_patron->clear_isdeleted();
407 $new_patron->clear_stat_cat_entries();
408 $new_patron->clear_permissions();
409 $new_patron->clear_standing_penalties();
419 my $user_obj = shift;
421 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
422 return (undef, $evt) if $evt;
424 my $ex = $session->request(
425 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
427 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
430 $evt = _check_dup_ident($session, $patron);
431 return (undef, $evt) if $evt;
433 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
435 my $id = $session->request(
436 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
437 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
439 $logger->info("Successfully created new user [$id] in DB");
441 return ( $session->request(
442 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
447 my( $session, $patron, $user_obj, $noperm) = @_;
449 $logger->info("Updating patron ".$patron->id." in DB");
454 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
455 return (undef, $evt) if $evt;
458 # We can' check for dup idents on update because some existing
459 # users may already have dup idents
460 #$evt = _check_dup_ident($session, $patron);
461 #return (undef, $evt) if $evt;
464 # update the password by itself to avoid the password protection magic
465 if( $patron->passwd ) {
466 my $s = $session->request(
467 'open-ils.storage.direct.actor.user.remote_update',
468 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
469 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
470 $patron->clear_passwd;
473 if(!$patron->ident_type) {
474 $patron->clear_ident_type;
475 $patron->clear_ident_value;
478 if(!$patron->ident_type2) {
479 $patron->clear_ident_type2;
480 $patron->clear_ident_value2;
483 my $stat = $session->request(
484 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
485 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
490 sub _check_dup_ident {
491 my( $session, $patron ) = @_;
493 return undef unless $patron->ident_value;
496 ident_type => $patron->ident_type,
497 ident_value => $patron->ident_value,
500 $logger->debug("patron update searching for dup ident values: " .
501 $patron->ident_type . ':' . $patron->ident_value);
503 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
505 my $dups = $session->request(
506 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
509 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
516 sub _add_update_addresses {
520 my $new_patron = shift;
524 my $current_id; # id of the address before creation
526 for my $address (@{$patron->addresses()}) {
528 next unless ref $address;
529 $current_id = $address->id();
531 if( $patron->billing_address() and
532 $patron->billing_address() == $current_id ) {
533 $logger->info("setting billing addr to $current_id");
534 $new_patron->billing_address($address->id());
535 $new_patron->ischanged(1);
538 if( $patron->mailing_address() and
539 $patron->mailing_address() == $current_id ) {
540 $new_patron->mailing_address($address->id());
541 $logger->info("setting mailing addr to $current_id");
542 $new_patron->ischanged(1);
546 if($address->isnew()) {
548 $address->usr($new_patron->id());
550 ($address, $evt) = _add_address($session,$address);
551 return (undef, $evt) if $evt;
553 # we need to get the new id
554 if( $patron->billing_address() and
555 $patron->billing_address() == $current_id ) {
556 $new_patron->billing_address($address->id());
557 $logger->info("setting billing addr to $current_id");
558 $new_patron->ischanged(1);
561 if( $patron->mailing_address() and
562 $patron->mailing_address() == $current_id ) {
563 $new_patron->mailing_address($address->id());
564 $logger->info("setting mailing addr to $current_id");
565 $new_patron->ischanged(1);
568 } elsif($address->ischanged() ) {
570 ($address, $evt) = _update_address($session, $address);
571 return (undef, $evt) if $evt;
573 } elsif($address->isdeleted() ) {
575 if( $address->id() == $new_patron->mailing_address() ) {
576 $new_patron->clear_mailing_address();
577 ($new_patron, $evt) = _update_patron($session, $new_patron);
578 return (undef, $evt) if $evt;
581 if( $address->id() == $new_patron->billing_address() ) {
582 $new_patron->clear_billing_address();
583 ($new_patron, $evt) = _update_patron($session, $new_patron);
584 return (undef, $evt) if $evt;
587 $evt = _delete_address($session, $address);
588 return (undef, $evt) if $evt;
592 return ( $new_patron, undef );
596 # adds an address to the db and returns the address with new id
598 my($session, $address) = @_;
599 $address->clear_id();
601 $logger->info("Creating new address at street ".$address->street1);
603 # put the address into the database
604 my $id = $session->request(
605 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
606 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
609 return ($address, undef);
613 sub _update_address {
614 my( $session, $address ) = @_;
616 $logger->info("Updating address ".$address->id." in the DB");
618 my $stat = $session->request(
619 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
621 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
622 return ($address, undef);
627 sub _add_update_cards {
631 my $new_patron = shift;
635 my $virtual_id; #id of the card before creation
636 for my $card (@{$patron->cards()}) {
638 $card->usr($new_patron->id());
640 if(ref($card) and $card->isnew()) {
642 $virtual_id = $card->id();
643 ( $card, $evt ) = _add_card($session,$card);
644 return (undef, $evt) if $evt;
646 #if(ref($patron->card)) { $patron->card($patron->card->id); }
647 if($patron->card() == $virtual_id) {
648 $new_patron->card($card->id());
649 $new_patron->ischanged(1);
652 } elsif( ref($card) and $card->ischanged() ) {
653 $evt = _update_card($session, $card);
654 return (undef, $evt) if $evt;
658 return ( $new_patron, undef );
662 # adds an card to the db and returns the card with new id
664 my( $session, $card ) = @_;
667 $logger->info("Adding new patron card ".$card->barcode);
669 my $id = $session->request(
670 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
671 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
672 $logger->info("Successfully created patron card $id");
675 return ( $card, undef );
679 # returns event on error. returns undef otherwise
681 my( $session, $card ) = @_;
682 $logger->info("Updating patron card ".$card->id);
684 my $stat = $session->request(
685 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
686 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
693 # returns event on error. returns undef otherwise
694 sub _delete_address {
695 my( $session, $address ) = @_;
697 $logger->info("Deleting address ".$address->id." from DB");
699 my $stat = $session->request(
700 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
702 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
708 sub _add_survey_responses {
709 my ($session, $patron, $new_patron) = @_;
711 $logger->info( "Updating survey responses for patron ".$new_patron->id );
713 my $responses = $patron->survey_responses;
717 $_->usr($new_patron->id) for (@$responses);
719 my $evt = $U->simplereq( "open-ils.circ",
720 "open-ils.circ.survey.submit.user_id", $responses );
722 return (undef, $evt) if defined($U->event_code($evt));
726 return ( $new_patron, undef );
730 sub _create_stat_maps {
732 my($session, $user_session, $patron, $new_patron) = @_;
734 my $maps = $patron->stat_cat_entries();
736 for my $map (@$maps) {
738 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
740 if ($map->isdeleted()) {
741 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
743 } elsif ($map->isnew()) {
744 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
749 $map->target_usr($new_patron->id);
752 $logger->info("Updating stat entry with method $method and map $map");
754 my $stat = $session->request($method, $map)->gather(1);
755 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
759 return ($new_patron, undef);
762 sub _create_perm_maps {
764 my($session, $user_session, $patron, $new_patron) = @_;
766 my $maps = $patron->permissions;
768 for my $map (@$maps) {
770 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
771 if ($map->isdeleted()) {
772 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
773 } elsif ($map->isnew()) {
774 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
779 $map->usr($new_patron->id);
781 #warn( "Updating permissions with method $method and session $user_session and map $map" );
782 $logger->info( "Updating permissions with method $method and map $map" );
784 my $stat = $session->request($method, $map)->gather(1);
785 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
789 return ($new_patron, undef);
793 sub _create_standing_penalties {
795 my($session, $user_session, $patron, $new_patron) = @_;
797 my $maps = $patron->standing_penalties;
800 for my $map (@$maps) {
802 if ($map->isdeleted()) {
803 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
804 } elsif ($map->isnew()) {
805 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
811 $map->usr($new_patron->id);
813 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
815 my $stat = $session->request($method, $map)->gather(1);
816 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
819 return ($new_patron, undef);
824 __PACKAGE__->register_method(
825 method => "search_username",
826 api_name => "open-ils.actor.user.search.username",
829 sub search_username {
830 my($self, $client, $username) = @_;
831 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
833 "open-ils.cstore.direct.actor.user.search.atomic",
834 { usrname => $username }
842 __PACKAGE__->register_method(
843 method => "user_retrieve_by_barcode",
844 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
846 sub user_retrieve_by_barcode {
847 my($self, $client, $user_session, $barcode) = @_;
849 $logger->debug("Searching for user with barcode $barcode");
850 my ($user_obj, $evt) = $apputils->checkses($user_session);
853 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
855 "open-ils.cstore.direct.actor.card.search.atomic",
856 { barcode => $barcode }
859 if(!$card || !$card->[0]) {
860 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
864 my $user = flesh_user($card->usr());
866 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
869 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
876 __PACKAGE__->register_method(
877 method => "get_user_by_id",
878 api_name => "open-ils.actor.user.retrieve",);
881 my ($self, $client, $auth, $id) = @_;
882 my $e = new_editor(authtoken=>$auth);
883 return $e->event unless $e->checkauth;
884 my $user = $e->retrieve_actor_user($id)
886 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
892 __PACKAGE__->register_method(
893 method => "get_org_types",
894 api_name => "open-ils.actor.org_types.retrieve",);
898 my($self, $client) = @_;
899 return $org_types if $org_types;
900 return $org_types = new_editor()->retrieve_all_actor_org_unit_type();
905 __PACKAGE__->register_method(
906 method => "get_user_ident_types",
907 api_name => "open-ils.actor.user.ident_types.retrieve",
910 sub get_user_ident_types {
911 return $ident_types if $ident_types;
912 return $ident_types =
913 new_editor()->retrieve_all_config_identification_type();
919 __PACKAGE__->register_method(
920 method => "get_org_unit",
921 api_name => "open-ils.actor.org_unit.retrieve",
925 my( $self, $client, $user_session, $org_id ) = @_;
926 my $e = new_editor(authtoken => $user_session);
928 return $e->event unless $e->checkauth;
929 $org_id = $e->requestor->ws_ou;
931 my $o = $e->retrieve_actor_org_unit($org_id)
936 __PACKAGE__->register_method(
937 method => "search_org_unit",
938 api_name => "open-ils.actor.org_unit_list.search",
941 sub search_org_unit {
943 my( $self, $client, $field, $value ) = @_;
945 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
947 "open-ils.cstore.direct.actor.org_unit.search.atomic",
948 { $field => $value } );
956 __PACKAGE__->register_method(
957 method => "get_org_tree",
958 api_name => "open-ils.actor.org_tree.retrieve",
960 note => "Returns the entire org tree structure",
964 my( $self, $client) = @_;
966 $cache = OpenSRF::Utils::Cache->new("global", 0) unless $cache;
967 my $tree = $cache->get_cache('orgtree');
968 return $tree if $tree;
970 $tree = new_editor()->search_actor_org_unit(
972 {"parent_ou" => undef },
975 flesh_fields => { aou => ['children'] },
976 order_by => { aou => 'name'}
981 $cache->put_cache('orgtree', $tree);
986 # turns an org list into an org tree
989 my( $self, $orglist) = @_;
991 return $orglist unless (
992 ref($orglist) and @$orglist > 1 );
995 $a->ou_type <=> $b->ou_type ||
996 $a->name cmp $b->name } @$orglist;
998 for my $org (@list) {
1000 next unless ($org and defined($org->parent_ou));
1001 my ($parent) = grep { $_->id == $org->parent_ou } @list;
1002 next unless $parent;
1004 $parent->children([]) unless defined($parent->children);
1005 push( @{$parent->children}, $org );
1013 __PACKAGE__->register_method(
1014 method => "get_org_descendants",
1015 api_name => "open-ils.actor.org_tree.descendants.retrieve"
1018 # depth is optional. org_unit is the id
1019 sub get_org_descendants {
1020 my( $self, $client, $org_unit, $depth ) = @_;
1021 my $orglist = $apputils->simple_scalar_request(
1023 "open-ils.storage.actor.org_unit.descendants.atomic",
1024 $org_unit, $depth );
1025 return $self->build_org_tree($orglist);
1029 __PACKAGE__->register_method(
1030 method => "get_org_ancestors",
1031 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
1034 # depth is optional. org_unit is the id
1035 sub get_org_ancestors {
1036 my( $self, $client, $org_unit, $depth ) = @_;
1037 my $orglist = $apputils->simple_scalar_request(
1039 "open-ils.storage.actor.org_unit.ancestors.atomic",
1040 $org_unit, $depth );
1041 return $self->build_org_tree($orglist);
1045 __PACKAGE__->register_method(
1046 method => "get_standings",
1047 api_name => "open-ils.actor.standings.retrieve"
1052 return $user_standings if $user_standings;
1053 return $user_standings =
1054 $apputils->simple_scalar_request(
1056 "open-ils.cstore.direct.config.standing.search.atomic",
1057 { id => { "!=" => undef } }
1063 __PACKAGE__->register_method(
1064 method => "get_my_org_path",
1065 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1068 sub get_my_org_path {
1069 my( $self, $client, $user_session, $org_id ) = @_;
1070 my $user_obj = $apputils->check_user_session($user_session);
1071 if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
1073 return $apputils->simple_scalar_request(
1075 "open-ils.storage.actor.org_unit.full_path.atomic",
1080 __PACKAGE__->register_method(
1081 method => "patron_adv_search",
1082 api_name => "open-ils.actor.patron.search.advanced" );
1083 sub patron_adv_search {
1084 my( $self, $client, $auth, $search_hash, $search_limit, $search_sort, $include_inactive ) = @_;
1085 my $e = new_editor(authtoken=>$auth);
1086 return $e->event unless $e->checkauth;
1087 return $e->event unless $e->allowed('VIEW_USER');
1088 return $U->storagereq(
1089 "open-ils.storage.actor.user.crazy_search",
1090 $search_hash, $search_limit, $search_sort, $include_inactive);
1095 sub _verify_password {
1096 my($user_session, $password) = @_;
1097 my $user_obj = $apputils->check_user_session($user_session);
1099 #grab the user with password
1100 $user_obj = $apputils->simple_scalar_request(
1102 "open-ils.cstore.direct.actor.user.retrieve",
1105 if($user_obj->passwd eq $password) {
1113 __PACKAGE__->register_method(
1114 method => "update_password",
1115 api_name => "open-ils.actor.user.password.update");
1117 __PACKAGE__->register_method(
1118 method => "update_password",
1119 api_name => "open-ils.actor.user.username.update");
1121 __PACKAGE__->register_method(
1122 method => "update_password",
1123 api_name => "open-ils.actor.user.email.update");
1125 sub update_password {
1126 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1130 my $user_obj = $apputils->check_user_session($user_session);
1132 if($self->api_name =~ /password/o) {
1134 #make sure they know the current password
1135 if(!_verify_password($user_session, md5_hex($current_password))) {
1136 return OpenILS::Event->new('INCORRECT_PASSWORD');
1139 $logger->debug("update_password setting new password $new_value");
1140 $user_obj->passwd($new_value);
1142 } elsif($self->api_name =~ /username/o) {
1143 my $users = search_username(undef, undef, $new_value);
1144 if( $users and $users->[0] ) {
1145 return OpenILS::Event->new('USERNAME_EXISTS');
1147 $user_obj->usrname($new_value);
1149 } elsif($self->api_name =~ /email/o) {
1150 #warn "Updating email to $new_value\n";
1151 $user_obj->email($new_value);
1154 my $session = $apputils->start_db_session();
1156 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1157 return $evt if $evt;
1159 $apputils->commit_db_session($session);
1161 if($user_obj) { return 1; }
1166 __PACKAGE__->register_method(
1167 method => "check_user_perms",
1168 api_name => "open-ils.actor.user.perm.check",
1169 notes => <<" NOTES");
1170 Takes a login session, user id, an org id, and an array of perm type strings. For each
1171 perm type, if the user does *not* have the given permission it is added
1172 to a list which is returned from the method. If all permissions
1173 are allowed, an empty list is returned
1174 if the logged in user does not match 'user_id', then the logged in user must
1175 have VIEW_PERMISSION priveleges.
1178 sub check_user_perms {
1179 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1181 my( $staff, $evt ) = $apputils->checkses($login_session);
1182 return $evt if $evt;
1184 if($staff->id ne $user_id) {
1185 if( $evt = $apputils->check_perms(
1186 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1192 for my $perm (@$perm_types) {
1193 if($apputils->check_perms($user_id, $org_id, $perm)) {
1194 push @not_allowed, $perm;
1198 return \@not_allowed
1201 __PACKAGE__->register_method(
1202 method => "check_user_perms2",
1203 api_name => "open-ils.actor.user.perm.check.multi_org",
1205 Checks the permissions on a list of perms and orgs for a user
1206 @param authtoken The login session key
1207 @param user_id The id of the user to check
1208 @param orgs The array of org ids
1209 @param perms The array of permission names
1210 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1211 if the logged in user does not match 'user_id', then the logged in user must
1212 have VIEW_PERMISSION priveleges.
1215 sub check_user_perms2 {
1216 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1218 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1219 $authtoken, $user_id, 'VIEW_PERMISSION' );
1220 return $evt if $evt;
1223 for my $org (@$orgs) {
1224 for my $perm (@$perms) {
1225 if($apputils->check_perms($user_id, $org, $perm)) {
1226 push @not_allowed, [ $org, $perm ];
1231 return \@not_allowed
1235 __PACKAGE__->register_method(
1236 method => 'check_user_perms3',
1237 api_name => 'open-ils.actor.user.perm.highest_org',
1239 Returns the highest org unit id at which a user has a given permission
1240 If the requestor does not match the target user, the requestor must have
1241 'VIEW_PERMISSION' rights at the home org unit of the target user
1242 @param authtoken The login session key
1243 @param userid The id of the user in question
1244 @param perm The permission to check
1245 @return The org unit highest in the org tree within which the user has
1246 the requested permission
1249 sub check_user_perms3 {
1250 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1252 my( $staff, $target, $org, $evt );
1254 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1255 $authtoken, $userid, 'VIEW_PERMISSION' );
1256 return $evt if $evt;
1258 my $tree = $self->get_org_tree();
1259 return _find_highest_perm_org( $perm, $userid, $target->ws_ou, $tree );
1263 sub _find_highest_perm_org {
1264 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1265 my $org = $apputils->find_org($org_tree, $start_org );
1269 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1271 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1277 __PACKAGE__->register_method(
1278 method => 'check_user_perms4',
1279 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1281 Returns the highest org unit id at which a user has a given permission
1282 If the requestor does not match the target user, the requestor must have
1283 'VIEW_PERMISSION' rights at the home org unit of the target user
1284 @param authtoken The login session key
1285 @param userid The id of the user in question
1286 @param perms An array of perm names to check
1287 @return An array of orgId's representing the org unit
1288 highest in the org tree within which the user has the requested permission
1289 The arrah of orgId's has matches the order of the perms array
1292 sub check_user_perms4 {
1293 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1295 my( $staff, $target, $org, $evt );
1297 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1298 $authtoken, $userid, 'VIEW_PERMISSION' );
1299 return $evt if $evt;
1302 return [] unless ref($perms);
1303 my $tree = $self->get_org_tree();
1305 for my $p (@$perms) {
1306 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1314 __PACKAGE__->register_method(
1315 method => "user_fines_summary",
1316 api_name => "open-ils.actor.user.fines.summary",
1317 notes => <<" NOTES");
1318 Returns a short summary of the users total open fines, excluding voided fines
1319 Params are login_session, user_id
1320 Returns a 'mous' object.
1323 sub user_fines_summary {
1324 my( $self, $client, $login_session, $user_id ) = @_;
1326 my $user_obj = $apputils->check_user_session($login_session);
1327 if($user_obj->id ne $user_id) {
1328 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1329 return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY");
1333 return $apputils->simple_scalar_request(
1335 "open-ils.cstore.direct.money.open_user_summary.search",
1336 { usr => $user_id } );
1343 __PACKAGE__->register_method(
1344 method => "user_transactions",
1345 api_name => "open-ils.actor.user.transactions",
1346 notes => <<" NOTES");
1347 Returns a list of open user transactions (mbts objects);
1348 Params are login_session, user_id
1349 Optional third parameter is the transactions type. defaults to all
1352 __PACKAGE__->register_method(
1353 method => "user_transactions",
1354 api_name => "open-ils.actor.user.transactions.have_charge",
1355 notes => <<" NOTES");
1356 Returns a list of all open user transactions (mbts objects) that have an initial charge
1357 Params are login_session, user_id
1358 Optional third parameter is the transactions type. defaults to all
1361 __PACKAGE__->register_method(
1362 method => "user_transactions",
1363 api_name => "open-ils.actor.user.transactions.have_balance",
1364 notes => <<" NOTES");
1365 Returns a list of all open user transactions (mbts objects) that have a balance
1366 Params are login_session, user_id
1367 Optional third parameter is the transactions type. defaults to all
1370 __PACKAGE__->register_method(
1371 method => "user_transactions",
1372 api_name => "open-ils.actor.user.transactions.fleshed",
1373 notes => <<" NOTES");
1374 Returns an object/hash of transaction, circ, title where transaction = an open
1375 user transactions (mbts objects), circ is the attached circluation, and title
1376 is the title the circ points to
1377 Params are login_session, user_id
1378 Optional third parameter is the transactions type. defaults to all
1381 __PACKAGE__->register_method(
1382 method => "user_transactions",
1383 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1384 notes => <<" NOTES");
1385 Returns an object/hash of transaction, circ, title where transaction = an open
1386 user transactions that has an initial charge (mbts objects), circ is the
1387 attached circluation, and title is the title the circ points to
1388 Params are login_session, user_id
1389 Optional third parameter is the transactions type. defaults to all
1392 __PACKAGE__->register_method(
1393 method => "user_transactions",
1394 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1395 notes => <<" NOTES");
1396 Returns an object/hash of transaction, circ, title where transaction = an open
1397 user transaction that has a balance (mbts objects), circ is the attached
1398 circluation, and title is the title the circ points to
1399 Params are login_session, user_id
1400 Optional third parameter is the transaction type. defaults to all
1403 __PACKAGE__->register_method(
1404 method => "user_transactions",
1405 api_name => "open-ils.actor.user.transactions.count",
1406 notes => <<" NOTES");
1407 Returns an object/hash of transaction, circ, title where transaction = an open
1408 user transactions (mbts objects), circ is the attached circluation, and title
1409 is the title the circ points to
1410 Params are login_session, user_id
1411 Optional third parameter is the transactions type. defaults to all
1414 __PACKAGE__->register_method(
1415 method => "user_transactions",
1416 api_name => "open-ils.actor.user.transactions.have_charge.count",
1417 notes => <<" NOTES");
1418 Returns an object/hash of transaction, circ, title where transaction = an open
1419 user transactions that has an initial charge (mbts objects), circ is the
1420 attached circluation, and title is the title the circ points to
1421 Params are login_session, user_id
1422 Optional third parameter is the transactions type. defaults to all
1425 __PACKAGE__->register_method(
1426 method => "user_transactions",
1427 api_name => "open-ils.actor.user.transactions.have_balance.count",
1428 notes => <<" NOTES");
1429 Returns an object/hash of transaction, circ, title where transaction = an open
1430 user transaction that has a balance (mbts objects), circ is the attached
1431 circluation, and title is the title the circ points to
1432 Params are login_session, user_id
1433 Optional third parameter is the transaction type. defaults to all
1436 __PACKAGE__->register_method(
1437 method => "user_transactions",
1438 api_name => "open-ils.actor.user.transactions.have_balance.total",
1439 notes => <<" NOTES");
1440 Returns an object/hash of transaction, circ, title where transaction = an open
1441 user transaction that has a balance (mbts objects), circ is the attached
1442 circluation, and title is the title the circ points to
1443 Params are login_session, user_id
1444 Optional third parameter is the transaction type. defaults to all
1449 sub user_transactions {
1450 my( $self, $client, $login_session, $user_id, $type ) = @_;
1452 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1453 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1454 return $evt if $evt;
1456 my $api = $self->api_name();
1460 if(defined($type)) { @xact = (xact_type => $type);
1462 } else { @xact = (); }
1464 if($api =~ /have_charge/o) {
1466 $trans = $apputils->simple_scalar_request(
1468 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1469 { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1471 } elsif($api =~ /have_balance/o) {
1473 $trans = $apputils->simple_scalar_request(
1475 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1476 { usr => $user_id, balance_owed => { "<>" => 0 }, @xact });
1480 $trans = $apputils->simple_scalar_request(
1482 "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1483 { usr => $user_id, @xact });
1486 if($api =~ /total/o) {
1488 for my $t (@$trans) {
1489 $total += $t->balance_owed;
1492 $logger->debug("Total balance owed by user $user_id: $total");
1496 if($api =~ /count/o) { return scalar @$trans; }
1497 if($api !~ /fleshed/o) { return $trans; }
1500 for my $t (@$trans) {
1502 if( $t->xact_type ne 'circulation' ) {
1503 push @resp, {transaction => $t};
1507 my $circ = $apputils->simple_scalar_request(
1509 "open-ils.cstore.direct.action.circulation.retrieve",
1514 my $title = $apputils->simple_scalar_request(
1516 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1517 $circ->target_copy );
1521 my $u = OpenILS::Utils::ModsParser->new();
1522 $u->start_mods_batch($title->marc());
1523 my $mods = $u->finish_mods_batch();
1524 $mods->doc_id($title->id) if $mods;
1526 push @resp, {transaction => $t, circ => $circ, record => $mods };
1534 __PACKAGE__->register_method(
1535 method => "user_transaction_retrieve",
1536 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1538 notes => <<" NOTES");
1539 Returns a fleshedtransaction record
1541 __PACKAGE__->register_method(
1542 method => "user_transaction_retrieve",
1543 api_name => "open-ils.actor.user.transaction.retrieve",
1545 notes => <<" NOTES");
1546 Returns a transaction record
1548 sub user_transaction_retrieve {
1549 my( $self, $client, $login_session, $bill_id ) = @_;
1551 my $trans = $apputils->simple_scalar_request(
1553 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1557 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1558 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1559 return $evt if $evt;
1561 my $api = $self->api_name();
1562 if($api !~ /fleshed/o) { return $trans; }
1564 if( $trans->xact_type ne 'circulation' ) {
1565 $logger->debug("Returning non-circ transaction");
1566 return {transaction => $trans};
1569 my $circ = $apputils->simple_scalar_request(
1571 "open-ils..direct.action.circulation.retrieve",
1574 return {transaction => $trans} unless $circ;
1575 $logger->debug("Found the circ transaction");
1577 my $title = $apputils->simple_scalar_request(
1579 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1580 $circ->target_copy );
1582 return {transaction => $trans, circ => $circ } unless $title;
1583 $logger->debug("Found the circ title");
1587 my $u = OpenILS::Utils::ModsParser->new();
1588 $u->start_mods_batch($title->marc());
1589 $mods = $u->finish_mods_batch();
1591 if ($title->id == -1) {
1592 my $copy = $apputils->simple_scalar_request(
1594 "open-ils.cstore.direct.asset.copy.retrieve",
1595 $circ->target_copy );
1597 $mods = new Fieldmapper::metabib::virtual_record;
1599 $mods->title($copy->dummy_title);
1600 $mods->author($copy->dummy_author);
1604 $logger->debug("MODSized the circ title");
1606 return {transaction => $trans, circ => $circ, record => $mods };
1610 __PACKAGE__->register_method(
1611 method => "hold_request_count",
1612 api_name => "open-ils.actor.user.hold_requests.count",
1614 notes => <<" NOTES");
1615 Returns hold ready/total counts
1617 sub hold_request_count {
1618 my( $self, $client, $login_session, $userid ) = @_;
1620 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1621 $login_session, $userid, 'VIEW_HOLD' );
1622 return $evt if $evt;
1625 my $holds = $apputils->simple_scalar_request(
1627 "open-ils.cstore.direct.action.hold_request.search.atomic",
1630 fulfillment_time => {"=" => undef },
1631 cancel_time => undef,
1636 for my $h (@$holds) {
1637 next unless $h->capture_time and $h->current_copy;
1639 my $copy = $apputils->simple_scalar_request(
1641 "open-ils.cstore.direct.asset.copy.retrieve",
1645 if ($copy and $copy->status == 8) {
1650 return { total => scalar(@$holds), ready => scalar(@ready) };
1654 __PACKAGE__->register_method(
1655 method => "checkedout_count",
1656 api_name => "open-ils.actor.user.checked_out.count__",
1658 notes => <<" NOTES");
1659 Returns a transaction record
1663 sub checkedout_count {
1664 my( $self, $client, $login_session, $userid ) = @_;
1666 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1667 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1668 return $evt if $evt;
1670 my $circs = $apputils->simple_scalar_request(
1672 "open-ils.cstore.direct.action.circulation.search.atomic",
1673 { usr => $userid, stop_fines => undef }
1674 #{ usr => $userid, checkin_time => {"=" => undef } }
1677 my $parser = DateTime::Format::ISO8601->new;
1680 for my $c (@$circs) {
1681 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1682 my $due = $due_dt->epoch;
1684 if ($due < DateTime->today->epoch) {
1689 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1693 __PACKAGE__->register_method(
1694 method => "checked_out",
1695 api_name => "open-ils.actor.user.checked_out",
1698 Returns a structure of circulations objects sorted by
1699 out, overdue, lost, claims_returned, long_overdue.
1700 A list of IDs are returned of each type.
1701 lost, long_overdue, and claims_returned circ will not
1702 be "finished" (there is an outstanding balance or some
1703 other pending action on the circ).
1705 The .count method also includes a 'total' field which
1706 sums all "open" circs
1710 __PACKAGE__->register_method(
1711 method => "checked_out",
1712 api_name => "open-ils.actor.user.checked_out.count",
1714 signature => q/@see open-ils.actor.user.checked_out/
1718 my( $self, $conn, $auth, $userid ) = @_;
1720 my $e = new_editor(authtoken=>$auth);
1721 return $e->event unless $e->checkauth;
1723 if( $userid ne $e->requestor->id ) {
1724 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1727 my $count = $self->api_name =~ /count/;
1728 return _checked_out( $count, $e, $userid );
1732 my( $iscount, $e, $userid ) = @_;
1734 my $circs = $e->search_action_circulation(
1735 { usr => $userid, stop_fines => undef });
1737 my $parser = DateTime::Format::ISO8601->new;
1739 # split the circs up into overdue and not-overdue circs
1741 for my $c (@$circs) {
1742 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1743 my $due = $due_dt->epoch;
1744 if ($due < DateTime->today->epoch) {
1745 push @overdue, $c->id;
1751 # grab all of the lost, claims-returned, and longoverdue circs
1752 #my $open = $e->search_action_circulation(
1753 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1756 # these items have stop_fines, but no xact_finish, so money
1757 # is owed on them and they have not been checked in
1758 my $open = $e->search_action_circulation(
1761 stop_fines => { '!=' => undef },
1762 xact_finish => undef,
1763 checkin_time => undef,
1768 my( @lost, @cr, @lo );
1769 for my $c (@$open) {
1770 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1771 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1772 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1778 total => @$circs + @lost + @cr + @lo,
1779 out => scalar(@out),
1780 overdue => scalar(@overdue),
1781 lost => scalar(@lost),
1782 claims_returned => scalar(@cr),
1783 long_overdue => scalar(@lo)
1789 overdue => \@overdue,
1791 claims_returned => \@cr,
1792 long_overdue => \@lo
1798 __PACKAGE__->register_method(
1799 method => "checked_in_with_fines",
1800 api_name => "open-ils.actor.user.checked_in_with_fines",
1802 signature => q/@see open-ils.actor.user.checked_out/
1804 sub checked_in_with_fines {
1805 my( $self, $conn, $auth, $userid ) = @_;
1807 my $e = new_editor(authtoken=>$auth);
1808 return $e->event unless $e->checkauth;
1810 if( $userid ne $e->requestor->id ) {
1811 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1814 # money is owed on these items and they are checked in
1815 my $open = $e->search_action_circulation(
1818 xact_finish => undef,
1819 checkin_time => { "!=" => undef },
1824 my( @lost, @cr, @lo );
1825 for my $c (@$open) {
1826 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1827 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1828 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1833 claims_returned => \@cr,
1834 long_overdue => \@lo
1846 __PACKAGE__->register_method(
1847 method => "user_transaction_history",
1848 api_name => "open-ils.actor.user.transactions.history",
1850 notes => <<" NOTES");
1851 Returns a list of billable transaction ids for a user, optionally by type
1853 __PACKAGE__->register_method(
1854 method => "user_transaction_history",
1855 api_name => "open-ils.actor.user.transactions.history.have_charge",
1857 notes => <<" NOTES");
1858 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1860 __PACKAGE__->register_method(
1861 method => "user_transaction_history",
1862 api_name => "open-ils.actor.user.transactions.history.have_balance",
1864 notes => <<" NOTES");
1865 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1869 sub _user_transaction_history {
1870 my( $self, $client, $login_session, $user_id, $type ) = @_;
1872 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1873 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1874 return $evt if $evt;
1876 my $api = $self->api_name();
1881 @xact = (xact_type => $type) if(defined($type));
1882 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1883 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1885 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1887 my $trans = $apputils->simple_scalar_request(
1889 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1890 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1892 return [ map { $_->id } @$trans ];
1897 sub user_transaction_history {
1898 my( $self, $conn, $auth, $userid, $type ) = @_;
1899 my $e = new_editor(authtoken=>$auth);
1900 return $e->event unless $e->checkauth;
1901 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1903 my $api = $self->api_name;
1904 my @xact = (xact_type => $type) if(defined($type));
1905 my @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1906 my @charge = (last_billing_ts => { "!=" => undef }) if $api =~ /have_charge/;
1908 return $e->search_money_billable_transaction_summary(
1910 { usr => $userid, @xact, @charge, @balance },
1911 { order_by => 'xact_start DESC' }
1917 __PACKAGE__->register_method(
1918 method => "user_perms",
1919 api_name => "open-ils.actor.permissions.user_perms.retrieve",
1921 notes => <<" NOTES");
1922 Returns a list of permissions
1925 my( $self, $client, $authtoken, $user ) = @_;
1927 my( $staff, $evt ) = $apputils->checkses($authtoken);
1928 return $evt if $evt;
1930 $user ||= $staff->id;
1932 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1936 return $apputils->simple_scalar_request(
1938 "open-ils.storage.permission.user_perms.atomic",
1942 __PACKAGE__->register_method(
1943 method => "retrieve_perms",
1944 api_name => "open-ils.actor.permissions.retrieve",
1945 notes => <<" NOTES");
1946 Returns a list of permissions
1948 sub retrieve_perms {
1949 my( $self, $client ) = @_;
1950 return $apputils->simple_scalar_request(
1952 "open-ils.cstore.direct.permission.perm_list.search.atomic",
1953 { id => { '!=' => undef } }
1957 __PACKAGE__->register_method(
1958 method => "retrieve_groups",
1959 api_name => "open-ils.actor.groups.retrieve",
1960 notes => <<" NOTES");
1961 Returns a list of user groupss
1963 sub retrieve_groups {
1964 my( $self, $client ) = @_;
1965 return new_editor()->retrieve_all_permission_grp_tree();
1968 __PACKAGE__->register_method(
1969 method => "retrieve_org_address",
1970 api_name => "open-ils.actor.org_unit.address.retrieve",
1971 notes => <<' NOTES');
1972 Returns an org_unit address by ID
1973 @param An org_address ID
1975 sub retrieve_org_address {
1976 my( $self, $client, $id ) = @_;
1977 return $apputils->simple_scalar_request(
1979 "open-ils.cstore.direct.actor.org_address.retrieve",
1984 __PACKAGE__->register_method(
1985 method => "retrieve_groups_tree",
1986 api_name => "open-ils.actor.groups.tree.retrieve",
1987 notes => <<" NOTES");
1988 Returns a list of user groups
1990 sub retrieve_groups_tree {
1991 my( $self, $client ) = @_;
1992 return new_editor()->search_permission_grp_tree(
1997 flesh_fields => { pgt => ["children"] },
1998 order_by => { pgt => 'name'}
2005 # turns an org list into an org tree
2007 sub build_group_tree {
2009 my( $self, $grplist) = @_;
2011 return $grplist unless (
2012 ref($grplist) and @$grplist > 1 );
2014 my @list = sort { $a->name cmp $b->name } @$grplist;
2017 for my $grp (@list) {
2019 if ($grp and !defined($grp->parent)) {
2023 my ($parent) = grep { $_->id == $grp->parent} @list;
2025 $parent->children([]) unless defined($parent->children);
2026 push( @{$parent->children}, $grp );
2034 __PACKAGE__->register_method(
2035 method => "add_user_to_groups",
2036 api_name => "open-ils.actor.user.set_groups",
2037 notes => <<" NOTES");
2038 Adds a user to one or more permission groups
2041 sub add_user_to_groups {
2042 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2044 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2045 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2046 return $evt if $evt;
2048 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2049 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2050 return $evt if $evt;
2052 $apputils->simplereq(
2054 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2056 for my $group (@$groups) {
2057 my $link = Fieldmapper::permission::usr_grp_map->new;
2059 $link->usr($userid);
2061 my $id = $apputils->simplereq(
2063 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2069 __PACKAGE__->register_method(
2070 method => "get_user_perm_groups",
2071 api_name => "open-ils.actor.user.get_groups",
2072 notes => <<" NOTES");
2073 Retrieve a user's permission groups.
2077 sub get_user_perm_groups {
2078 my( $self, $client, $authtoken, $userid ) = @_;
2080 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2081 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2082 return $evt if $evt;
2084 return $apputils->simplereq(
2086 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2091 __PACKAGE__->register_method (
2092 method => 'register_workstation',
2093 api_name => 'open-ils.actor.workstation.register.override',
2094 signature => q/@see open-ils.actor.workstation.register/);
2096 __PACKAGE__->register_method (
2097 method => 'register_workstation',
2098 api_name => 'open-ils.actor.workstation.register',
2100 Registers a new workstion in the system
2101 @param authtoken The login session key
2102 @param name The name of the workstation id
2103 @param owner The org unit that owns this workstation
2104 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2105 if the name is already in use.
2108 sub _register_workstation {
2109 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2110 my( $requestor, $evt ) = $U->checkses($authtoken);
2111 return $evt if $evt;
2112 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2113 return $evt if $evt;
2115 my $ws = $U->cstorereq(
2116 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2117 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2119 $ws = Fieldmapper::actor::workstation->new;
2120 $ws->owning_lib($owner);
2123 my $id = $U->storagereq(
2124 'open-ils.storage.direct.actor.workstation.create', $ws );
2125 return $U->DB_UPDATE_FAILED($ws) unless $id;
2131 sub register_workstation {
2132 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2134 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2135 return $e->event unless $e->checkauth;
2136 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2137 my $existing = $e->search_actor_workstation({name => $name});
2140 if( $self->api_name =~ /override/o ) {
2141 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2142 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2144 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2148 my $ws = Fieldmapper::actor::workstation->new;
2149 $ws->owning_lib($owner);
2151 $e->create_actor_workstation($ws) or return $e->event;
2153 return $ws->id; # note: editor sets the id on the new object for us
2157 __PACKAGE__->register_method (
2158 method => 'fetch_patron_note',
2159 api_name => 'open-ils.actor.note.retrieve.all',
2161 Returns a list of notes for a given user
2162 Requestor must have VIEW_USER permission if pub==false and
2163 @param authtoken The login session key
2164 @param args Hash of params including
2165 patronid : the patron's id
2166 pub : true if retrieving only public notes
2170 sub fetch_patron_note {
2171 my( $self, $conn, $authtoken, $args ) = @_;
2172 my $patronid = $$args{patronid};
2174 my($reqr, $evt) = $U->checkses($authtoken);
2177 ($patron, $evt) = $U->fetch_user($patronid);
2178 return $evt if $evt;
2181 if( $patronid ne $reqr->id ) {
2182 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2183 return $evt if $evt;
2185 return $U->cstorereq(
2186 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2187 { usr => $patronid, pub => 't' } );
2190 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2191 return $evt if $evt;
2193 return $U->cstorereq(
2194 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2197 __PACKAGE__->register_method (
2198 method => 'create_user_note',
2199 api_name => 'open-ils.actor.note.create',
2201 Creates a new note for the given user
2202 @param authtoken The login session key
2203 @param note The note object
2206 sub create_user_note {
2207 my( $self, $conn, $authtoken, $note ) = @_;
2208 my( $reqr, $patron, $evt ) =
2209 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2210 return $evt if $evt;
2211 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2213 $note->creator($reqr->id);
2214 my $id = $U->storagereq(
2215 'open-ils.storage.direct.actor.usr_note.create', $note );
2216 return $U->DB_UPDATE_FAILED($note) unless $id;
2221 __PACKAGE__->register_method (
2222 method => 'delete_user_note',
2223 api_name => 'open-ils.actor.note.delete',
2225 Deletes a note for the given user
2226 @param authtoken The login session key
2227 @param noteid The note id
2230 sub delete_user_note {
2231 my( $self, $conn, $authtoken, $noteid ) = @_;
2233 my $note = $U->cstorereq(
2234 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2235 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2237 my( $reqr, $patron, $evt ) =
2238 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2239 return $evt if $evt;
2240 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2242 my $stat = $U->storagereq(
2243 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2244 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2249 __PACKAGE__->register_method (
2250 method => 'update_user_note',
2251 api_name => 'open-ils.actor.note.update',
2253 @param authtoken The login session key
2254 @param note The note
2258 sub update_user_note {
2259 my( $self, $conn, $auth, $note ) = @_;
2260 my $e = new_editor(authtoken=>$auth, xact=>1);
2261 return $e->event unless $e->checkauth;
2262 my $patron = $e->retrieve_actor_user($note->usr)
2263 or return $e->event;
2264 return $e->event unless
2265 $e->allowed('UPDATE_USER', $patron->home_ou);
2266 $e->update_actor_user_note($note)
2267 or return $e->event;
2275 __PACKAGE__->register_method (
2276 method => 'create_closed_date',
2277 api_name => 'open-ils.actor.org_unit.closed_date.create',
2279 Creates a new closing entry for the given org_unit
2280 @param authtoken The login session key
2281 @param note The closed_date object
2284 sub create_closed_date {
2285 my( $self, $conn, $authtoken, $cd ) = @_;
2287 my( $user, $evt ) = $U->checkses($authtoken);
2288 return $evt if $evt;
2290 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2291 return $evt if $evt;
2293 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2295 my $id = $U->storagereq(
2296 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2297 return $U->DB_UPDATE_FAILED($cd) unless $id;
2302 __PACKAGE__->register_method (
2303 method => 'delete_closed_date',
2304 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2306 Deletes a closing entry for the given org_unit
2307 @param authtoken The login session key
2308 @param noteid The close_date id
2311 sub delete_closed_date {
2312 my( $self, $conn, $authtoken, $cd ) = @_;
2314 my( $user, $evt ) = $U->checkses($authtoken);
2315 return $evt if $evt;
2318 ($cd_obj, $evt) = fetch_closed_date($cd);
2319 return $evt if $evt;
2321 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2322 return $evt if $evt;
2324 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2326 my $stat = $U->storagereq(
2327 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2328 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2333 __PACKAGE__->register_method(
2334 method => 'usrname_exists',
2335 api_name => 'open-ils.actor.username.exists',
2337 Returns 1 if the requested username exists, returns 0 otherwise
2341 sub usrname_exists {
2342 my( $self, $conn, $auth, $usrname ) = @_;
2343 my $e = new_editor(authtoken=>$auth);
2344 return $e->event unless $e->checkauth;
2345 my $a = $e->search_actor_user({usrname => $usrname}, {idlist=>1});
2346 return $$a[0] if $a and @$a;
2350 __PACKAGE__->register_method(
2351 method => 'barcode_exists',
2352 api_name => 'open-ils.actor.barcode.exists',
2354 Returns 1 if the requested barcode exists, returns 0 otherwise
2358 sub barcode_exists {
2359 my( $self, $conn, $auth, $barcode ) = @_;
2360 my $e = new_editor(authtoken=>$auth);
2361 return $e->event unless $e->checkauth;
2362 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2363 return $$a[0] if $a and @$a;
2368 __PACKAGE__->register_method(
2369 method => 'retrieve_net_levels',
2370 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2373 sub retrieve_net_levels {
2374 my( $self, $conn, $auth ) = @_;
2375 my $e = new_editor(authtoken=>$auth);
2376 return $e->event unless $e->checkauth;
2377 return $e->retrieve_all_config_net_access_level();