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);
251 if(!$patron->isnew) {
252 $opatron = new_editor()->retrieve_actor_user($new_patron->id);
255 $apputils->commit_db_session($session);
256 my $fuser = flesh_user($new_patron->id());
259 # Log the new and old patron for investigation
260 $logger->info("$user_session updating patron object. orig patron object = ".
261 JSON->perl2JSON($opatron). " |||| new patron = ".JSON->perl2JSON($fuser));
271 return new_flesh_user($id, [
274 "standing_penalties",
278 "stat_cat_entries" ] );
286 # clone and clear stuff that would break the database
290 my $new_patron = $patron->clone;
292 $new_patron->clear_billing_address();
293 $new_patron->clear_mailing_address();
294 $new_patron->clear_addresses();
295 $new_patron->clear_card();
296 $new_patron->clear_cards();
297 $new_patron->clear_id();
298 $new_patron->clear_isnew();
299 $new_patron->clear_ischanged();
300 $new_patron->clear_isdeleted();
301 $new_patron->clear_stat_cat_entries();
302 $new_patron->clear_permissions();
303 $new_patron->clear_standing_penalties();
313 my $user_obj = shift;
315 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
316 return (undef, $evt) if $evt;
318 my $ex = $session->request(
319 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
321 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
324 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
326 my $id = $session->request(
327 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
328 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
330 $logger->info("Successfully created new user [$id] in DB");
332 return ( $session->request(
333 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
338 my( $session, $patron, $user_obj, $noperm) = @_;
340 $logger->info("Updating patron ".$patron->id." in DB");
345 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
346 return (undef, $evt) if $evt;
349 # update the password by itself to avoid the password protection magic
350 if( $patron->passwd ) {
351 my $s = $session->request(
352 'open-ils.storage.direct.actor.user.remote_update',
353 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
354 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
355 $patron->clear_passwd;
358 if(!$patron->ident_type) {
359 $patron->clear_ident_type;
360 $patron->clear_ident_value;
363 my $stat = $session->request(
364 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
365 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
370 sub _check_dup_ident {
371 my( $session, $patron ) = @_;
373 return undef unless $patron->ident_value;
376 ident_type => $patron->ident_type,
377 ident_value => $patron->ident_value,
380 $logger->debug("patron update searching for dup ident values: " .
381 $patron->ident_type . ':' . $patron->ident_value);
383 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
385 my $dups = $session->request(
386 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
389 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
396 sub _add_update_addresses {
400 my $new_patron = shift;
404 my $current_id; # id of the address before creation
406 for my $address (@{$patron->addresses()}) {
408 next unless ref $address;
409 $current_id = $address->id();
411 if( $patron->billing_address() and
412 $patron->billing_address() == $current_id ) {
413 $logger->info("setting billing addr to $current_id");
414 $new_patron->billing_address($address->id());
415 $new_patron->ischanged(1);
418 if( $patron->mailing_address() and
419 $patron->mailing_address() == $current_id ) {
420 $new_patron->mailing_address($address->id());
421 $logger->info("setting mailing addr to $current_id");
422 $new_patron->ischanged(1);
426 if($address->isnew()) {
428 $address->usr($new_patron->id());
430 ($address, $evt) = _add_address($session,$address);
431 return (undef, $evt) if $evt;
433 # we need to get the new id
434 if( $patron->billing_address() and
435 $patron->billing_address() == $current_id ) {
436 $new_patron->billing_address($address->id());
437 $logger->info("setting billing addr to $current_id");
438 $new_patron->ischanged(1);
441 if( $patron->mailing_address() and
442 $patron->mailing_address() == $current_id ) {
443 $new_patron->mailing_address($address->id());
444 $logger->info("setting mailing addr to $current_id");
445 $new_patron->ischanged(1);
448 } elsif($address->ischanged() ) {
450 ($address, $evt) = _update_address($session, $address);
451 return (undef, $evt) if $evt;
453 } elsif($address->isdeleted() ) {
455 if( $address->id() == $new_patron->mailing_address() ) {
456 $new_patron->clear_mailing_address();
457 ($new_patron, $evt) = _update_patron($session, $new_patron);
458 return (undef, $evt) if $evt;
461 if( $address->id() == $new_patron->billing_address() ) {
462 $new_patron->clear_billing_address();
463 ($new_patron, $evt) = _update_patron($session, $new_patron);
464 return (undef, $evt) if $evt;
467 $evt = _delete_address($session, $address);
468 return (undef, $evt) if $evt;
472 return ( $new_patron, undef );
476 # adds an address to the db and returns the address with new id
478 my($session, $address) = @_;
479 $address->clear_id();
481 $logger->info("Creating new address at street ".$address->street1);
483 # put the address into the database
484 my $id = $session->request(
485 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
486 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
489 return ($address, undef);
493 sub _update_address {
494 my( $session, $address ) = @_;
496 $logger->info("Updating address ".$address->id." in the DB");
498 my $stat = $session->request(
499 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
501 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
502 return ($address, undef);
507 sub _add_update_cards {
511 my $new_patron = shift;
515 my $virtual_id; #id of the card before creation
516 for my $card (@{$patron->cards()}) {
518 $card->usr($new_patron->id());
520 if(ref($card) and $card->isnew()) {
522 $virtual_id = $card->id();
523 ( $card, $evt ) = _add_card($session,$card);
524 return (undef, $evt) if $evt;
526 #if(ref($patron->card)) { $patron->card($patron->card->id); }
527 if($patron->card() == $virtual_id) {
528 $new_patron->card($card->id());
529 $new_patron->ischanged(1);
532 } elsif( ref($card) and $card->ischanged() ) {
533 $evt = _update_card($session, $card);
534 return (undef, $evt) if $evt;
538 return ( $new_patron, undef );
542 # adds an card to the db and returns the card with new id
544 my( $session, $card ) = @_;
547 $logger->info("Adding new patron card ".$card->barcode);
549 my $id = $session->request(
550 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
551 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
552 $logger->info("Successfully created patron card $id");
555 return ( $card, undef );
559 # returns event on error. returns undef otherwise
561 my( $session, $card ) = @_;
562 $logger->info("Updating patron card ".$card->id);
564 my $stat = $session->request(
565 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
566 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
573 # returns event on error. returns undef otherwise
574 sub _delete_address {
575 my( $session, $address ) = @_;
577 $logger->info("Deleting address ".$address->id." from DB");
579 my $stat = $session->request(
580 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
582 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
588 sub _add_survey_responses {
589 my ($session, $patron, $new_patron) = @_;
591 $logger->info( "Updating survey responses for patron ".$new_patron->id );
593 my $responses = $patron->survey_responses;
597 $_->usr($new_patron->id) for (@$responses);
599 my $evt = $U->simplereq( "open-ils.circ",
600 "open-ils.circ.survey.submit.user_id", $responses );
602 return (undef, $evt) if defined($U->event_code($evt));
606 return ( $new_patron, undef );
610 sub _create_stat_maps {
612 my($session, $user_session, $patron, $new_patron) = @_;
614 my $maps = $patron->stat_cat_entries();
616 for my $map (@$maps) {
618 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
620 if ($map->isdeleted()) {
621 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
623 } elsif ($map->isnew()) {
624 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
629 $map->target_usr($new_patron->id);
632 $logger->info("Updating stat entry with method $method and map $map");
634 my $stat = $session->request($method, $map)->gather(1);
635 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
639 return ($new_patron, undef);
642 sub _create_perm_maps {
644 my($session, $user_session, $patron, $new_patron) = @_;
646 my $maps = $patron->permissions;
648 for my $map (@$maps) {
650 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
651 if ($map->isdeleted()) {
652 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
653 } elsif ($map->isnew()) {
654 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
659 $map->usr($new_patron->id);
661 #warn( "Updating permissions with method $method and session $user_session and map $map" );
662 $logger->info( "Updating permissions with method $method and map $map" );
664 my $stat = $session->request($method, $map)->gather(1);
665 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
669 return ($new_patron, undef);
673 sub _create_standing_penalties {
675 my($session, $user_session, $patron, $new_patron) = @_;
677 my $maps = $patron->standing_penalties;
680 for my $map (@$maps) {
682 if ($map->isdeleted()) {
683 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
684 } elsif ($map->isnew()) {
685 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
691 $map->usr($new_patron->id);
693 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
695 my $stat = $session->request($method, $map)->gather(1);
696 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
699 return ($new_patron, undef);
704 __PACKAGE__->register_method(
705 method => "search_username",
706 api_name => "open-ils.actor.user.search.username",
709 sub search_username {
710 my($self, $client, $username) = @_;
711 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
713 "open-ils.cstore.direct.actor.user.search.atomic",
714 { usrname => $username }
722 __PACKAGE__->register_method(
723 method => "user_retrieve_by_barcode",
724 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
726 sub user_retrieve_by_barcode {
727 my($self, $client, $user_session, $barcode) = @_;
729 $logger->debug("Searching for user with barcode $barcode");
730 my ($user_obj, $evt) = $apputils->checkses($user_session);
733 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
735 "open-ils.cstore.direct.actor.card.search.atomic",
736 { barcode => $barcode }
739 if(!$card || !$card->[0]) {
740 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
744 my $user = flesh_user($card->usr());
746 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
749 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
756 __PACKAGE__->register_method(
757 method => "get_user_by_id",
758 api_name => "open-ils.actor.user.retrieve",);
761 my ($self, $client, $auth, $id) = @_;
762 my $e = new_editor(authtoken=>$auth);
763 return $e->event unless $e->checkauth;
764 my $user = $e->retrieve_actor_user($id)
766 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
772 __PACKAGE__->register_method(
773 method => "get_org_types",
774 api_name => "open-ils.actor.org_types.retrieve",);
778 my($self, $client) = @_;
779 return $org_types if $org_types;
780 return $org_types = new_editor()->retrieve_all_actor_org_unit_type();
785 __PACKAGE__->register_method(
786 method => "get_user_ident_types",
787 api_name => "open-ils.actor.user.ident_types.retrieve",
790 sub get_user_ident_types {
791 return $ident_types if $ident_types;
792 return $ident_types =
793 new_editor()->retrieve_all_config_identification_type();
799 __PACKAGE__->register_method(
800 method => "get_org_unit",
801 api_name => "open-ils.actor.org_unit.retrieve",
805 my( $self, $client, $user_session, $org_id ) = @_;
806 my $e = new_editor(authtoken => $user_session);
808 return $e->event unless $e->checkauth;
809 $org_id = $e->requestor->ws_ou;
811 my $o = $e->retrieve_actor_org_unit($org_id)
816 __PACKAGE__->register_method(
817 method => "search_org_unit",
818 api_name => "open-ils.actor.org_unit_list.search",
821 sub search_org_unit {
823 my( $self, $client, $field, $value ) = @_;
825 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
827 "open-ils.cstore.direct.actor.org_unit.search.atomic",
828 { $field => $value } );
836 __PACKAGE__->register_method(
837 method => "get_org_tree",
838 api_name => "open-ils.actor.org_tree.retrieve",
840 note => "Returns the entire org tree structure",
844 my( $self, $client) = @_;
846 $cache = OpenSRF::Utils::Cache->new("global", 0) unless $cache;
847 my $tree = $cache->get_cache('orgtree');
848 return $tree if $tree;
850 $tree = new_editor()->search_actor_org_unit(
852 {"parent_ou" => undef },
855 flesh_fields => { aou => ['children'] },
856 order_by => { aou => 'name'}
861 $cache->put_cache('orgtree', $tree);
866 # turns an org list into an org tree
869 my( $self, $orglist) = @_;
871 return $orglist unless (
872 ref($orglist) and @$orglist > 1 );
875 $a->ou_type <=> $b->ou_type ||
876 $a->name cmp $b->name } @$orglist;
878 for my $org (@list) {
880 next unless ($org and defined($org->parent_ou));
881 my ($parent) = grep { $_->id == $org->parent_ou } @list;
884 $parent->children([]) unless defined($parent->children);
885 push( @{$parent->children}, $org );
893 __PACKAGE__->register_method(
894 method => "get_org_descendants",
895 api_name => "open-ils.actor.org_tree.descendants.retrieve"
898 # depth is optional. org_unit is the id
899 sub get_org_descendants {
900 my( $self, $client, $org_unit, $depth ) = @_;
901 my $orglist = $apputils->simple_scalar_request(
903 "open-ils.storage.actor.org_unit.descendants.atomic",
905 return $self->build_org_tree($orglist);
909 __PACKAGE__->register_method(
910 method => "get_org_ancestors",
911 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
914 # depth is optional. org_unit is the id
915 sub get_org_ancestors {
916 my( $self, $client, $org_unit, $depth ) = @_;
917 my $orglist = $apputils->simple_scalar_request(
919 "open-ils.storage.actor.org_unit.ancestors.atomic",
921 return $self->build_org_tree($orglist);
925 __PACKAGE__->register_method(
926 method => "get_standings",
927 api_name => "open-ils.actor.standings.retrieve"
932 return $user_standings if $user_standings;
933 return $user_standings =
934 $apputils->simple_scalar_request(
936 "open-ils.cstore.direct.config.standing.search.atomic",
937 { id => { "!=" => undef } }
943 __PACKAGE__->register_method(
944 method => "get_my_org_path",
945 api_name => "open-ils.actor.org_unit.full_path.retrieve"
948 sub get_my_org_path {
949 my( $self, $client, $user_session, $org_id ) = @_;
950 my $user_obj = $apputils->check_user_session($user_session);
951 if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
953 return $apputils->simple_scalar_request(
955 "open-ils.storage.actor.org_unit.full_path.atomic",
960 __PACKAGE__->register_method(
961 method => "patron_adv_search",
962 api_name => "open-ils.actor.patron.search.advanced" );
963 sub patron_adv_search {
964 my( $self, $client, $auth, $search_hash, $search_limit, $search_sort, $include_inactive ) = @_;
965 my $e = new_editor(authtoken=>$auth);
966 return $e->event unless $e->checkauth;
967 return $e->event unless $e->allowed('VIEW_USER');
968 return $U->storagereq(
969 "open-ils.storage.actor.user.crazy_search",
970 $search_hash, $search_limit, $search_sort, $include_inactive);
975 sub _verify_password {
976 my($user_session, $password) = @_;
977 my $user_obj = $apputils->check_user_session($user_session);
979 #grab the user with password
980 $user_obj = $apputils->simple_scalar_request(
982 "open-ils.cstore.direct.actor.user.retrieve",
985 if($user_obj->passwd eq $password) {
993 __PACKAGE__->register_method(
994 method => "update_password",
995 api_name => "open-ils.actor.user.password.update");
997 __PACKAGE__->register_method(
998 method => "update_password",
999 api_name => "open-ils.actor.user.username.update");
1001 __PACKAGE__->register_method(
1002 method => "update_password",
1003 api_name => "open-ils.actor.user.email.update");
1005 sub update_password {
1006 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1010 my $user_obj = $apputils->check_user_session($user_session);
1012 if($self->api_name =~ /password/o) {
1014 #make sure they know the current password
1015 if(!_verify_password($user_session, md5_hex($current_password))) {
1016 return OpenILS::Event->new('INCORRECT_PASSWORD');
1019 $logger->debug("update_password setting new password $new_value");
1020 $user_obj->passwd($new_value);
1022 } elsif($self->api_name =~ /username/o) {
1023 my $users = search_username(undef, undef, $new_value);
1024 if( $users and $users->[0] ) {
1025 return OpenILS::Event->new('USERNAME_EXISTS');
1027 $user_obj->usrname($new_value);
1029 } elsif($self->api_name =~ /email/o) {
1030 #warn "Updating email to $new_value\n";
1031 $user_obj->email($new_value);
1034 my $session = $apputils->start_db_session();
1036 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1037 return $evt if $evt;
1039 $apputils->commit_db_session($session);
1041 if($user_obj) { return 1; }
1046 __PACKAGE__->register_method(
1047 method => "check_user_perms",
1048 api_name => "open-ils.actor.user.perm.check",
1049 notes => <<" NOTES");
1050 Takes a login session, user id, an org id, and an array of perm type strings. For each
1051 perm type, if the user does *not* have the given permission it is added
1052 to a list which is returned from the method. If all permissions
1053 are allowed, an empty list is returned
1054 if the logged in user does not match 'user_id', then the logged in user must
1055 have VIEW_PERMISSION priveleges.
1058 sub check_user_perms {
1059 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1061 my( $staff, $evt ) = $apputils->checkses($login_session);
1062 return $evt if $evt;
1064 if($staff->id ne $user_id) {
1065 if( $evt = $apputils->check_perms(
1066 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1072 for my $perm (@$perm_types) {
1073 if($apputils->check_perms($user_id, $org_id, $perm)) {
1074 push @not_allowed, $perm;
1078 return \@not_allowed
1081 __PACKAGE__->register_method(
1082 method => "check_user_perms2",
1083 api_name => "open-ils.actor.user.perm.check.multi_org",
1085 Checks the permissions on a list of perms and orgs for a user
1086 @param authtoken The login session key
1087 @param user_id The id of the user to check
1088 @param orgs The array of org ids
1089 @param perms The array of permission names
1090 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1091 if the logged in user does not match 'user_id', then the logged in user must
1092 have VIEW_PERMISSION priveleges.
1095 sub check_user_perms2 {
1096 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1098 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1099 $authtoken, $user_id, 'VIEW_PERMISSION' );
1100 return $evt if $evt;
1103 for my $org (@$orgs) {
1104 for my $perm (@$perms) {
1105 if($apputils->check_perms($user_id, $org, $perm)) {
1106 push @not_allowed, [ $org, $perm ];
1111 return \@not_allowed
1115 __PACKAGE__->register_method(
1116 method => 'check_user_perms3',
1117 api_name => 'open-ils.actor.user.perm.highest_org',
1119 Returns the highest org unit id at which a user has a given permission
1120 If the requestor does not match the target user, the requestor must have
1121 'VIEW_PERMISSION' rights at the home org unit of the target user
1122 @param authtoken The login session key
1123 @param userid The id of the user in question
1124 @param perm The permission to check
1125 @return The org unit highest in the org tree within which the user has
1126 the requested permission
1129 sub check_user_perms3 {
1130 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1132 my( $staff, $target, $org, $evt );
1134 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1135 $authtoken, $userid, 'VIEW_PERMISSION' );
1136 return $evt if $evt;
1138 my $tree = $self->get_org_tree();
1139 return _find_highest_perm_org( $perm, $userid, $target->ws_ou, $tree );
1143 sub _find_highest_perm_org {
1144 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1145 my $org = $apputils->find_org($org_tree, $start_org );
1149 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1151 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1157 __PACKAGE__->register_method(
1158 method => 'check_user_perms4',
1159 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1161 Returns the highest org unit id at which a user has a given permission
1162 If the requestor does not match the target user, the requestor must have
1163 'VIEW_PERMISSION' rights at the home org unit of the target user
1164 @param authtoken The login session key
1165 @param userid The id of the user in question
1166 @param perms An array of perm names to check
1167 @return An array of orgId's representing the org unit
1168 highest in the org tree within which the user has the requested permission
1169 The arrah of orgId's has matches the order of the perms array
1172 sub check_user_perms4 {
1173 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1175 my( $staff, $target, $org, $evt );
1177 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1178 $authtoken, $userid, 'VIEW_PERMISSION' );
1179 return $evt if $evt;
1182 return [] unless ref($perms);
1183 my $tree = $self->get_org_tree();
1185 for my $p (@$perms) {
1186 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1194 __PACKAGE__->register_method(
1195 method => "user_fines_summary",
1196 api_name => "open-ils.actor.user.fines.summary",
1197 notes => <<" NOTES");
1198 Returns a short summary of the users total open fines, excluding voided fines
1199 Params are login_session, user_id
1200 Returns a 'mous' object.
1203 sub user_fines_summary {
1204 my( $self, $client, $login_session, $user_id ) = @_;
1206 my $user_obj = $apputils->check_user_session($login_session);
1207 if($user_obj->id ne $user_id) {
1208 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1209 return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY");
1213 return $apputils->simple_scalar_request(
1215 "open-ils.cstore.direct.money.open_user_summary.search",
1216 { usr => $user_id } );
1223 __PACKAGE__->register_method(
1224 method => "user_transactions",
1225 api_name => "open-ils.actor.user.transactions",
1226 notes => <<" NOTES");
1227 Returns a list of open user transactions (mbts objects);
1228 Params are login_session, user_id
1229 Optional third parameter is the transactions type. defaults to all
1232 __PACKAGE__->register_method(
1233 method => "user_transactions",
1234 api_name => "open-ils.actor.user.transactions.have_charge",
1235 notes => <<" NOTES");
1236 Returns a list of all open user transactions (mbts objects) that have an initial charge
1237 Params are login_session, user_id
1238 Optional third parameter is the transactions type. defaults to all
1241 __PACKAGE__->register_method(
1242 method => "user_transactions",
1243 api_name => "open-ils.actor.user.transactions.have_balance",
1244 notes => <<" NOTES");
1245 Returns a list of all open user transactions (mbts objects) that have a balance
1246 Params are login_session, user_id
1247 Optional third parameter is the transactions type. defaults to all
1250 __PACKAGE__->register_method(
1251 method => "user_transactions",
1252 api_name => "open-ils.actor.user.transactions.fleshed",
1253 notes => <<" NOTES");
1254 Returns an object/hash of transaction, circ, title where transaction = an open
1255 user transactions (mbts objects), circ is the attached circluation, and title
1256 is the title the circ points to
1257 Params are login_session, user_id
1258 Optional third parameter is the transactions type. defaults to all
1261 __PACKAGE__->register_method(
1262 method => "user_transactions",
1263 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1264 notes => <<" NOTES");
1265 Returns an object/hash of transaction, circ, title where transaction = an open
1266 user transactions that has an initial charge (mbts objects), circ is the
1267 attached circluation, and title is the title the circ points to
1268 Params are login_session, user_id
1269 Optional third parameter is the transactions type. defaults to all
1272 __PACKAGE__->register_method(
1273 method => "user_transactions",
1274 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1275 notes => <<" NOTES");
1276 Returns an object/hash of transaction, circ, title where transaction = an open
1277 user transaction that has a balance (mbts objects), circ is the attached
1278 circluation, and title is the title the circ points to
1279 Params are login_session, user_id
1280 Optional third parameter is the transaction type. defaults to all
1283 __PACKAGE__->register_method(
1284 method => "user_transactions",
1285 api_name => "open-ils.actor.user.transactions.count",
1286 notes => <<" NOTES");
1287 Returns an object/hash of transaction, circ, title where transaction = an open
1288 user transactions (mbts objects), circ is the attached circluation, and title
1289 is the title the circ points to
1290 Params are login_session, user_id
1291 Optional third parameter is the transactions type. defaults to all
1294 __PACKAGE__->register_method(
1295 method => "user_transactions",
1296 api_name => "open-ils.actor.user.transactions.have_charge.count",
1297 notes => <<" NOTES");
1298 Returns an object/hash of transaction, circ, title where transaction = an open
1299 user transactions that has an initial charge (mbts objects), circ is the
1300 attached circluation, and title is the title the circ points to
1301 Params are login_session, user_id
1302 Optional third parameter is the transactions type. defaults to all
1305 __PACKAGE__->register_method(
1306 method => "user_transactions",
1307 api_name => "open-ils.actor.user.transactions.have_balance.count",
1308 notes => <<" NOTES");
1309 Returns an object/hash of transaction, circ, title where transaction = an open
1310 user transaction that has a balance (mbts objects), circ is the attached
1311 circluation, and title is the title the circ points to
1312 Params are login_session, user_id
1313 Optional third parameter is the transaction type. defaults to all
1316 __PACKAGE__->register_method(
1317 method => "user_transactions",
1318 api_name => "open-ils.actor.user.transactions.have_balance.total",
1319 notes => <<" NOTES");
1320 Returns an object/hash of transaction, circ, title where transaction = an open
1321 user transaction that has a balance (mbts objects), circ is the attached
1322 circluation, and title is the title the circ points to
1323 Params are login_session, user_id
1324 Optional third parameter is the transaction type. defaults to all
1329 sub user_transactions {
1330 my( $self, $client, $login_session, $user_id, $type ) = @_;
1332 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1333 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1334 return $evt if $evt;
1336 my $api = $self->api_name();
1340 if(defined($type)) { @xact = (xact_type => $type);
1342 } else { @xact = (); }
1344 if($api =~ /have_charge/o) {
1347 ->method_lookup('open-ils.actor.user.transactions.history.have_bill')
1348 ->run($login_session => $user_id => $type);
1349 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1351 } elsif($api =~ /have_balance/o) {
1354 ->method_lookup('open-ils.actor.user.transactions.history.have_balance')
1355 ->run($login_session => $user_id => $type);
1360 ->method_lookup('open-ils.actor.user.transactions.history.still_open')
1361 ->run($login_session => $user_id => $type);
1362 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1366 $trans = [ grep { !$_->xact_finish } @$trans ];
1368 if($api =~ /total/o) {
1370 for my $t (@$trans) {
1371 $total += $t->balance_owed;
1374 $logger->debug("Total balance owed by user $user_id: $total");
1378 if($api =~ /count/o) { return scalar @$trans; }
1379 if($api !~ /fleshed/o) { return $trans; }
1382 for my $t (@$trans) {
1384 if( $t->xact_type ne 'circulation' ) {
1385 push @resp, {transaction => $t};
1389 my $circ = $apputils->simple_scalar_request(
1391 "open-ils.cstore.direct.action.circulation.retrieve",
1396 my $title = $apputils->simple_scalar_request(
1398 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1399 $circ->target_copy );
1403 my $u = OpenILS::Utils::ModsParser->new();
1404 $u->start_mods_batch($title->marc());
1405 my $mods = $u->finish_mods_batch();
1406 $mods->doc_id($title->id) if $mods;
1408 push @resp, {transaction => $t, circ => $circ, record => $mods };
1416 __PACKAGE__->register_method(
1417 method => "user_transaction_retrieve",
1418 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1420 notes => <<" NOTES");
1421 Returns a fleshedtransaction record
1423 __PACKAGE__->register_method(
1424 method => "user_transaction_retrieve",
1425 api_name => "open-ils.actor.user.transaction.retrieve",
1427 notes => <<" NOTES");
1428 Returns a transaction record
1430 sub user_transaction_retrieve {
1431 my( $self, $client, $login_session, $bill_id ) = @_;
1433 my $trans = $apputils->simple_scalar_request(
1435 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1439 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1440 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1441 return $evt if $evt;
1443 my $api = $self->api_name();
1444 if($api !~ /fleshed/o) { return $trans; }
1446 if( $trans->xact_type ne 'circulation' ) {
1447 $logger->debug("Returning non-circ transaction");
1448 return {transaction => $trans};
1451 my $circ = $apputils->simple_scalar_request(
1453 "open-ils..direct.action.circulation.retrieve",
1456 return {transaction => $trans} unless $circ;
1457 $logger->debug("Found the circ transaction");
1459 my $title = $apputils->simple_scalar_request(
1461 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1462 $circ->target_copy );
1464 return {transaction => $trans, circ => $circ } unless $title;
1465 $logger->debug("Found the circ title");
1469 my $u = OpenILS::Utils::ModsParser->new();
1470 $u->start_mods_batch($title->marc());
1471 $mods = $u->finish_mods_batch();
1473 if ($title->id == OILS_PRECAT_RECORD) {
1474 my $copy = $apputils->simple_scalar_request(
1476 "open-ils.cstore.direct.asset.copy.retrieve",
1477 $circ->target_copy );
1479 $mods = new Fieldmapper::metabib::virtual_record;
1480 $mods->doc_id(OILS_PRECAT_RECORD);
1481 $mods->title($copy->dummy_title);
1482 $mods->author($copy->dummy_author);
1486 $logger->debug("MODSized the circ title");
1488 return {transaction => $trans, circ => $circ, record => $mods };
1492 __PACKAGE__->register_method(
1493 method => "hold_request_count",
1494 api_name => "open-ils.actor.user.hold_requests.count",
1496 notes => <<" NOTES");
1497 Returns hold ready/total counts
1499 sub hold_request_count {
1500 my( $self, $client, $login_session, $userid ) = @_;
1502 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1503 $login_session, $userid, 'VIEW_HOLD' );
1504 return $evt if $evt;
1507 my $holds = $apputils->simple_scalar_request(
1509 "open-ils.cstore.direct.action.hold_request.search.atomic",
1512 fulfillment_time => {"=" => undef },
1513 cancel_time => undef,
1518 for my $h (@$holds) {
1519 next unless $h->capture_time and $h->current_copy;
1521 my $copy = $apputils->simple_scalar_request(
1523 "open-ils.cstore.direct.asset.copy.retrieve",
1527 if ($copy and $copy->status == 8) {
1532 return { total => scalar(@$holds), ready => scalar(@ready) };
1536 __PACKAGE__->register_method(
1537 method => "checkedout_count",
1538 api_name => "open-ils.actor.user.checked_out.count__",
1540 notes => <<" NOTES");
1541 Returns a transaction record
1545 sub checkedout_count {
1546 my( $self, $client, $login_session, $userid ) = @_;
1548 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1549 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1550 return $evt if $evt;
1552 my $circs = $apputils->simple_scalar_request(
1554 "open-ils.cstore.direct.action.circulation.search.atomic",
1555 { usr => $userid, stop_fines => undef }
1556 #{ usr => $userid, checkin_time => {"=" => undef } }
1559 my $parser = DateTime::Format::ISO8601->new;
1562 for my $c (@$circs) {
1563 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1564 my $due = $due_dt->epoch;
1566 if ($due < DateTime->today->epoch) {
1571 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1575 __PACKAGE__->register_method(
1576 method => "checked_out",
1577 api_name => "open-ils.actor.user.checked_out",
1580 Returns a structure of circulations objects sorted by
1581 out, overdue, lost, claims_returned, long_overdue.
1582 A list of IDs are returned of each type.
1583 lost, long_overdue, and claims_returned circ will not
1584 be "finished" (there is an outstanding balance or some
1585 other pending action on the circ).
1587 The .count method also includes a 'total' field which
1588 sums all "open" circs
1592 __PACKAGE__->register_method(
1593 method => "checked_out",
1594 api_name => "open-ils.actor.user.checked_out.count",
1596 signature => q/@see open-ils.actor.user.checked_out/
1600 my( $self, $conn, $auth, $userid ) = @_;
1602 my $e = new_editor(authtoken=>$auth);
1603 return $e->event unless $e->checkauth;
1605 if( $userid ne $e->requestor->id ) {
1606 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1609 my $count = $self->api_name =~ /count/;
1610 return _checked_out( $count, $e, $userid );
1614 my( $iscount, $e, $userid ) = @_;
1616 my $circs = $e->search_action_circulation(
1617 { usr => $userid, stop_fines => undef });
1619 my $mcircs = $e->search_action_circulation(
1622 checkin_time => undef,
1623 xact_finish => undef,
1624 stop_fines => OILS_STOP_FINES_MAX_FINES
1628 push( @$circs, @$mcircs );
1630 my $parser = DateTime::Format::ISO8601->new;
1632 # split the circs up into overdue and not-overdue circs
1634 for my $c (@$circs) {
1635 if( $c->due_date ) {
1636 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1637 my $due = $due_dt->epoch;
1638 if ($due < DateTime->today->epoch) {
1639 push @overdue, $c->id;
1648 # grab all of the lost, claims-returned, and longoverdue circs
1649 #my $open = $e->search_action_circulation(
1650 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1653 # these items have stop_fines, but no xact_finish, so money
1654 # is owed on them and they have not been checked in
1655 my $open = $e->search_action_circulation(
1658 stop_fines => { '!=' => undef },
1659 xact_finish => undef,
1660 checkin_time => undef,
1665 my( @lost, @cr, @lo );
1666 for my $c (@$open) {
1667 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1668 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1669 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1675 total => @$circs + @lost + @cr + @lo,
1676 out => scalar(@out),
1677 overdue => scalar(@overdue),
1678 lost => scalar(@lost),
1679 claims_returned => scalar(@cr),
1680 long_overdue => scalar(@lo)
1686 overdue => \@overdue,
1688 claims_returned => \@cr,
1689 long_overdue => \@lo
1695 __PACKAGE__->register_method(
1696 method => "checked_in_with_fines",
1697 api_name => "open-ils.actor.user.checked_in_with_fines",
1699 signature => q/@see open-ils.actor.user.checked_out/
1701 sub checked_in_with_fines {
1702 my( $self, $conn, $auth, $userid ) = @_;
1704 my $e = new_editor(authtoken=>$auth);
1705 return $e->event unless $e->checkauth;
1707 if( $userid ne $e->requestor->id ) {
1708 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1711 # money is owed on these items and they are checked in
1712 my $open = $e->search_action_circulation(
1715 xact_finish => undef,
1716 checkin_time => { "!=" => undef },
1721 my( @lost, @cr, @lo );
1722 for my $c (@$open) {
1723 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1724 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1725 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1730 claims_returned => \@cr,
1731 long_overdue => \@lo
1743 __PACKAGE__->register_method(
1744 method => "user_transaction_history",
1745 api_name => "open-ils.actor.user.transactions.history",
1747 notes => <<" NOTES");
1748 Returns a list of billable transaction ids for a user, optionally by type
1750 __PACKAGE__->register_method(
1751 method => "user_transaction_history",
1752 api_name => "open-ils.actor.user.transactions.history.have_charge",
1754 notes => <<" NOTES");
1755 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1757 __PACKAGE__->register_method(
1758 method => "user_transaction_history",
1759 api_name => "open-ils.actor.user.transactions.history.have_balance",
1761 notes => <<" NOTES");
1762 Returns a list of billable transaction ids for a user that have a balance, optionally by type
1764 __PACKAGE__->register_method(
1765 method => "user_transaction_history",
1766 api_name => "open-ils.actor.user.transactions.history.still_open",
1768 notes => <<" NOTES");
1769 Returns a list of billable transaction ids for a user that are not finished
1771 __PACKAGE__->register_method(
1772 method => "user_transaction_history",
1773 api_name => "open-ils.actor.user.transactions.history.have_bill",
1775 notes => <<" NOTES");
1776 Returns a list of billable transaction ids for a user that has billings
1782 sub _user_transaction_history {
1783 my( $self, $client, $login_session, $user_id, $type ) = @_;
1785 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1786 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1787 return $evt if $evt;
1789 my $api = $self->api_name();
1794 @xact = (xact_type => $type) if(defined($type));
1795 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1796 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1798 $logger->debug("searching for transaction history: @xact : @balance, @charge");
1800 my $trans = $apputils->simple_scalar_request(
1802 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1803 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1805 return [ map { $_->id } @$trans ];
1813 for my $x (@xacts) {
1814 my $s = new Fieldmapper::money::billable_transaction_summary;
1817 $s->xact_start( $x->xact_start );
1818 $s->xact_finish( $x->xact_finish );
1822 for my $b (@{ $x->billings }) {
1823 next if ($U->is_true($b->voided));
1825 $lb ||= $b->billing_ts;
1826 if ($b->billing_ts ge $lb) {
1827 $lb = $b->billing_ts;
1828 $s->last_billing_note($b->note);
1829 $s->last_billing_ts($b->billing_ts);
1830 $s->last_billing_type($b->billing_type);
1833 $s->total_owed( $to );
1837 for my $p (@{ $x->payments }) {
1838 next if ($U->is_true($p->voided));
1840 $lp ||= $p->payment_ts;
1841 if ($p->payment_ts ge $lp) {
1842 $lp = $p->payment_ts;
1843 $s->last_payment_note($p->note);
1844 $s->last_payment_ts($p->payment_ts);
1845 $s->last_payment_type($p->payment_type);
1848 $s->total_paid( $tp );
1850 $s->balance_owed( $s->total_owed - $s->total_paid );
1852 $s->xact_type( 'grocery' ) if ($x->grocery);
1853 $s->xact_type( 'circulation' ) if ($x->circulation);
1861 sub user_transaction_history {
1862 my( $self, $conn, $auth, $userid, $type ) = @_;
1863 my $e = new_editor(authtoken=>$auth);
1864 return $e->event unless $e->checkauth;
1865 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1867 my $api = $self->api_name;
1868 my @xact_finish = (xact_finish => undef ) if $api =~ /still_open/;
1870 my @xacts = @{ $e->search_money_billable_transaction(
1871 [ { usr => $userid, @xact_finish },
1873 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
1874 order_by => { mbt => 'xact_start DESC' },
1879 my @mbts = _make_mbts( @xacts );
1881 if(defined($type)) {
1882 @mbts = grep { $_->xact_type eq $type } @mbts;
1885 if($api =~ /have_balance/o) {
1886 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
1889 if($api =~ /have_charge/o) {
1890 @mbts = grep { defined($_->last_billing_ts) } @mbts;
1893 if($api =~ /have_bill/o) {
1894 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
1902 __PACKAGE__->register_method(
1903 method => "user_perms",
1904 api_name => "open-ils.actor.permissions.user_perms.retrieve",
1906 notes => <<" NOTES");
1907 Returns a list of permissions
1910 my( $self, $client, $authtoken, $user ) = @_;
1912 my( $staff, $evt ) = $apputils->checkses($authtoken);
1913 return $evt if $evt;
1915 $user ||= $staff->id;
1917 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1921 return $apputils->simple_scalar_request(
1923 "open-ils.storage.permission.user_perms.atomic",
1927 __PACKAGE__->register_method(
1928 method => "retrieve_perms",
1929 api_name => "open-ils.actor.permissions.retrieve",
1930 notes => <<" NOTES");
1931 Returns a list of permissions
1933 sub retrieve_perms {
1934 my( $self, $client ) = @_;
1935 return $apputils->simple_scalar_request(
1937 "open-ils.cstore.direct.permission.perm_list.search.atomic",
1938 { id => { '!=' => undef } }
1942 __PACKAGE__->register_method(
1943 method => "retrieve_groups",
1944 api_name => "open-ils.actor.groups.retrieve",
1945 notes => <<" NOTES");
1946 Returns a list of user groupss
1948 sub retrieve_groups {
1949 my( $self, $client ) = @_;
1950 return new_editor()->retrieve_all_permission_grp_tree();
1953 __PACKAGE__->register_method(
1954 method => "retrieve_org_address",
1955 api_name => "open-ils.actor.org_unit.address.retrieve",
1956 notes => <<' NOTES');
1957 Returns an org_unit address by ID
1958 @param An org_address ID
1960 sub retrieve_org_address {
1961 my( $self, $client, $id ) = @_;
1962 return $apputils->simple_scalar_request(
1964 "open-ils.cstore.direct.actor.org_address.retrieve",
1969 __PACKAGE__->register_method(
1970 method => "retrieve_groups_tree",
1971 api_name => "open-ils.actor.groups.tree.retrieve",
1972 notes => <<" NOTES");
1973 Returns a list of user groups
1975 sub retrieve_groups_tree {
1976 my( $self, $client ) = @_;
1977 return new_editor()->search_permission_grp_tree(
1982 flesh_fields => { pgt => ["children"] },
1983 order_by => { pgt => 'name'}
1990 # turns an org list into an org tree
1992 sub build_group_tree {
1994 my( $self, $grplist) = @_;
1996 return $grplist unless (
1997 ref($grplist) and @$grplist > 1 );
1999 my @list = sort { $a->name cmp $b->name } @$grplist;
2002 for my $grp (@list) {
2004 if ($grp and !defined($grp->parent)) {
2008 my ($parent) = grep { $_->id == $grp->parent} @list;
2010 $parent->children([]) unless defined($parent->children);
2011 push( @{$parent->children}, $grp );
2019 __PACKAGE__->register_method(
2020 method => "add_user_to_groups",
2021 api_name => "open-ils.actor.user.set_groups",
2022 notes => <<" NOTES");
2023 Adds a user to one or more permission groups
2026 sub add_user_to_groups {
2027 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2029 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2030 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2031 return $evt if $evt;
2033 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2034 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2035 return $evt if $evt;
2037 $apputils->simplereq(
2039 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2041 for my $group (@$groups) {
2042 my $link = Fieldmapper::permission::usr_grp_map->new;
2044 $link->usr($userid);
2046 my $id = $apputils->simplereq(
2048 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2054 __PACKAGE__->register_method(
2055 method => "get_user_perm_groups",
2056 api_name => "open-ils.actor.user.get_groups",
2057 notes => <<" NOTES");
2058 Retrieve a user's permission groups.
2062 sub get_user_perm_groups {
2063 my( $self, $client, $authtoken, $userid ) = @_;
2065 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2066 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2067 return $evt if $evt;
2069 return $apputils->simplereq(
2071 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2076 __PACKAGE__->register_method (
2077 method => 'register_workstation',
2078 api_name => 'open-ils.actor.workstation.register.override',
2079 signature => q/@see open-ils.actor.workstation.register/);
2081 __PACKAGE__->register_method (
2082 method => 'register_workstation',
2083 api_name => 'open-ils.actor.workstation.register',
2085 Registers a new workstion in the system
2086 @param authtoken The login session key
2087 @param name The name of the workstation id
2088 @param owner The org unit that owns this workstation
2089 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2090 if the name is already in use.
2093 sub _register_workstation {
2094 my( $self, $connection, $authtoken, $name, $owner ) = @_;
2095 my( $requestor, $evt ) = $U->checkses($authtoken);
2096 return $evt if $evt;
2097 $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2098 return $evt if $evt;
2100 my $ws = $U->cstorereq(
2101 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2102 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2104 $ws = Fieldmapper::actor::workstation->new;
2105 $ws->owning_lib($owner);
2108 my $id = $U->storagereq(
2109 'open-ils.storage.direct.actor.workstation.create', $ws );
2110 return $U->DB_UPDATE_FAILED($ws) unless $id;
2116 sub register_workstation {
2117 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2119 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2120 return $e->event unless $e->checkauth;
2121 return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2122 my $existing = $e->search_actor_workstation({name => $name});
2125 if( $self->api_name =~ /override/o ) {
2126 return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2127 return $e->event unless $e->delete_actor_workstation($$existing[0]);
2129 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2133 my $ws = Fieldmapper::actor::workstation->new;
2134 $ws->owning_lib($owner);
2136 $e->create_actor_workstation($ws) or return $e->event;
2138 return $ws->id; # note: editor sets the id on the new object for us
2142 __PACKAGE__->register_method (
2143 method => 'fetch_patron_note',
2144 api_name => 'open-ils.actor.note.retrieve.all',
2146 Returns a list of notes for a given user
2147 Requestor must have VIEW_USER permission if pub==false and
2148 @param authtoken The login session key
2149 @param args Hash of params including
2150 patronid : the patron's id
2151 pub : true if retrieving only public notes
2155 sub fetch_patron_note {
2156 my( $self, $conn, $authtoken, $args ) = @_;
2157 my $patronid = $$args{patronid};
2159 my($reqr, $evt) = $U->checkses($authtoken);
2162 ($patron, $evt) = $U->fetch_user($patronid);
2163 return $evt if $evt;
2166 if( $patronid ne $reqr->id ) {
2167 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2168 return $evt if $evt;
2170 return $U->cstorereq(
2171 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2172 { usr => $patronid, pub => 't' } );
2175 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2176 return $evt if $evt;
2178 return $U->cstorereq(
2179 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2182 __PACKAGE__->register_method (
2183 method => 'create_user_note',
2184 api_name => 'open-ils.actor.note.create',
2186 Creates a new note for the given user
2187 @param authtoken The login session key
2188 @param note The note object
2191 sub create_user_note {
2192 my( $self, $conn, $authtoken, $note ) = @_;
2193 my( $reqr, $patron, $evt ) =
2194 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2195 return $evt if $evt;
2196 $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2198 $note->creator($reqr->id);
2199 my $id = $U->storagereq(
2200 'open-ils.storage.direct.actor.usr_note.create', $note );
2201 return $U->DB_UPDATE_FAILED($note) unless $id;
2206 __PACKAGE__->register_method (
2207 method => 'delete_user_note',
2208 api_name => 'open-ils.actor.note.delete',
2210 Deletes a note for the given user
2211 @param authtoken The login session key
2212 @param noteid The note id
2215 sub delete_user_note {
2216 my( $self, $conn, $authtoken, $noteid ) = @_;
2218 my $note = $U->cstorereq(
2219 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2220 return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2222 my( $reqr, $patron, $evt ) =
2223 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2224 return $evt if $evt;
2225 $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2227 my $stat = $U->storagereq(
2228 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2229 return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2234 __PACKAGE__->register_method (
2235 method => 'update_user_note',
2236 api_name => 'open-ils.actor.note.update',
2238 @param authtoken The login session key
2239 @param note The note
2243 sub update_user_note {
2244 my( $self, $conn, $auth, $note ) = @_;
2245 my $e = new_editor(authtoken=>$auth, xact=>1);
2246 return $e->event unless $e->checkauth;
2247 my $patron = $e->retrieve_actor_user($note->usr)
2248 or return $e->event;
2249 return $e->event unless
2250 $e->allowed('UPDATE_USER', $patron->home_ou);
2251 $e->update_actor_user_note($note)
2252 or return $e->event;
2260 __PACKAGE__->register_method (
2261 method => 'create_closed_date',
2262 api_name => 'open-ils.actor.org_unit.closed_date.create',
2264 Creates a new closing entry for the given org_unit
2265 @param authtoken The login session key
2266 @param note The closed_date object
2269 sub create_closed_date {
2270 my( $self, $conn, $authtoken, $cd ) = @_;
2272 my( $user, $evt ) = $U->checkses($authtoken);
2273 return $evt if $evt;
2275 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2276 return $evt if $evt;
2278 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2280 my $id = $U->storagereq(
2281 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2282 return $U->DB_UPDATE_FAILED($cd) unless $id;
2287 __PACKAGE__->register_method (
2288 method => 'delete_closed_date',
2289 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2291 Deletes a closing entry for the given org_unit
2292 @param authtoken The login session key
2293 @param noteid The close_date id
2296 sub delete_closed_date {
2297 my( $self, $conn, $authtoken, $cd ) = @_;
2299 my( $user, $evt ) = $U->checkses($authtoken);
2300 return $evt if $evt;
2303 ($cd_obj, $evt) = fetch_closed_date($cd);
2304 return $evt if $evt;
2306 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2307 return $evt if $evt;
2309 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2311 my $stat = $U->storagereq(
2312 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2313 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2318 __PACKAGE__->register_method(
2319 method => 'usrname_exists',
2320 api_name => 'open-ils.actor.username.exists',
2322 Returns 1 if the requested username exists, returns 0 otherwise
2326 sub usrname_exists {
2327 my( $self, $conn, $auth, $usrname ) = @_;
2328 my $e = new_editor(authtoken=>$auth);
2329 return $e->event unless $e->checkauth;
2330 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2331 return $$a[0] if $a and @$a;
2335 __PACKAGE__->register_method(
2336 method => 'barcode_exists',
2337 api_name => 'open-ils.actor.barcode.exists',
2339 Returns 1 if the requested barcode exists, returns 0 otherwise
2343 sub barcode_exists {
2344 my( $self, $conn, $auth, $barcode ) = @_;
2345 my $e = new_editor(authtoken=>$auth);
2346 return $e->event unless $e->checkauth;
2347 my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2348 return $$a[0] if $a and @$a;
2353 __PACKAGE__->register_method(
2354 method => 'retrieve_net_levels',
2355 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2358 sub retrieve_net_levels {
2359 my( $self, $conn, $auth ) = @_;
2360 my $e = new_editor(authtoken=>$auth);
2361 return $e->event unless $e->checkauth;
2362 return $e->retrieve_all_config_net_access_level();
2366 __PACKAGE__->register_method(
2367 method => 'fetch_org_by_shortname',
2368 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2370 sub fetch_org_by_shortname {
2371 my( $self, $conn, $sname ) = @_;
2372 my $e = new_editor();
2373 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2374 return $e->event unless $org;
2379 __PACKAGE__->register_method(
2380 method => 'session_home_lib',
2381 api_name => 'open-ils.actor.session.home_lib',
2384 sub session_home_lib {
2385 my( $self, $conn, $auth ) = @_;
2386 my $e = new_editor(authtoken=>$auth);
2387 return undef unless $e->checkauth;
2388 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2389 return $org->shortname;
2394 __PACKAGE__->register_method(
2395 method => 'slim_tree',
2396 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2399 my $tree = new_editor()->search_actor_org_unit(
2401 {"parent_ou" => undef },
2404 flesh_fields => { aou => ['children'] },
2405 order_by => { aou => 'name'},
2406 select => { aou => ["id","shortname", "name"]},
2411 return trim_tree($tree);
2417 return undef unless $tree;
2419 code => $tree->shortname,
2420 name => $tree->name,
2422 if( $tree->children and @{$tree->children} ) {
2423 $htree->{children} = [];
2424 for my $c (@{$tree->children}) {
2425 push( @{$htree->{children}}, trim_tree($c) );
2434 __PACKAGE__->register_method(
2435 method => "user_retrieve_fleshed_by_id",
2436 api_name => "open-ils.actor.user.fleshed.retrieve",);
2438 sub user_retrieve_fleshed_by_id {
2439 my( $self, $client, $auth, $user_id, $fields ) = @_;
2440 my $e = new_editor(authtoken => $auth);
2441 return $e->event unless $e->checkauth;
2442 if( $e->requestor->id != $user_id ) {
2443 return $e->event unless $e->allowed('VIEW_USER');
2448 "standing_penalties",
2452 "stat_cat_entries" ];
2453 return new_flesh_user($user_id, $fields, $e);
2457 sub new_flesh_user {
2460 my $fields = shift || [];
2461 my $e = shift || new_editor(xact=>1);
2463 my $user = $e->retrieve_actor_user(
2468 "flesh_fields" => { "au" => $fields }
2471 ) or return $e->event;
2474 if( grep { $_ eq 'addresses' } @$fields ) {
2476 $user->addresses([]) unless @{$user->addresses};
2478 if( ref $user->billing_address ) {
2479 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2480 push( @{$user->addresses}, $user->billing_address );
2484 if( ref $user->mailing_address ) {
2485 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2486 push( @{$user->addresses}, $user->mailing_address );
2492 $user->clear_passwd();
2499 __PACKAGE__->register_method(
2500 method => "user_retrieve_parts",
2501 api_name => "open-ils.actor.user.retrieve.parts",);
2503 sub user_retrieve_parts {
2504 my( $self, $client, $auth, $user_id, $fields ) = @_;
2505 my $e = new_editor(authtoken => $auth);
2506 return $e->event unless $e->checkauth;
2507 if( $e->requestor->id != $user_id ) {
2508 return $e->event unless $e->allowed('VIEW_USER');
2511 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2512 push(@resp, $user->$_()) for(@$fields);