1 package OpenILS::Application::Actor;
2 use OpenILS::Application;
3 use base qw/OpenILS::Application/;
4 use strict; use warnings;
6 $Data::Dumper::Indent = 0;
9 use Digest::MD5 qw(md5_hex);
11 use OpenSRF::EX qw(:try);
14 use OpenILS::Application::AppUtils;
16 use OpenILS::Utils::Fieldmapper;
17 use OpenILS::Utils::ModsParser;
18 use OpenSRF::Utils::Logger qw/$logger/;
19 use OpenSRF::Utils qw/:datetime/;
20 use OpenSRF::Utils::SettingsClient;
22 use OpenSRF::Utils::Cache;
24 use OpenSRF::Utils::JSON;
26 use DateTime::Format::ISO8601;
27 use OpenILS::Const qw/:const/;
29 use OpenILS::Application::Actor::Container;
30 use OpenILS::Application::Actor::ClosedDates;
32 use OpenILS::Utils::CStoreEditor qw/:funcs/;
34 use OpenILS::Application::Actor::UserGroups;
36 OpenILS::Application::Actor::Container->initialize();
37 OpenILS::Application::Actor::UserGroups->initialize();
38 OpenILS::Application::Actor::ClosedDates->initialize();
41 my $apputils = "OpenILS::Application::AppUtils";
44 sub _d { warn "Patron:\n" . Dumper(shift()); }
49 my $set_user_settings;
52 __PACKAGE__->register_method(
53 method => "set_user_settings",
54 api_name => "open-ils.actor.patron.settings.update",
56 sub set_user_settings {
57 my( $self, $client, $user_session, $uid, $settings ) = @_;
59 $logger->debug("Setting user settings: $user_session, $uid, " . Dumper($settings));
61 my( $staff, $user, $evt ) =
62 $apputils->checkses_requestor( $user_session, $uid, 'UPDATE_USER' );
66 [{ usr => $user->id, name => $_}, {value => $$settings{$_}}] } keys %$settings;
68 $_->[1]->{value} = OpenSRF::Utils::JSON->perl2JSON($_->[1]->{value}) for @params;
70 $logger->activity("User " . $staff->id . " updating user $uid settings with: " . Dumper(\@params));
72 my $ses = $U->start_db_session();
73 my $stat = $ses->request(
74 'open-ils.storage.direct.actor.user_setting.batch.merge', @params )->gather(1);
75 $U->commit_db_session($ses);
82 __PACKAGE__->register_method(
83 method => "set_ou_settings",
84 api_name => "open-ils.actor.org_unit.settings.update",
87 my( $self, $client, $user_session, $ouid, $settings ) = @_;
89 my( $staff, $evt ) = $apputils->checkses( $user_session );
91 $evt = $apputils->check_perms( $staff->id, $ouid, 'UPDATE_ORG_SETTING' );
95 for my $set (keys %$settings) {
97 my $json = OpenSRF::Utils::JSON->perl2JSON($$settings{$set});
98 $logger->activity("updating org_unit.setting: $ouid : $set : $json");
101 { org_unit => $ouid, name => $set },
102 { value => $json } );
105 my $ses = $U->start_db_session();
106 my $stat = $ses->request(
107 'open-ils.storage.direct.actor.org_unit_setting.merge', @params )->gather(1);
108 $U->commit_db_session($ses);
114 my $fetch_user_settings;
115 my $fetch_ou_settings;
117 __PACKAGE__->register_method(
118 method => "user_settings",
119 api_name => "open-ils.actor.patron.settings.retrieve",
122 my( $self, $client, $user_session, $uid, $setting ) = @_;
124 my( $staff, $user, $evt ) =
125 $apputils->checkses_requestor( $user_session, $uid, 'VIEW_USER' );
128 $logger->debug("User " . $staff->id . " fetching user $uid\n");
129 my $s = $apputils->simplereq(
131 'open-ils.cstore.direct.actor.user_setting.search.atomic', { usr => $uid } );
133 my $settings = { map { ( $_->name => OpenSRF::Utils::JSON->JSON2perl($_->value) ) } @$s };
135 return $$settings{$setting} if $setting;
141 __PACKAGE__->register_method(
142 method => "ou_settings",
143 api_name => "open-ils.actor.org_unit.settings.retrieve",
146 my( $self, $client, $ouid ) = @_;
148 $logger->info("Fetching org unit settings for org $ouid");
150 my $s = $apputils->simplereq(
152 'open-ils.cstore.direct.actor.org_unit_setting.search.atomic', {org_unit => $ouid});
154 return { map { ( $_->name => OpenSRF::Utils::JSON->JSON2perl($_->value) ) } @$s };
159 __PACKAGE__->register_method(
160 api_name => 'open-ils.actor.ou_setting.ancestor_default',
161 method => 'ou_ancestor_setting',
164 # ------------------------------------------------------------------
165 # Attempts to find the org setting value for a given org. if not
166 # found at the requested org, searches up the org tree until it
167 # finds a parent that has the requested setting.
168 # when found, returns { org => $id, value => $value }
169 # otherwise, returns NULL
170 # ------------------------------------------------------------------
171 sub ou_ancestor_setting {
172 my( $self, $client, $orgid, $name ) = @_;
173 return $U->ou_ancestor_setting($orgid, $name);
179 __PACKAGE__->register_method (
180 method => "ou_setting_delete",
181 api_name => 'open-ils.actor.org_setting.delete',
183 Deletes a specific org unit setting for a specific location
184 @param authtoken The login session key
185 @param orgid The org unit whose setting we're changing
186 @param setting The name of the setting to delete
187 @return True value on success.
191 sub ou_setting_delete {
192 my( $self, $conn, $authtoken, $orgid, $setting ) = @_;
193 my( $reqr, $evt) = $U->checkses($authtoken);
195 $evt = $U->check_perms($reqr->id, $orgid, 'UPDATE_ORG_SETTING');
198 my $id = $U->cstorereq(
199 'open-ils.cstore.direct.actor.org_unit_setting.id_list',
200 { name => $setting, org_unit => $orgid } );
202 $logger->debug("Retrieved setting $id in org unit setting delete");
204 my $s = $U->cstorereq(
205 'open-ils.cstore.direct.actor.org_unit_setting.delete', $id );
207 $logger->activity("User ".$reqr->id." deleted org unit setting $id") if $s;
221 __PACKAGE__->register_method(
222 method => "update_patron",
223 api_name => "open-ils.actor.patron.update",);
226 my( $self, $client, $user_session, $patron ) = @_;
228 my $session = $apputils->start_db_session();
232 $logger->info("Creating new patron...") if $patron->isnew;
233 $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
235 my( $user_obj, $evt ) = $U->checkses($user_session);
238 $evt = check_group_perm($session, $user_obj, $patron);
242 # $new_patron is the patron in progress. $patron is the original patron
243 # passed in with the method. new_patron will change as the components
244 # of patron are added/updated.
248 # unflesh the real items on the patron
249 $patron->card( $patron->card->id ) if(ref($patron->card));
250 $patron->billing_address( $patron->billing_address->id )
251 if(ref($patron->billing_address));
252 $patron->mailing_address( $patron->mailing_address->id )
253 if(ref($patron->mailing_address));
255 # create/update the patron first so we can use his id
256 if($patron->isnew()) {
257 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
259 } else { $new_patron = $patron; }
261 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
264 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
267 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
270 # re-update the patron if anything has happened to him during this process
271 if($new_patron->ischanged()) {
272 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
276 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
279 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
282 ($new_patron, $evt) = _create_standing_penalties($session, $user_session, $patron, $new_patron, $user_obj);
285 $logger->activity("user ".$user_obj->id." updating/creating user ".$new_patron->id);
288 if(!$patron->isnew) {
289 $opatron = new_editor()->retrieve_actor_user($new_patron->id);
292 $apputils->commit_db_session($session);
293 my $fuser = flesh_user($new_patron->id());
296 # Log the new and old patron for investigation
297 $logger->info("$user_session updating patron object. orig patron object = ".
298 OpenSRF::Utils::JSON->perl2JSON($opatron). " |||| new patron = ".OpenSRF::Utils::JSON->perl2JSON($fuser));
308 return new_flesh_user($id, [
311 "standing_penalties",
315 "stat_cat_entries" ] );
323 # clone and clear stuff that would break the database
327 my $new_patron = $patron->clone;
329 $new_patron->clear_billing_address();
330 $new_patron->clear_mailing_address();
331 $new_patron->clear_addresses();
332 $new_patron->clear_card();
333 $new_patron->clear_cards();
334 $new_patron->clear_id();
335 $new_patron->clear_isnew();
336 $new_patron->clear_ischanged();
337 $new_patron->clear_isdeleted();
338 $new_patron->clear_stat_cat_entries();
339 $new_patron->clear_permissions();
340 $new_patron->clear_standing_penalties();
350 my $user_obj = shift;
352 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
353 return (undef, $evt) if $evt;
355 my $ex = $session->request(
356 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
358 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
361 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
363 my $id = $session->request(
364 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
365 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
367 $logger->info("Successfully created new user [$id] in DB");
369 return ( $session->request(
370 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
374 sub check_group_perm {
375 my( $session, $requestor, $patron ) = @_;
378 # first let's see if the requestor has
379 # priveleges to update this user in any way
380 if( ! $patron->isnew ) {
381 my $p = $session->request(
382 'open-ils.storage.direct.actor.user.retrieve', $patron->id )->gather(1);
384 # If we are the requestor (trying to update our own account)
385 # and we are not trying to change our profile, we're good
386 if( $p->id == $requestor->id and
387 $p->profile == $patron->profile ) {
392 $evt = group_perm_failed($session, $requestor, $p);
396 # They are allowed to edit this patron.. can they put the
397 # patron into the group requested?
398 $evt = group_perm_failed($session, $requestor, $patron);
404 sub group_perm_failed {
405 my( $session, $requestor, $patron ) = @_;
409 my $grpid = $patron->profile;
413 $logger->debug("user update looking for group perm for group $grpid");
414 $grp = $session->request(
415 'open-ils.storage.direct.permission.grp_tree.retrieve', $grpid )->gather(1);
416 return OpenILS::Event->new('PERMISSION_GRP_TREE_NOT_FOUND') unless $grp;
418 } while( !($perm = $grp->application_perm) and ($grpid = $grp->parent) );
420 $logger->info("user update checking perm $perm on user ".
421 $requestor->id." for update/create on user username=".$patron->usrname);
423 my $evt = $U->check_perms($requestor->id, $patron->home_ou, $perm);
431 my( $session, $patron, $user_obj, $noperm) = @_;
433 $logger->info("Updating patron ".$patron->id." in DB");
438 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
439 return (undef, $evt) if $evt;
442 # update the password by itself to avoid the password protection magic
443 if( $patron->passwd ) {
444 my $s = $session->request(
445 'open-ils.storage.direct.actor.user.remote_update',
446 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
447 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
448 $patron->clear_passwd;
451 if(!$patron->ident_type) {
452 $patron->clear_ident_type;
453 $patron->clear_ident_value;
456 $evt = verify_last_xact($session, $patron);
457 return (undef, $evt) if $evt;
459 my $stat = $session->request(
460 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
461 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
466 sub verify_last_xact {
467 my( $session, $patron ) = @_;
468 return undef unless $patron->id and $patron->id > 0;
469 my $p = $session->request(
470 'open-ils.storage.direct.actor.user.retrieve', $patron->id)->gather(1);
471 my $xact = $p->last_xact_id;
472 return undef unless $xact;
473 $logger->info("user xact = $xact, saving with xact " . $patron->last_xact_id);
474 return OpenILS::Event->new('XACT_COLLISION')
475 if $xact != $patron->last_xact_id;
480 sub _check_dup_ident {
481 my( $session, $patron ) = @_;
483 return undef unless $patron->ident_value;
486 ident_type => $patron->ident_type,
487 ident_value => $patron->ident_value,
490 $logger->debug("patron update searching for dup ident values: " .
491 $patron->ident_type . ':' . $patron->ident_value);
493 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
495 my $dups = $session->request(
496 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
499 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
506 sub _add_update_addresses {
510 my $new_patron = shift;
514 my $current_id; # id of the address before creation
516 for my $address (@{$patron->addresses()}) {
518 next unless ref $address;
519 $current_id = $address->id();
521 if( $patron->billing_address() and
522 $patron->billing_address() == $current_id ) {
523 $logger->info("setting billing addr to $current_id");
524 $new_patron->billing_address($address->id());
525 $new_patron->ischanged(1);
528 if( $patron->mailing_address() and
529 $patron->mailing_address() == $current_id ) {
530 $new_patron->mailing_address($address->id());
531 $logger->info("setting mailing addr to $current_id");
532 $new_patron->ischanged(1);
536 if($address->isnew()) {
538 $address->usr($new_patron->id());
540 ($address, $evt) = _add_address($session,$address);
541 return (undef, $evt) if $evt;
543 # we need to get the new id
544 if( $patron->billing_address() and
545 $patron->billing_address() == $current_id ) {
546 $new_patron->billing_address($address->id());
547 $logger->info("setting billing addr to $current_id");
548 $new_patron->ischanged(1);
551 if( $patron->mailing_address() and
552 $patron->mailing_address() == $current_id ) {
553 $new_patron->mailing_address($address->id());
554 $logger->info("setting mailing addr to $current_id");
555 $new_patron->ischanged(1);
558 } elsif($address->ischanged() ) {
560 ($address, $evt) = _update_address($session, $address);
561 return (undef, $evt) if $evt;
563 } elsif($address->isdeleted() ) {
565 if( $address->id() == $new_patron->mailing_address() ) {
566 $new_patron->clear_mailing_address();
567 ($new_patron, $evt) = _update_patron($session, $new_patron);
568 return (undef, $evt) if $evt;
571 if( $address->id() == $new_patron->billing_address() ) {
572 $new_patron->clear_billing_address();
573 ($new_patron, $evt) = _update_patron($session, $new_patron);
574 return (undef, $evt) if $evt;
577 $evt = _delete_address($session, $address);
578 return (undef, $evt) if $evt;
582 return ( $new_patron, undef );
586 # adds an address to the db and returns the address with new id
588 my($session, $address) = @_;
589 $address->clear_id();
591 $logger->info("Creating new address at street ".$address->street1);
593 # put the address into the database
594 my $id = $session->request(
595 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
596 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
599 return ($address, undef);
603 sub _update_address {
604 my( $session, $address ) = @_;
606 $logger->info("Updating address ".$address->id." in the DB");
608 my $stat = $session->request(
609 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
611 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
612 return ($address, undef);
617 sub _add_update_cards {
621 my $new_patron = shift;
625 my $virtual_id; #id of the card before creation
626 for my $card (@{$patron->cards()}) {
628 $card->usr($new_patron->id());
630 if(ref($card) and $card->isnew()) {
632 $virtual_id = $card->id();
633 ( $card, $evt ) = _add_card($session,$card);
634 return (undef, $evt) if $evt;
636 #if(ref($patron->card)) { $patron->card($patron->card->id); }
637 if($patron->card() == $virtual_id) {
638 $new_patron->card($card->id());
639 $new_patron->ischanged(1);
642 } elsif( ref($card) and $card->ischanged() ) {
643 $evt = _update_card($session, $card);
644 return (undef, $evt) if $evt;
648 return ( $new_patron, undef );
652 # adds an card to the db and returns the card with new id
654 my( $session, $card ) = @_;
657 $logger->info("Adding new patron card ".$card->barcode);
659 my $id = $session->request(
660 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
661 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
662 $logger->info("Successfully created patron card $id");
665 return ( $card, undef );
669 # returns event on error. returns undef otherwise
671 my( $session, $card ) = @_;
672 $logger->info("Updating patron card ".$card->id);
674 my $stat = $session->request(
675 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
676 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
683 # returns event on error. returns undef otherwise
684 sub _delete_address {
685 my( $session, $address ) = @_;
687 $logger->info("Deleting address ".$address->id." from DB");
689 my $stat = $session->request(
690 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
692 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
698 sub _add_survey_responses {
699 my ($session, $patron, $new_patron) = @_;
701 $logger->info( "Updating survey responses for patron ".$new_patron->id );
703 my $responses = $patron->survey_responses;
707 $_->usr($new_patron->id) for (@$responses);
709 my $evt = $U->simplereq( "open-ils.circ",
710 "open-ils.circ.survey.submit.user_id", $responses );
712 return (undef, $evt) if defined($U->event_code($evt));
716 return ( $new_patron, undef );
720 sub _create_stat_maps {
722 my($session, $user_session, $patron, $new_patron) = @_;
724 my $maps = $patron->stat_cat_entries();
726 for my $map (@$maps) {
728 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
730 if ($map->isdeleted()) {
731 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
733 } elsif ($map->isnew()) {
734 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
739 $map->target_usr($new_patron->id);
742 $logger->info("Updating stat entry with method $method and map $map");
744 my $stat = $session->request($method, $map)->gather(1);
745 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
749 return ($new_patron, undef);
752 sub _create_perm_maps {
754 my($session, $user_session, $patron, $new_patron) = @_;
756 my $maps = $patron->permissions;
758 for my $map (@$maps) {
760 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
761 if ($map->isdeleted()) {
762 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
763 } elsif ($map->isnew()) {
764 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
769 $map->usr($new_patron->id);
771 #warn( "Updating permissions with method $method and session $user_session and map $map" );
772 $logger->info( "Updating permissions with method $method and map $map" );
774 my $stat = $session->request($method, $map)->gather(1);
775 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
779 return ($new_patron, undef);
783 __PACKAGE__->register_method(
784 method => "set_user_work_ous",
785 api_name => "open-ils.actor.user.work_ous.update",
788 sub set_user_work_ous {
794 my( $requestor, $evt ) = $apputils->checksesperm( $ses, 'ASSIGN_WORK_ORG_UNIT' );
797 my $session = $apputils->start_db_session();
799 for my $map (@$maps) {
801 my $method = "open-ils.storage.direct.permission.usr_work_ou_map.update";
802 if ($map->isdeleted()) {
803 $method = "open-ils.storage.direct.permission.usr_work_ou_map.delete";
804 } elsif ($map->isnew()) {
805 $method = "open-ils.storage.direct.permission.usr_work_ou_map.create";
809 #warn( "Updating permissions with method $method and session $ses and map $map" );
810 $logger->info( "Updating work_ou map with method $method and map $map" );
812 my $stat = $session->request($method, $map)->gather(1);
813 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
817 $apputils->commit_db_session($session);
819 return scalar(@$maps);
823 __PACKAGE__->register_method(
824 method => "set_user_perms",
825 api_name => "open-ils.actor.user.permissions.update",
834 my $session = $apputils->start_db_session();
836 my( $user_obj, $evt ) = $U->checkses($ses);
839 my $perms = $session->request('open-ils.storage.permission.user_perms.atomic', $user_obj->id)->gather(1);
842 $all = 1 if ($user_obj->super_user());
843 $all = 1 unless ($U->check_perms($user_obj->id, $user_obj->home_ou, 'EVERYTHING'));
845 for my $map (@$maps) {
847 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
848 if ($map->isdeleted()) {
849 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
850 } elsif ($map->isnew()) {
851 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
855 next if (!$all || !grep { $_->perm eq $map->perm and $_->grantable == 1 and $_->depth <= $map->depth } @$perms);
857 #warn( "Updating permissions with method $method and session $ses and map $map" );
858 $logger->info( "Updating permissions with method $method and map $map" );
860 my $stat = $session->request($method, $map)->gather(1);
861 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
865 $apputils->commit_db_session($session);
867 return scalar(@$maps);
871 sub _create_standing_penalties {
873 my($session, $user_session, $patron, $new_patron) = @_;
875 my $maps = $patron->standing_penalties;
878 for my $map (@$maps) {
880 if ($map->isdeleted()) {
881 $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
882 } elsif ($map->isnew()) {
883 $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
889 $map->usr($new_patron->id);
891 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
893 my $stat = $session->request($method, $map)->gather(1);
894 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
897 return ($new_patron, undef);
902 __PACKAGE__->register_method(
903 method => "search_username",
904 api_name => "open-ils.actor.user.search.username",
907 sub search_username {
908 my($self, $client, $username) = @_;
909 return new_editor()->search_actor_user({usrname=>$username});
915 __PACKAGE__->register_method(
916 method => "user_retrieve_by_barcode",
917 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
919 sub user_retrieve_by_barcode {
920 my($self, $client, $user_session, $barcode) = @_;
922 $logger->debug("Searching for user with barcode $barcode");
923 my ($user_obj, $evt) = $apputils->checkses($user_session);
926 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
928 "open-ils.cstore.direct.actor.card.search.atomic",
929 { barcode => $barcode }
932 if(!$card || !$card->[0]) {
933 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
937 my $user = flesh_user($card->usr());
939 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
942 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
949 __PACKAGE__->register_method(
950 method => "get_user_by_id",
951 api_name => "open-ils.actor.user.retrieve",);
954 my ($self, $client, $auth, $id) = @_;
955 my $e = new_editor(authtoken=>$auth);
956 return $e->event unless $e->checkauth;
957 my $user = $e->retrieve_actor_user($id)
959 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
965 __PACKAGE__->register_method(
966 method => "get_org_types",
967 api_name => "open-ils.actor.org_types.retrieve",);
971 my($self, $client) = @_;
972 return $org_types if $org_types;
973 return $org_types = new_editor()->retrieve_all_actor_org_unit_type();
978 __PACKAGE__->register_method(
979 method => "get_user_ident_types",
980 api_name => "open-ils.actor.user.ident_types.retrieve",
983 sub get_user_ident_types {
984 return $ident_types if $ident_types;
985 return $ident_types =
986 new_editor()->retrieve_all_config_identification_type();
992 __PACKAGE__->register_method(
993 method => "get_org_unit",
994 api_name => "open-ils.actor.org_unit.retrieve",
998 my( $self, $client, $user_session, $org_id ) = @_;
999 my $e = new_editor(authtoken => $user_session);
1001 return $e->event unless $e->checkauth;
1002 $org_id = $e->requestor->ws_ou;
1004 my $o = $e->retrieve_actor_org_unit($org_id)
1005 or return $e->event;
1009 __PACKAGE__->register_method(
1010 method => "search_org_unit",
1011 api_name => "open-ils.actor.org_unit_list.search",
1014 sub search_org_unit {
1016 my( $self, $client, $field, $value ) = @_;
1018 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
1020 "open-ils.cstore.direct.actor.org_unit.search.atomic",
1021 { $field => $value } );
1027 # build the org tree
1029 __PACKAGE__->register_method(
1030 method => "get_org_tree",
1031 api_name => "open-ils.actor.org_tree.retrieve",
1033 note => "Returns the entire org tree structure",
1037 my( $self, $client) = @_;
1039 $cache = OpenSRF::Utils::Cache->new("global", 0) unless $cache;
1040 my $tree = $cache->get_cache('orgtree');
1041 return $tree if $tree;
1043 $tree = new_editor()->search_actor_org_unit(
1045 {"parent_ou" => undef },
1048 flesh_fields => { aou => ['children'] },
1049 order_by => { aou => 'name'}
1054 $cache->put_cache('orgtree', $tree);
1059 # turns an org list into an org tree
1060 sub build_org_tree {
1062 my( $self, $orglist) = @_;
1064 return $orglist unless ref $orglist;
1065 return $$orglist[0] if @$orglist == 1;
1068 $a->ou_type <=> $b->ou_type ||
1069 $a->name cmp $b->name } @$orglist;
1071 for my $org (@list) {
1073 next unless ($org and defined($org->parent_ou));
1074 my ($parent) = grep { $_->id == $org->parent_ou } @list;
1075 next unless $parent;
1077 $parent->children([]) unless defined($parent->children);
1078 push( @{$parent->children}, $org );
1086 __PACKAGE__->register_method(
1087 method => "get_org_descendants",
1088 api_name => "open-ils.actor.org_tree.descendants.retrieve"
1091 # depth is optional. org_unit is the id
1092 sub get_org_descendants {
1093 my( $self, $client, $org_unit, $depth ) = @_;
1094 my $orglist = $apputils->simple_scalar_request(
1096 "open-ils.storage.actor.org_unit.descendants.atomic",
1097 $org_unit, $depth );
1098 return $self->build_org_tree($orglist);
1102 __PACKAGE__->register_method(
1103 method => "get_org_ancestors",
1104 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
1107 # depth is optional. org_unit is the id
1108 sub get_org_ancestors {
1109 my( $self, $client, $org_unit, $depth ) = @_;
1110 my $orglist = $apputils->simple_scalar_request(
1112 "open-ils.storage.actor.org_unit.ancestors.atomic",
1113 $org_unit, $depth );
1114 return $self->build_org_tree($orglist);
1118 __PACKAGE__->register_method(
1119 method => "get_standings",
1120 api_name => "open-ils.actor.standings.retrieve"
1125 return $user_standings if $user_standings;
1126 return $user_standings =
1127 $apputils->simple_scalar_request(
1129 "open-ils.cstore.direct.config.standing.search.atomic",
1130 { id => { "!=" => undef } }
1136 __PACKAGE__->register_method(
1137 method => "get_my_org_path",
1138 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1141 sub get_my_org_path {
1142 my( $self, $client, $auth, $org_id ) = @_;
1143 my $e = new_editor(authtoken=>$auth);
1144 return $e->event unless $e->checkauth;
1145 $org_id = $e->requestor->ws_ou unless defined $org_id;
1147 return $apputils->simple_scalar_request(
1149 "open-ils.storage.actor.org_unit.full_path.atomic",
1154 __PACKAGE__->register_method(
1155 method => "patron_adv_search",
1156 api_name => "open-ils.actor.patron.search.advanced" );
1157 sub patron_adv_search {
1158 my( $self, $client, $auth, $search_hash,
1159 $search_limit, $search_sort, $include_inactive, $search_depth ) = @_;
1161 my $e = new_editor(authtoken=>$auth);
1162 return $e->event unless $e->checkauth;
1163 return $e->event unless $e->allowed('VIEW_USER');
1164 return $U->storagereq(
1165 "open-ils.storage.actor.user.crazy_search", $search_hash,
1166 $search_limit, $search_sort, $include_inactive, $e->requestor->ws_ou, $search_depth);
1172 sub _verify_password {
1173 my($user_session, $password) = @_;
1174 my $user_obj = $apputils->check_user_session($user_session);
1176 #grab the user with password
1177 $user_obj = $apputils->simple_scalar_request(
1179 "open-ils.cstore.direct.actor.user.retrieve",
1182 if($user_obj->passwd eq $password) {
1190 __PACKAGE__->register_method(
1191 method => "update_password",
1192 api_name => "open-ils.actor.user.password.update");
1194 __PACKAGE__->register_method(
1195 method => "update_password",
1196 api_name => "open-ils.actor.user.username.update");
1198 __PACKAGE__->register_method(
1199 method => "update_password",
1200 api_name => "open-ils.actor.user.email.update");
1202 sub update_password {
1203 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1207 my $session = $apputils->start_db_session();
1208 my $user_obj = $apputils->check_user_session($user_session);
1210 #fetch the in-database version so we get the latest xact_id
1211 $user_obj = $session->request(
1212 'open-ils.storage.direct.actor.user.retrieve', $user_obj->id)->gather(1);
1214 if($self->api_name =~ /password/o) {
1216 #make sure they know the current password
1217 if(!_verify_password($user_session, md5_hex($current_password))) {
1218 return OpenILS::Event->new('INCORRECT_PASSWORD');
1221 $logger->debug("update_password setting new password $new_value");
1222 $user_obj->passwd($new_value);
1224 } elsif($self->api_name =~ /username/o) {
1225 my $users = search_username(undef, undef, $new_value);
1226 if( $users and $users->[0] ) {
1227 return OpenILS::Event->new('USERNAME_EXISTS');
1229 $user_obj->usrname($new_value);
1231 } elsif($self->api_name =~ /email/o) {
1232 #warn "Updating email to $new_value\n";
1233 $user_obj->email($new_value);
1237 ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1238 return $evt if $evt;
1240 $apputils->commit_db_session($session);
1242 if($user_obj) { return 1; }
1247 __PACKAGE__->register_method(
1248 method => "update_passwd",
1250 api_name => "open-ils.actor.user.password.update");
1252 __PACKAGE__->register_method(
1253 method => "update_passwd",
1254 api_name => "open-ils.actor.user.username.update");
1256 __PACKAGE__->register_method(
1257 method => "update_passwd",
1258 api_name => "open-ils.actor.user.email.update");
1261 my( $self, $conn, $auth, $new_val, $orig_pw ) = @_;
1262 my $e = new_editor(xact=>1, authtoken=>$auth);
1263 return $e->die_event unless $e->checkauth;
1265 my $db_user = $e->retrieve_actor_user($e->requestor->id)
1266 or return $e->die_event;
1267 my $api = $self->api_name;
1269 if( $api =~ /password/o ) {
1271 # make sure the original password matches the in-database password
1272 return OpenILS::Event->new('INCORRECT_PASSWORD')
1273 if md5_hex($orig_pw) ne $db_user->passwd;
1274 $db_user->passwd($new_val);
1278 # if we don't clear the password, the user will be updated with
1279 # a hashed version of the hashed version of their password
1280 $db_user->clear_passwd;
1282 if( $api =~ /username/o ) {
1284 # make sure no one else has this username
1285 my $exist = $e->search_actor_user({usrname=>$new_val},{idlist=>1});
1286 return OpenILS::Event->new('USERNAME_EXISTS') if @$exist;
1287 $db_user->usrname($new_val);
1289 } elsif( $api =~ /email/o ) {
1290 $db_user->email($new_val);
1294 $e->update_actor_user($db_user) or return $e->die_event;
1302 __PACKAGE__->register_method(
1303 method => "check_user_perms",
1304 api_name => "open-ils.actor.user.perm.check",
1305 notes => <<" NOTES");
1306 Takes a login session, user id, an org id, and an array of perm type strings. For each
1307 perm type, if the user does *not* have the given permission it is added
1308 to a list which is returned from the method. If all permissions
1309 are allowed, an empty list is returned
1310 if the logged in user does not match 'user_id', then the logged in user must
1311 have VIEW_PERMISSION priveleges.
1314 sub check_user_perms {
1315 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1317 my( $staff, $evt ) = $apputils->checkses($login_session);
1318 return $evt if $evt;
1320 if($staff->id ne $user_id) {
1321 if( $evt = $apputils->check_perms(
1322 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1328 for my $perm (@$perm_types) {
1329 if($apputils->check_perms($user_id, $org_id, $perm)) {
1330 push @not_allowed, $perm;
1334 return \@not_allowed
1337 __PACKAGE__->register_method(
1338 method => "check_user_perms2",
1339 api_name => "open-ils.actor.user.perm.check.multi_org",
1341 Checks the permissions on a list of perms and orgs for a user
1342 @param authtoken The login session key
1343 @param user_id The id of the user to check
1344 @param orgs The array of org ids
1345 @param perms The array of permission names
1346 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1347 if the logged in user does not match 'user_id', then the logged in user must
1348 have VIEW_PERMISSION priveleges.
1351 sub check_user_perms2 {
1352 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1354 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1355 $authtoken, $user_id, 'VIEW_PERMISSION' );
1356 return $evt if $evt;
1359 for my $org (@$orgs) {
1360 for my $perm (@$perms) {
1361 if($apputils->check_perms($user_id, $org, $perm)) {
1362 push @not_allowed, [ $org, $perm ];
1367 return \@not_allowed
1371 __PACKAGE__->register_method(
1372 method => 'check_user_perms3',
1373 api_name => 'open-ils.actor.user.perm.highest_org',
1375 Returns the highest org unit id at which a user has a given permission
1376 If the requestor does not match the target user, the requestor must have
1377 'VIEW_PERMISSION' rights at the home org unit of the target user
1378 @param authtoken The login session key
1379 @param userid The id of the user in question
1380 @param perm The permission to check
1381 @return The org unit highest in the org tree within which the user has
1382 the requested permission
1385 sub check_user_perms3 {
1386 my( $self, $client, $authtoken, $userid, $perm ) = @_;
1388 my( $staff, $target, $org, $evt );
1390 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1391 $authtoken, $userid, 'VIEW_PERMISSION' );
1392 return $evt if $evt;
1394 my $tree = $self->get_org_tree();
1395 return _find_highest_perm_org( $perm, $userid, $target->ws_ou, $tree );
1399 sub _find_highest_perm_org {
1400 my ( $perm, $userid, $start_org, $org_tree ) = @_;
1401 my $org = $apputils->find_org($org_tree, $start_org );
1405 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1407 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1414 __PACKAGE__->register_method(
1415 method => 'check_user_work_perms',
1416 api_name => 'open-ils.actor.user.work_perm.highest_org_set',
1420 Returns a set of org units which represent the highest orgs in
1421 the org tree where the user has the requested permission. The
1422 purpose of this method is to return the smallest set of org units
1423 which represent the full expanse of the user's ability to perform
1424 the requested action. The user whose perms this method should
1425 check is implied by the authtoken. /,
1427 {desc => 'authtoken', type => 'string'},
1428 {desc => 'permission name', type => 'string'}
1430 return => {desc => 'An array of org IDs'}
1434 sub check_user_work_perms {
1435 my($self, $conn, $auth, $perm) = @_;
1436 my $e = new_editor(authtoken=>$auth);
1437 return $e->event unless $e->checkauth;
1439 my $work_orgs = _get_user_work_ou_ids($e, $e->requestor->id);
1441 $logger->debug("found work orgs @$work_orgs");
1444 my $org_tree = get_org_tree();
1445 my $org_types = get_org_types();
1447 # use the first work org to determine the highest depth at which
1448 # the user has the requested permission
1449 my $first_org = shift @$work_orgs;
1450 my $high_org_id = _find_highest_perm_org($perm, $e->requestor->id, $first_org, $org_tree);
1451 $logger->debug("found highest work org $high_org_id");
1454 return [] if $high_org_id == -1; # not allowed anywhere
1455 push(@allowed_orgs, $high_org_id);
1456 my $high_org = $apputils->find_org($org_tree, $high_org_id);
1458 my ($high_org_type) = grep { $_->id == $high_org->ou_type } @$org_types;
1459 return [$high_org_id] if $high_org_type->depth == 0;
1461 # now that we have the highest depth, find the org in the tree relative to
1462 # each work org at that depth.
1463 my ($org_type) = grep { $_->id == $high_org->ou_type } @$org_types;
1464 my $org_depth = $org_type->depth;
1465 for my $org (@$work_orgs) {
1466 $logger->debug("work org looking at $org");
1468 # retrieve sorted list of ancestors and descendants for this work_ou
1469 my $org_list = $e->json_query({
1472 transform => 'actor.org_unit_full_path',
1474 result_field => 'id',
1475 params => [$org_depth]
1482 # go through the list until we find the org at the correct depth
1484 push(@org_list, $_->{id}) for @$org_list;
1485 for my $sub_org (@org_list) {
1486 $logger->debug("work org looking at sub-org $sub_org");
1487 my $org_unit = $apputils->find_org($org_tree, $sub_org);
1488 my ($ou_type) = grep { $_->id == $org_unit->ou_type } @$org_types;
1489 if($ou_type->depth >= $org_depth) {
1490 push(@allowed_orgs, $sub_org);
1497 $de_dupe{$_} = 1 for @allowed_orgs;
1498 return [keys %de_dupe];
1503 __PACKAGE__->register_method(
1504 method => 'check_user_perms4',
1505 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1507 Returns the highest org unit id at which a user has a given permission
1508 If the requestor does not match the target user, the requestor must have
1509 'VIEW_PERMISSION' rights at the home org unit of the target user
1510 @param authtoken The login session key
1511 @param userid The id of the user in question
1512 @param perms An array of perm names to check
1513 @return An array of orgId's representing the org unit
1514 highest in the org tree within which the user has the requested permission
1515 The arrah of orgId's has matches the order of the perms array
1518 sub check_user_perms4 {
1519 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1521 my( $staff, $target, $org, $evt );
1523 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1524 $authtoken, $userid, 'VIEW_PERMISSION' );
1525 return $evt if $evt;
1528 return [] unless ref($perms);
1529 my $tree = $self->get_org_tree();
1531 for my $p (@$perms) {
1532 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1540 __PACKAGE__->register_method(
1541 method => "user_fines_summary",
1542 api_name => "open-ils.actor.user.fines.summary",
1543 notes => <<" NOTES");
1544 Returns a short summary of the users total open fines, excluding voided fines
1545 Params are login_session, user_id
1546 Returns a 'mous' object.
1549 sub user_fines_summary {
1550 my( $self, $client, $auth, $user_id ) = @_;
1551 my $e = new_editor(authtoken=>$auth);
1552 return $e->event unless $e->checkauth;
1553 my $user = $e->retrieve_actor_user($user_id)
1554 or return $e->event;
1556 if( $user_id ne $e->requestor->id ) {
1557 return $e->event unless
1558 $e->allowed('VIEW_USER_FINES_SUMMARY', $user->home_ou);
1561 # run this inside a transaction to prevent replication delay errors
1562 my $ses = $U->start_db_session();
1563 my $s = $ses->request(
1564 'open-ils.storage.money.open_user_summary.search', $user_id )->gather(1);
1565 $U->rollback_db_session($ses);
1572 __PACKAGE__->register_method(
1573 method => "user_transactions",
1574 api_name => "open-ils.actor.user.transactions",
1575 notes => <<" NOTES");
1576 Returns a list of open user transactions (mbts objects);
1577 Params are login_session, user_id
1578 Optional third parameter is the transactions type. defaults to all
1581 __PACKAGE__->register_method(
1582 method => "user_transactions",
1583 api_name => "open-ils.actor.user.transactions.have_charge",
1584 notes => <<" NOTES");
1585 Returns a list of all open user transactions (mbts objects) that have an initial charge
1586 Params are login_session, user_id
1587 Optional third parameter is the transactions type. defaults to all
1590 __PACKAGE__->register_method(
1591 method => "user_transactions",
1592 api_name => "open-ils.actor.user.transactions.have_balance",
1593 notes => <<" NOTES");
1594 Returns a list of all open user transactions (mbts objects) that have a balance
1595 Params are login_session, user_id
1596 Optional third parameter is the transactions type. defaults to all
1599 __PACKAGE__->register_method(
1600 method => "user_transactions",
1601 api_name => "open-ils.actor.user.transactions.fleshed",
1602 notes => <<" NOTES");
1603 Returns an object/hash of transaction, circ, title where transaction = an open
1604 user transactions (mbts objects), circ is the attached circluation, and title
1605 is the title the circ points to
1606 Params are login_session, user_id
1607 Optional third parameter is the transactions type. defaults to all
1610 __PACKAGE__->register_method(
1611 method => "user_transactions",
1612 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1613 notes => <<" NOTES");
1614 Returns an object/hash of transaction, circ, title where transaction = an open
1615 user transactions that has an initial charge (mbts objects), circ is the
1616 attached circluation, and title is the title the circ points to
1617 Params are login_session, user_id
1618 Optional third parameter is the transactions type. defaults to all
1621 __PACKAGE__->register_method(
1622 method => "user_transactions",
1623 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1624 notes => <<" NOTES");
1625 Returns an object/hash of transaction, circ, title where transaction = an open
1626 user transaction that has a balance (mbts objects), circ is the attached
1627 circluation, and title is the title the circ points to
1628 Params are login_session, user_id
1629 Optional third parameter is the transaction type. defaults to all
1632 __PACKAGE__->register_method(
1633 method => "user_transactions",
1634 api_name => "open-ils.actor.user.transactions.count",
1635 notes => <<" NOTES");
1636 Returns an object/hash of transaction, circ, title where transaction = an open
1637 user transactions (mbts objects), circ is the attached circluation, and title
1638 is the title the circ points to
1639 Params are login_session, user_id
1640 Optional third parameter is the transactions type. defaults to all
1643 __PACKAGE__->register_method(
1644 method => "user_transactions",
1645 api_name => "open-ils.actor.user.transactions.have_charge.count",
1646 notes => <<" NOTES");
1647 Returns an object/hash of transaction, circ, title where transaction = an open
1648 user transactions that has an initial charge (mbts objects), circ is the
1649 attached circluation, and title is the title the circ points to
1650 Params are login_session, user_id
1651 Optional third parameter is the transactions type. defaults to all
1654 __PACKAGE__->register_method(
1655 method => "user_transactions",
1656 api_name => "open-ils.actor.user.transactions.have_balance.count",
1657 notes => <<" NOTES");
1658 Returns an object/hash of transaction, circ, title where transaction = an open
1659 user transaction that has a balance (mbts objects), circ is the attached
1660 circluation, and title is the title the circ points to
1661 Params are login_session, user_id
1662 Optional third parameter is the transaction type. defaults to all
1665 __PACKAGE__->register_method(
1666 method => "user_transactions",
1667 api_name => "open-ils.actor.user.transactions.have_balance.total",
1668 notes => <<" NOTES");
1669 Returns an object/hash of transaction, circ, title where transaction = an open
1670 user transaction that has a balance (mbts objects), circ is the attached
1671 circluation, and title is the title the circ points to
1672 Params are login_session, user_id
1673 Optional third parameter is the transaction type. defaults to all
1678 sub user_transactions {
1679 my( $self, $client, $login_session, $user_id, $type ) = @_;
1681 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1682 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1683 return $evt if $evt;
1685 my $api = $self->api_name();
1689 if(defined($type)) { @xact = (xact_type => $type);
1691 } else { @xact = (); }
1694 ->method_lookup('open-ils.actor.user.transactions.history.still_open')
1695 ->run($login_session => $user_id => $type);
1697 if($api =~ /have_charge/o) {
1699 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1701 } elsif($api =~ /have_balance/o) {
1703 $trans = [ grep { int($_->balance_owed * 100) != 0 } @$trans ];
1706 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1710 if($api =~ /total/o) {
1712 for my $t (@$trans) {
1713 $total += $t->balance_owed;
1716 $logger->debug("Total balance owed by user $user_id: $total");
1720 if($api =~ /count/o) { return scalar @$trans; }
1721 if($api !~ /fleshed/o) { return $trans; }
1724 for my $t (@$trans) {
1726 if( $t->xact_type ne 'circulation' ) {
1727 push @resp, {transaction => $t};
1731 my $circ = $apputils->simple_scalar_request(
1733 "open-ils.cstore.direct.action.circulation.retrieve",
1738 my $title = $apputils->simple_scalar_request(
1740 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1741 $circ->target_copy );
1745 my $u = OpenILS::Utils::ModsParser->new();
1746 $u->start_mods_batch($title->marc());
1747 my $mods = $u->finish_mods_batch();
1748 $mods->doc_id($title->id) if $mods;
1750 push @resp, {transaction => $t, circ => $circ, record => $mods };
1758 __PACKAGE__->register_method(
1759 method => "user_transaction_retrieve",
1760 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1762 notes => <<" NOTES");
1763 Returns a fleshedtransaction record
1765 __PACKAGE__->register_method(
1766 method => "user_transaction_retrieve",
1767 api_name => "open-ils.actor.user.transaction.retrieve",
1769 notes => <<" NOTES");
1770 Returns a transaction record
1772 sub user_transaction_retrieve {
1773 my( $self, $client, $login_session, $bill_id ) = @_;
1775 # XXX I think I'm deprecated... make sure
1777 my $trans = $apputils->simple_scalar_request(
1779 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1783 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1784 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1785 return $evt if $evt;
1787 my $api = $self->api_name();
1788 if($api !~ /fleshed/o) { return $trans; }
1790 if( $trans->xact_type ne 'circulation' ) {
1791 $logger->debug("Returning non-circ transaction");
1792 return {transaction => $trans};
1795 my $circ = $apputils->simple_scalar_request(
1797 "open-ils..direct.action.circulation.retrieve",
1800 return {transaction => $trans} unless $circ;
1801 $logger->debug("Found the circ transaction");
1803 my $title = $apputils->simple_scalar_request(
1805 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1806 $circ->target_copy );
1808 return {transaction => $trans, circ => $circ } unless $title;
1809 $logger->debug("Found the circ title");
1813 my $u = OpenILS::Utils::ModsParser->new();
1814 $u->start_mods_batch($title->marc());
1815 $mods = $u->finish_mods_batch();
1817 if ($title->id == OILS_PRECAT_RECORD) {
1818 my $copy = $apputils->simple_scalar_request(
1820 "open-ils.cstore.direct.asset.copy.retrieve",
1821 $circ->target_copy );
1823 $mods = new Fieldmapper::metabib::virtual_record;
1824 $mods->doc_id(OILS_PRECAT_RECORD);
1825 $mods->title($copy->dummy_title);
1826 $mods->author($copy->dummy_author);
1830 $logger->debug("MODSized the circ title");
1832 return {transaction => $trans, circ => $circ, record => $mods };
1836 __PACKAGE__->register_method(
1837 method => "hold_request_count",
1838 api_name => "open-ils.actor.user.hold_requests.count",
1840 notes => <<" NOTES");
1841 Returns hold ready/total counts
1843 sub hold_request_count {
1844 my( $self, $client, $login_session, $userid ) = @_;
1846 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1847 $login_session, $userid, 'VIEW_HOLD' );
1848 return $evt if $evt;
1851 my $holds = $apputils->simple_scalar_request(
1853 "open-ils.cstore.direct.action.hold_request.search.atomic",
1856 fulfillment_time => {"=" => undef },
1857 cancel_time => undef,
1862 for my $h (@$holds) {
1863 next unless $h->capture_time and $h->current_copy;
1865 my $copy = $apputils->simple_scalar_request(
1867 "open-ils.cstore.direct.asset.copy.retrieve",
1871 if ($copy and $copy->status == 8) {
1876 return { total => scalar(@$holds), ready => scalar(@ready) };
1880 __PACKAGE__->register_method(
1881 method => "checkedout_count",
1882 api_name => "open-ils.actor.user.checked_out.count__",
1884 notes => <<" NOTES");
1885 Returns a transaction record
1889 sub checkedout_count {
1890 my( $self, $client, $login_session, $userid ) = @_;
1892 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1893 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1894 return $evt if $evt;
1896 my $circs = $apputils->simple_scalar_request(
1898 "open-ils.cstore.direct.action.circulation.search.atomic",
1899 { usr => $userid, stop_fines => undef }
1900 #{ usr => $userid, checkin_time => {"=" => undef } }
1903 my $parser = DateTime::Format::ISO8601->new;
1906 for my $c (@$circs) {
1907 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1908 my $due = $due_dt->epoch;
1910 if ($due < DateTime->today->epoch) {
1915 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1919 __PACKAGE__->register_method(
1920 method => "checked_out",
1921 api_name => "open-ils.actor.user.checked_out",
1924 Returns a structure of circulations objects sorted by
1925 out, overdue, lost, claims_returned, long_overdue.
1926 A list of IDs are returned of each type.
1927 lost, long_overdue, and claims_returned circ will not
1928 be "finished" (there is an outstanding balance or some
1929 other pending action on the circ).
1931 The .count method also includes a 'total' field which
1932 sums all "open" circs
1936 __PACKAGE__->register_method(
1937 method => "checked_out",
1938 api_name => "open-ils.actor.user.checked_out.count",
1940 signature => q/@see open-ils.actor.user.checked_out/
1944 my( $self, $conn, $auth, $userid ) = @_;
1946 my $e = new_editor(authtoken=>$auth);
1947 return $e->event unless $e->checkauth;
1949 if( $userid ne $e->requestor->id ) {
1950 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1953 my $count = $self->api_name =~ /count/;
1954 return _checked_out( $count, $e, $userid );
1958 my( $iscount, $e, $userid ) = @_;
1961 my $meth = 'open-ils.storage.actor.user.checked_out';
1962 $meth = "$meth.count" if $iscount;
1963 return $U->storagereq($meth, $userid);
1965 # XXX Old code - moved to storage
1966 #------------------------------------------------------------------------------
1967 #------------------------------------------------------------------------------
1968 my $circs = $e->search_action_circulation(
1969 { usr => $userid, checkin_time => undef });
1971 my $parser = DateTime::Format::ISO8601->new;
1973 # split the circs up into overdue and not-overdue circs
1975 for my $c (@$circs) {
1976 if( $c->due_date ) {
1977 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1978 my $due = $due_dt->epoch;
1979 if ($due < DateTime->today->epoch) {
1989 my( @open, @od, @lost, @cr, @lo );
1991 while (my $c = shift(@out)) {
1992 push( @open, $c->id ) if (!$c->stop_fines || $c->stop_fines eq 'MAXFINES' || $c->stop_fines eq 'RENEW');
1993 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1994 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1995 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1998 while (my $c = shift(@overdue)) {
1999 push( @od, $c->id ) if (!$c->stop_fines || $c->stop_fines eq 'MAXFINES' || $c->stop_fines eq 'RENEW');
2000 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
2001 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
2002 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
2007 total => @open + @od + @lost + @cr + @lo,
2008 out => scalar(@open),
2009 overdue => scalar(@od),
2010 lost => scalar(@lost),
2011 claims_returned => scalar(@cr),
2012 long_overdue => scalar(@lo)
2020 claims_returned => \@cr,
2021 long_overdue => \@lo
2026 sub _checked_out_WHAT {
2027 my( $iscount, $e, $userid ) = @_;
2029 my $circs = $e->search_action_circulation(
2030 { usr => $userid, stop_fines => undef });
2032 my $mcircs = $e->search_action_circulation(
2035 checkin_time => undef,
2036 xact_finish => undef,
2040 push( @$circs, @$mcircs );
2042 my $parser = DateTime::Format::ISO8601->new;
2044 # split the circs up into overdue and not-overdue circs
2046 for my $c (@$circs) {
2047 if( $c->due_date ) {
2048 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
2049 my $due = $due_dt->epoch;
2050 if ($due < DateTime->today->epoch) {
2051 push @overdue, $c->id;
2060 # grab all of the lost, claims-returned, and longoverdue circs
2061 #my $open = $e->search_action_circulation(
2062 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
2065 # these items have stop_fines, but no xact_finish, so money
2066 # is owed on them and they have not been checked in
2067 my $open = $e->search_action_circulation(
2070 stop_fines => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] },
2071 xact_finish => undef,
2072 checkin_time => undef,
2077 my( @lost, @cr, @lo );
2078 for my $c (@$open) {
2079 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
2080 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
2081 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
2087 total => @$circs + @lost + @cr + @lo,
2088 out => scalar(@out),
2089 overdue => scalar(@overdue),
2090 lost => scalar(@lost),
2091 claims_returned => scalar(@cr),
2092 long_overdue => scalar(@lo)
2098 overdue => \@overdue,
2100 claims_returned => \@cr,
2101 long_overdue => \@lo
2107 __PACKAGE__->register_method(
2108 method => "checked_in_with_fines",
2109 api_name => "open-ils.actor.user.checked_in_with_fines",
2111 signature => q/@see open-ils.actor.user.checked_out/
2113 sub checked_in_with_fines {
2114 my( $self, $conn, $auth, $userid ) = @_;
2116 my $e = new_editor(authtoken=>$auth);
2117 return $e->event unless $e->checkauth;
2119 if( $userid ne $e->requestor->id ) {
2120 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
2123 # money is owed on these items and they are checked in
2124 my $open = $e->search_action_circulation(
2127 xact_finish => undef,
2128 checkin_time => { "!=" => undef },
2133 my( @lost, @cr, @lo );
2134 for my $c (@$open) {
2135 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
2136 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
2137 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
2142 claims_returned => \@cr,
2143 long_overdue => \@lo
2155 __PACKAGE__->register_method(
2156 method => "user_transaction_history",
2157 api_name => "open-ils.actor.user.transactions.history",
2159 notes => <<" NOTES");
2160 Returns a list of billable transaction ids for a user, optionally by type
2162 __PACKAGE__->register_method(
2163 method => "user_transaction_history",
2164 api_name => "open-ils.actor.user.transactions.history.have_charge",
2166 notes => <<" NOTES");
2167 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
2169 __PACKAGE__->register_method(
2170 method => "user_transaction_history",
2171 api_name => "open-ils.actor.user.transactions.history.have_balance",
2173 notes => <<" NOTES");
2174 Returns a list of billable transaction ids for a user that have a balance, optionally by type
2176 __PACKAGE__->register_method(
2177 method => "user_transaction_history",
2178 api_name => "open-ils.actor.user.transactions.history.still_open",
2180 notes => <<" NOTES");
2181 Returns a list of billable transaction ids for a user that are not finished
2183 __PACKAGE__->register_method(
2184 method => "user_transaction_history",
2185 api_name => "open-ils.actor.user.transactions.history.have_bill",
2187 notes => <<" NOTES");
2188 Returns a list of billable transaction ids for a user that has billings
2194 sub _user_transaction_history {
2195 my( $self, $client, $login_session, $user_id, $type ) = @_;
2197 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
2198 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
2199 return $evt if $evt;
2201 my $api = $self->api_name();
2206 @xact = (xact_type => $type) if(defined($type));
2207 @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
2208 @charge = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
2210 $logger->debug("searching for transaction history: @xact : @balance, @charge");
2212 my $trans = $apputils->simple_scalar_request(
2214 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
2215 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
2217 return [ map { $_->id } @$trans ];
2221 =head SEE APPUTILS.PM
2226 for my $x (@xacts) {
2227 my $s = new Fieldmapper::money::billable_transaction_summary;
2230 $s->xact_start( $x->xact_start );
2231 $s->xact_finish( $x->xact_finish );
2235 for my $b (@{ $x->billings }) {
2236 next if ($U->is_true($b->voided));
2237 $to += ($b->amount * 100);
2238 $lb ||= $b->billing_ts;
2239 if ($b->billing_ts ge $lb) {
2240 $lb = $b->billing_ts;
2241 $s->last_billing_note($b->note);
2242 $s->last_billing_ts($b->billing_ts);
2243 $s->last_billing_type($b->billing_type);
2247 $s->total_owed( sprintf('%0.2f', $to / 100 ) );
2251 for my $p (@{ $x->payments }) {
2252 next if ($U->is_true($p->voided));
2253 $tp += ($p->amount * 100);
2254 $lp ||= $p->payment_ts;
2255 if ($p->payment_ts ge $lp) {
2256 $lp = $p->payment_ts;
2257 $s->last_payment_note($p->note);
2258 $s->last_payment_ts($p->payment_ts);
2259 $s->last_payment_type($p->payment_type);
2262 $s->total_paid( sprintf('%0.2f', $tp / 100 ) );
2264 $s->balance_owed( sprintf('%0.2f', ($to - $tp) / 100) );
2266 $s->xact_type( 'grocery' ) if ($x->grocery);
2267 $s->xact_type( 'circulation' ) if ($x->circulation);
2276 sub user_transaction_history {
2277 my( $self, $conn, $auth, $userid, $type ) = @_;
2279 # run inside of a transaction to prevent replication delays
2280 my $e = new_editor(xact=>1, authtoken=>$auth);
2281 return $e->die_event unless $e->checkauth;
2283 if( $e->requestor->id ne $userid ) {
2284 return $e->die_event
2285 unless $e->allowed('VIEW_USER_TRANSACTIONS');
2288 my $api = $self->api_name;
2289 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
2291 my @xacts = @{ $e->search_money_billable_transaction(
2292 [ { usr => $userid, @xact_finish },
2294 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
2295 order_by => { mbt => 'xact_start DESC' },
2303 #my @mbts = _make_mbts( @xacts );
2304 my @mbts = $U->make_mbts( @xacts );
2306 if(defined($type)) {
2307 @mbts = grep { $_->xact_type eq $type } @mbts;
2310 if($api =~ /have_balance/o) {
2311 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
2314 if($api =~ /have_charge/o) {
2315 @mbts = grep { defined($_->last_billing_ts) } @mbts;
2318 if($api =~ /have_bill/o) {
2319 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
2327 __PACKAGE__->register_method(
2328 method => "user_perms",
2329 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2331 notes => <<" NOTES");
2332 Returns a list of permissions
2335 my( $self, $client, $authtoken, $user ) = @_;
2337 my( $staff, $evt ) = $apputils->checkses($authtoken);
2338 return $evt if $evt;
2340 $user ||= $staff->id;
2342 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2346 return $apputils->simple_scalar_request(
2348 "open-ils.storage.permission.user_perms.atomic",
2352 __PACKAGE__->register_method(
2353 method => "retrieve_perms",
2354 api_name => "open-ils.actor.permissions.retrieve",
2355 notes => <<" NOTES");
2356 Returns a list of permissions
2358 sub retrieve_perms {
2359 my( $self, $client ) = @_;
2360 return $apputils->simple_scalar_request(
2362 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2363 { id => { '!=' => undef } }
2367 __PACKAGE__->register_method(
2368 method => "retrieve_groups",
2369 api_name => "open-ils.actor.groups.retrieve",
2370 notes => <<" NOTES");
2371 Returns a list of user groupss
2373 sub retrieve_groups {
2374 my( $self, $client ) = @_;
2375 return new_editor()->retrieve_all_permission_grp_tree();
2378 __PACKAGE__->register_method(
2379 method => "retrieve_org_address",
2380 api_name => "open-ils.actor.org_unit.address.retrieve",
2381 notes => <<' NOTES');
2382 Returns an org_unit address by ID
2383 @param An org_address ID
2385 sub retrieve_org_address {
2386 my( $self, $client, $id ) = @_;
2387 return $apputils->simple_scalar_request(
2389 "open-ils.cstore.direct.actor.org_address.retrieve",
2394 __PACKAGE__->register_method(
2395 method => "retrieve_groups_tree",
2396 api_name => "open-ils.actor.groups.tree.retrieve",
2397 notes => <<" NOTES");
2398 Returns a list of user groups
2400 sub retrieve_groups_tree {
2401 my( $self, $client ) = @_;
2402 return new_editor()->search_permission_grp_tree(
2407 flesh_fields => { pgt => ["children"] },
2408 order_by => { pgt => 'name'}
2415 # turns an org list into an org tree
2417 sub build_group_tree {
2419 my( $self, $grplist) = @_;
2421 return $grplist unless (
2422 ref($grplist) and @$grplist > 1 );
2424 my @list = sort { $a->name cmp $b->name } @$grplist;
2427 for my $grp (@list) {
2429 if ($grp and !defined($grp->parent)) {
2433 my ($parent) = grep { $_->id == $grp->parent} @list;
2435 $parent->children([]) unless defined($parent->children);
2436 push( @{$parent->children}, $grp );
2444 __PACKAGE__->register_method(
2445 method => "add_user_to_groups",
2446 api_name => "open-ils.actor.user.set_groups",
2447 notes => <<" NOTES");
2448 Adds a user to one or more permission groups
2451 sub add_user_to_groups {
2452 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2454 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2455 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2456 return $evt if $evt;
2458 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2459 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2460 return $evt if $evt;
2462 $apputils->simplereq(
2464 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2466 for my $group (@$groups) {
2467 my $link = Fieldmapper::permission::usr_grp_map->new;
2469 $link->usr($userid);
2471 my $id = $apputils->simplereq(
2473 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2479 __PACKAGE__->register_method(
2480 method => "get_user_perm_groups",
2481 api_name => "open-ils.actor.user.get_groups",
2482 notes => <<" NOTES");
2483 Retrieve a user's permission groups.
2487 sub get_user_perm_groups {
2488 my( $self, $client, $authtoken, $userid ) = @_;
2490 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2491 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2492 return $evt if $evt;
2494 return $apputils->simplereq(
2496 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2500 __PACKAGE__->register_method(
2501 method => "get_user_work_ous",
2502 api_name => "open-ils.actor.user.get_work_ous",
2503 notes => <<" NOTES");
2504 Retrieve a user's work org units.
2506 __PACKAGE__->register_method(
2507 method => "get_user_work_ous",
2508 api_name => "open-ils.actor.user.get_work_ous.ids",
2509 notes => <<" NOTES");
2510 Retrieve a user's work org units.
2514 sub get_user_work_ous {
2515 my( $self, $client, $auth, $userid ) = @_;
2516 my $e = new_editor(authtoken=>$auth);
2517 return $e->event unless $e->checkauth;
2518 $userid ||= $e->requestor->id;
2520 if($e->requestor->id != $userid) {
2521 my $user = $e->retrieve_actor_user($userid)
2522 or return $e->event;
2523 return $e->event unless $e->allowed('ASSIGN_WORK_ORG_UNIT', $user->home_ou);
2526 return $e->search_permission_usr_work_ou_map({usr => $userid})
2527 unless $self->api_name =~ /.ids$/;
2529 # client just wants a list of org IDs
2530 return _get_user_work_ou_ids($e, $userid);
2533 sub _get_user_work_ou_ids {
2534 my($e, $userid) = @_;
2535 my $work_orgs = $e->json_query({
2536 select => {puwoum => ['work_ou']},
2538 where => {usr => $e->requestor->id}});
2540 return [] unless @$work_orgs;
2542 push(@work_orgs, $_->{work_ou}) for @$work_orgs;
2548 __PACKAGE__->register_method (
2549 method => 'register_workstation',
2550 api_name => 'open-ils.actor.workstation.register.override',
2551 signature => q/@see open-ils.actor.workstation.register/);
2553 __PACKAGE__->register_method (
2554 method => 'register_workstation',
2555 api_name => 'open-ils.actor.workstation.register',
2557 Registers a new workstion in the system
2558 @param authtoken The login session key
2559 @param name The name of the workstation id
2560 @param owner The org unit that owns this workstation
2561 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2562 if the name is already in use.
2565 sub register_workstation {
2566 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2568 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2569 return $e->die_event unless $e->checkauth;
2570 return $e->die_event unless $e->allowed('REGISTER_WORKSTATION', $owner);
2571 my $existing = $e->search_actor_workstation({name => $name})->[0];
2575 if( $self->api_name =~ /override/o ) {
2576 # workstation with the given name exists.
2578 if($owner ne $existing->owning_lib) {
2579 # if necessary, update the owning_lib of the workstation
2581 $logger->info("changing owning lib of workstation ".$existing->id.
2582 " from ".$existing->owning_lib." to $owner");
2583 return $e->die_event unless
2584 $e->allowed('UPDATE_WORKSTATION', $existing->owning_lib);
2586 return $e->die_event unless $e->allowed('UPDATE_WORKSTATION', $owner);
2588 $existing->owning_lib($owner);
2589 return $e->die_event unless $e->update_actor_workstation($existing);
2595 "attempt to register an existing workstation. returning existing ID");
2598 return $existing->id;
2601 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2605 my $ws = Fieldmapper::actor::workstation->new;
2606 $ws->owning_lib($owner);
2608 $e->create_actor_workstation($ws) or return $e->die_event;
2610 return $ws->id; # note: editor sets the id on the new object for us
2613 __PACKAGE__->register_method (
2614 method => 'workstation_list',
2615 api_name => 'open-ils.actor.workstation.list',
2617 Returns a list of workstations registered at the given location
2618 @param authtoken The login session key
2619 @param ids A list of org_unit.id's for the workstation owners
2622 sub workstation_list {
2623 my( $self, $conn, $authtoken, @orgs ) = @_;
2625 my $e = new_editor(authtoken=>$authtoken);
2626 return $e->event unless $e->checkauth;
2631 unless $e->allowed('REGISTER_WORKSTATION', $o);
2632 $results{$o} = $e->search_actor_workstation({owning_lib=>$o});
2643 __PACKAGE__->register_method (
2644 method => 'fetch_patron_note',
2645 api_name => 'open-ils.actor.note.retrieve.all',
2647 Returns a list of notes for a given user
2648 Requestor must have VIEW_USER permission if pub==false and
2649 @param authtoken The login session key
2650 @param args Hash of params including
2651 patronid : the patron's id
2652 pub : true if retrieving only public notes
2656 sub fetch_patron_note {
2657 my( $self, $conn, $authtoken, $args ) = @_;
2658 my $patronid = $$args{patronid};
2660 my($reqr, $evt) = $U->checkses($authtoken);
2661 return $evt if $evt;
2664 ($patron, $evt) = $U->fetch_user($patronid);
2665 return $evt if $evt;
2668 if( $patronid ne $reqr->id ) {
2669 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2670 return $evt if $evt;
2672 return $U->cstorereq(
2673 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2674 { usr => $patronid, pub => 't' } );
2677 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2678 return $evt if $evt;
2680 return $U->cstorereq(
2681 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2684 __PACKAGE__->register_method (
2685 method => 'create_user_note',
2686 api_name => 'open-ils.actor.note.create',
2688 Creates a new note for the given user
2689 @param authtoken The login session key
2690 @param note The note object
2693 sub create_user_note {
2694 my( $self, $conn, $authtoken, $note ) = @_;
2695 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2696 return $e->die_event unless $e->checkauth;
2698 my $user = $e->retrieve_actor_user($note->usr)
2699 or return $e->die_event;
2701 return $e->die_event unless
2702 $e->allowed('UPDATE_USER',$user->home_ou);
2704 $note->creator($e->requestor->id);
2705 $e->create_actor_usr_note($note) or return $e->die_event;
2711 __PACKAGE__->register_method (
2712 method => 'delete_user_note',
2713 api_name => 'open-ils.actor.note.delete',
2715 Deletes a note for the given user
2716 @param authtoken The login session key
2717 @param noteid The note id
2720 sub delete_user_note {
2721 my( $self, $conn, $authtoken, $noteid ) = @_;
2723 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2724 return $e->die_event unless $e->checkauth;
2725 my $note = $e->retrieve_actor_usr_note($noteid)
2726 or return $e->die_event;
2727 my $user = $e->retrieve_actor_user($note->usr)
2728 or return $e->die_event;
2729 return $e->die_event unless
2730 $e->allowed('UPDATE_USER', $user->home_ou);
2732 $e->delete_actor_usr_note($note) or return $e->die_event;
2738 __PACKAGE__->register_method (
2739 method => 'update_user_note',
2740 api_name => 'open-ils.actor.note.update',
2742 @param authtoken The login session key
2743 @param note The note
2747 sub update_user_note {
2748 my( $self, $conn, $auth, $note ) = @_;
2749 my $e = new_editor(authtoken=>$auth, xact=>1);
2750 return $e->event unless $e->checkauth;
2751 my $patron = $e->retrieve_actor_user($note->usr)
2752 or return $e->event;
2753 return $e->event unless
2754 $e->allowed('UPDATE_USER', $patron->home_ou);
2755 $e->update_actor_user_note($note)
2756 or return $e->event;
2764 __PACKAGE__->register_method (
2765 method => 'create_closed_date',
2766 api_name => 'open-ils.actor.org_unit.closed_date.create',
2768 Creates a new closing entry for the given org_unit
2769 @param authtoken The login session key
2770 @param note The closed_date object
2773 sub create_closed_date {
2774 my( $self, $conn, $authtoken, $cd ) = @_;
2776 my( $user, $evt ) = $U->checkses($authtoken);
2777 return $evt if $evt;
2779 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2780 return $evt if $evt;
2782 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2784 my $id = $U->storagereq(
2785 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2786 return $U->DB_UPDATE_FAILED($cd) unless $id;
2791 __PACKAGE__->register_method (
2792 method => 'delete_closed_date',
2793 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2795 Deletes a closing entry for the given org_unit
2796 @param authtoken The login session key
2797 @param noteid The close_date id
2800 sub delete_closed_date {
2801 my( $self, $conn, $authtoken, $cd ) = @_;
2803 my( $user, $evt ) = $U->checkses($authtoken);
2804 return $evt if $evt;
2807 ($cd_obj, $evt) = fetch_closed_date($cd);
2808 return $evt if $evt;
2810 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2811 return $evt if $evt;
2813 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2815 my $stat = $U->storagereq(
2816 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2817 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2822 __PACKAGE__->register_method(
2823 method => 'usrname_exists',
2824 api_name => 'open-ils.actor.username.exists',
2826 Returns the user ID of the requested username if that username exists, returns null otherwise
2830 # XXX Make me retun undef if no user has the ID
2832 sub usrname_exists {
2833 my( $self, $conn, $auth, $usrname ) = @_;
2834 my $e = new_editor(authtoken=>$auth);
2835 return $e->event unless $e->checkauth;
2836 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2837 return $$a[0] if $a and @$a;
2841 __PACKAGE__->register_method(
2842 method => 'barcode_exists',
2843 api_name => 'open-ils.actor.barcode.exists',
2845 Returns the user ID for the requested barcode if that barcode exists, returns null otherwise
2849 sub barcode_exists {
2850 my( $self, $conn, $auth, $barcode ) = @_;
2851 my $e = new_editor(authtoken=>$auth);
2852 return $e->event unless $e->checkauth;
2853 my $card = $e->search_actor_card({barcode => $barcode});
2854 return undef unless @$card;
2855 return $card->[0]->usr;
2859 __PACKAGE__->register_method(
2860 method => 'retrieve_net_levels',
2861 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2864 sub retrieve_net_levels {
2865 my( $self, $conn, $auth ) = @_;
2866 my $e = new_editor(authtoken=>$auth);
2867 return $e->event unless $e->checkauth;
2868 return $e->retrieve_all_config_net_access_level();
2872 __PACKAGE__->register_method(
2873 method => 'fetch_org_by_shortname',
2874 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2876 sub fetch_org_by_shortname {
2877 my( $self, $conn, $sname ) = @_;
2878 my $e = new_editor();
2879 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2880 return $e->event unless $org;
2885 __PACKAGE__->register_method(
2886 method => 'session_home_lib',
2887 api_name => 'open-ils.actor.session.home_lib',
2890 sub session_home_lib {
2891 my( $self, $conn, $auth ) = @_;
2892 my $e = new_editor(authtoken=>$auth);
2893 return undef unless $e->checkauth;
2894 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2895 return $org->shortname;
2898 __PACKAGE__->register_method(
2899 method => 'session_safe_token',
2900 api_name => 'open-ils.actor.session.safe_token',
2902 Returns a hashed session ID that is safe for export to the world.
2903 This safe token will expire after 1 hour of non-use.
2904 @param auth Active authentication token
2908 sub session_safe_token {
2909 my( $self, $conn, $auth ) = @_;
2910 my $e = new_editor(authtoken=>$auth);
2911 return undef unless $e->checkauth;
2913 my $safe_token = md5_hex($auth);
2915 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2917 # Add more like the following if needed...
2919 "safe-token-home_lib-shortname-$safe_token",
2920 $e->retrieve_actor_org_unit(
2921 $e->requestor->home_ou
2930 __PACKAGE__->register_method(
2931 method => 'safe_token_home_lib',
2932 api_name => 'open-ils.actor.safe_token.home_lib.shortname',
2934 Returns the home library shortname from the session
2935 asscociated with a safe token from generated by
2936 open-ils.actor.session.safe_token.
2937 @param safe_token Active safe token
2941 sub safe_token_home_lib {
2942 my( $self, $conn, $safe_token ) = @_;
2944 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2945 return $cache->get_cache( 'safe-token-home_lib-shortname-'. $safe_token );
2950 __PACKAGE__->register_method(
2951 method => 'slim_tree',
2952 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2955 my $tree = new_editor()->search_actor_org_unit(
2957 {"parent_ou" => undef },
2960 flesh_fields => { aou => ['children'] },
2961 order_by => { aou => 'name'},
2962 select => { aou => ["id","shortname", "name"]},
2967 return trim_tree($tree);
2973 return undef unless $tree;
2975 code => $tree->shortname,
2976 name => $tree->name,
2978 if( $tree->children and @{$tree->children} ) {
2979 $htree->{children} = [];
2980 for my $c (@{$tree->children}) {
2981 push( @{$htree->{children}}, trim_tree($c) );
2989 __PACKAGE__->register_method(
2990 method => "update_penalties",
2991 api_name => "open-ils.actor.user.penalties.update");
2992 sub update_penalties {
2993 my( $self, $conn, $auth, $userid ) = @_;
2994 my $e = new_editor(authtoken=>$auth);
2995 return $e->event unless $e->checkauth;
2996 $U->update_patron_penalties(
2998 patronid => $userid,
3005 __PACKAGE__->register_method(
3006 method => "user_retrieve_fleshed_by_id",
3007 api_name => "open-ils.actor.user.fleshed.retrieve",);
3009 sub user_retrieve_fleshed_by_id {
3010 my( $self, $client, $auth, $user_id, $fields ) = @_;
3011 my $e = new_editor(authtoken => $auth);
3012 return $e->event unless $e->checkauth;
3014 if( $e->requestor->id != $user_id ) {
3015 return $e->event unless $e->allowed('VIEW_USER');
3021 "standing_penalties",
3025 "stat_cat_entries" ];
3026 return new_flesh_user($user_id, $fields, $e);
3030 sub new_flesh_user {
3033 my $fields = shift || [];
3034 my $e = shift || new_editor(xact=>1);
3036 my $user = $e->retrieve_actor_user(
3041 "flesh_fields" => { "au" => $fields }
3044 ) or return $e->event;
3047 if( grep { $_ eq 'addresses' } @$fields ) {
3049 $user->addresses([]) unless @{$user->addresses};
3051 if( ref $user->billing_address ) {
3052 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
3053 push( @{$user->addresses}, $user->billing_address );
3057 if( ref $user->mailing_address ) {
3058 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
3059 push( @{$user->addresses}, $user->mailing_address );
3065 $user->clear_passwd();
3072 __PACKAGE__->register_method(
3073 method => "user_retrieve_parts",
3074 api_name => "open-ils.actor.user.retrieve.parts",);
3076 sub user_retrieve_parts {
3077 my( $self, $client, $auth, $user_id, $fields ) = @_;
3078 my $e = new_editor(authtoken => $auth);
3079 return $e->event unless $e->checkauth;
3080 if( $e->requestor->id != $user_id ) {
3081 return $e->event unless $e->allowed('VIEW_USER');
3084 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3085 push(@resp, $user->$_()) for(@$fields);
3091 __PACKAGE__->register_method(
3092 method => 'user_opt_in_enabled',
3093 api_name => 'open-ils.actor.user.org_unit_opt_in.enabled',
3095 @return 1 if user opt-in is globally enabled, 0 otherwise.
3098 sub user_opt_in_enabled {
3099 my($self, $conn) = @_;
3100 my $sc = OpenSRF::Utils::SettingsClient->new;
3101 return 1 if lc($sc->config_value(share => user => 'opt_in')) eq 'true';
3106 __PACKAGE__->register_method(
3107 method => 'user_opt_in_at_org',
3108 api_name => 'open-ils.actor.user.org_unit_opt_in.check',
3110 @param $auth The auth token
3111 @param user_id The ID of the user to test
3112 @return 1 if the user has opted in at the specified org,
3113 event on error, and 0 otherwise. /);
3114 sub user_opt_in_at_org {
3115 my($self, $conn, $auth, $user_id) = @_;
3117 # see if we even need to enforce the opt-in value
3118 return 1 unless $self->user_opt_in_enabled;
3120 my $e = new_editor(authtoken => $auth);
3121 return $e->event unless $e->checkauth;
3122 my $org_id = $e->requestor->ws_ou;
3124 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3125 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3127 # user is automatically opted-in at the home org
3128 return 1 if $user->home_ou eq $org_id;
3130 my $vals = $e->search_actor_usr_org_unit_opt_in(
3131 {org_unit=>$org_id, usr=>$user_id},{idlist=>1});
3137 __PACKAGE__->register_method(
3138 method => 'create_user_opt_in_at_org',
3139 api_name => 'open-ils.actor.user.org_unit_opt_in.create',
3141 @param $auth The auth token
3142 @param user_id The ID of the user to test
3143 @return The ID of the newly created object, event on error./);
3145 sub create_user_opt_in_at_org {
3146 my($self, $conn, $auth, $user_id) = @_;
3148 my $e = new_editor(authtoken => $auth, xact=>1);
3149 return $e->die_event unless $e->checkauth;
3150 my $org_id = $e->requestor->ws_ou;
3152 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3153 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3155 my $opt_in = Fieldmapper::actor::usr_org_unit_opt_in->new;
3157 $opt_in->org_unit($org_id);
3158 $opt_in->usr($user_id);
3159 $opt_in->staff($e->requestor->id);
3160 $opt_in->opt_in_ts('now');
3161 $opt_in->opt_in_ws($e->requestor->wsid);
3163 $opt_in = $e->create_actor_usr_org_unit_opt_in($opt_in)
3164 or return $e->die_event;