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;
31 use OpenILS::Application::Actor::UserGroups;
32 use OpenILS::Application::Actor::Friends;
33 use OpenILS::Application::Actor::Stage;
35 use OpenILS::Utils::CStoreEditor qw/:funcs/;
36 use OpenILS::Utils::Penalty;
37 use List::Util qw/max/;
40 OpenILS::Application::Actor::Container->initialize();
41 OpenILS::Application::Actor::UserGroups->initialize();
42 OpenILS::Application::Actor::ClosedDates->initialize();
45 my $apputils = "OpenILS::Application::AppUtils";
48 sub _d { warn "Patron:\n" . Dumper(shift()); }
51 my $set_user_settings;
55 #__PACKAGE__->register_method(
56 # method => "allowed_test",
57 # api_name => "open-ils.actor.allowed_test",
60 # my($self, $conn, $auth, $orgid, $permcode) = @_;
61 # my $e = new_editor(authtoken => $auth);
62 # return $e->die_event unless $e->checkauth;
66 # permcode => $permcode,
67 # result => $e->allowed($permcode, $orgid)
71 __PACKAGE__->register_method(
72 method => "update_user_setting",
73 api_name => "open-ils.actor.patron.settings.update",
75 sub update_user_setting {
76 my($self, $conn, $auth, $user_id, $settings) = @_;
77 my $e = new_editor(xact => 1, authtoken => $auth);
78 return $e->die_event unless $e->checkauth;
80 $user_id = $e->requestor->id unless defined $user_id;
82 unless($e->requestor->id == $user_id) {
83 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
84 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
87 for my $name (keys %$settings) {
88 my $val = $$settings{$name};
89 my $set = $e->search_actor_user_setting({usr => $user_id, name => $name})->[0];
92 $val = OpenSRF::Utils::JSON->perl2JSON($val);
95 $e->update_actor_user_setting($set) or return $e->die_event;
97 $set = Fieldmapper::actor::user_setting->new;
101 $e->create_actor_user_setting($set) or return $e->die_event;
104 $e->delete_actor_user_setting($set) or return $e->die_event;
113 __PACKAGE__->register_method(
114 method => "set_ou_settings",
115 api_name => "open-ils.actor.org_unit.settings.update",
118 Updates the value for a given org unit setting. The permission to update
119 an org unit setting is either the UPDATE_ORG_UNIT_SETTING_ALL, or a specific
120 permission specified in the update_perm column of the
121 config.org_unit_setting_type table's row corresponding to the setting being
124 {desc => 'authtoken', type => 'string'},
125 {desc => 'org unit id', type => 'number'},
126 {desc => q/Hash of setting name-value pairs/, type => 'hash'},
128 return => {desc => '1 on success, Event on error'}
132 sub set_ou_settings {
133 my( $self, $client, $auth, $org_id, $settings ) = @_;
135 my $e = new_editor(authtoken => $auth, xact => 1);
136 return $e->die_event unless $e->checkauth;
138 my $all_allowed = $e->allowed("UPDATE_ORG_UNIT_SETTING_ALL", $org_id);
140 for my $name (keys %$settings) {
141 my $val = $$settings{$name};
143 my $type = $e->retrieve_config_org_unit_setting_type([
145 {flesh => 1, flesh_fields => {'coust' => ['update_perm']}}
146 ]) or return $e->die_event;
147 my $set = $e->search_actor_org_unit_setting({org_unit => $org_id, name => $name})->[0];
149 # If there is no relevant permission, the default assumption will
150 # be, "no, the caller cannot change that value."
151 return $e->die_event unless ($all_allowed ||
152 ($type->update_perm && $e->allowed($type->update_perm->code, $org_id)));
155 $val = OpenSRF::Utils::JSON->perl2JSON($val);
158 $e->update_actor_org_unit_setting($set) or return $e->die_event;
160 $set = Fieldmapper::actor::org_unit_setting->new;
161 $set->org_unit($org_id);
164 $e->create_actor_org_unit_setting($set) or return $e->die_event;
167 $e->delete_actor_org_unit_setting($set) or return $e->die_event;
175 __PACKAGE__->register_method(
176 method => "user_settings",
177 api_name => "open-ils.actor.patron.settings.retrieve",
180 my( $self, $client, $auth, $user_id, $setting ) = @_;
182 my $e = new_editor(authtoken => $auth);
183 return $e->event unless $e->checkauth;
184 $user_id = $e->requestor->id unless defined $user_id;
186 my $patron = $e->retrieve_actor_user($user_id) or return $e->event;
187 if($e->requestor->id != $user_id) {
188 return $e->event unless $e->allowed('VIEW_USER', $patron->home_ou);
192 my($e, $user_id, $setting) = @_;
193 my $val = $e->search_actor_user_setting({usr => $user_id, name => $setting})->[0];
194 return undef unless $val; # XXX this should really return undef, but needs testing
195 return OpenSRF::Utils::JSON->JSON2perl($val->value);
199 if(ref $setting eq 'ARRAY') {
201 $settings{$_} = get_setting($e, $user_id, $_) for @$setting;
204 return get_setting($e, $user_id, $setting);
207 my $s = $e->search_actor_user_setting({usr => $user_id});
208 return { map { ( $_->name => OpenSRF::Utils::JSON->JSON2perl($_->value) ) } @$s };
214 __PACKAGE__->register_method(
215 method => "ranged_ou_settings",
216 api_name => "open-ils.actor.org_unit_setting.values.ranged.retrieve",
219 Retrieves all org unit settings for the given org_id, up to whatever limit
220 is implied for retrieving OU settings by the authenticated users' permissions./,
222 {desc => 'authtoken', type => 'string'},
223 {desc => 'org unit id', type => 'number'},
225 return => {desc => 'A hashref of "ranged" settings'}
228 sub ranged_ou_settings {
229 my( $self, $client, $auth, $org_id ) = @_;
231 my $e = new_editor(authtoken => $auth);
232 return $e->event unless $e->checkauth;
235 my $org_list = $U->get_org_ancestors($org_id);
236 my $settings = $e->search_actor_org_unit_setting({org_unit => $org_list});
237 $org_list = [ reverse @$org_list ];
239 # start at the context org and capture the setting value
240 # without clobbering settings we've already captured
241 for my $this_org_id (@$org_list) {
243 my @sets = grep { $_->org_unit == $this_org_id } @$settings;
245 for my $set (@sets) {
246 my $type = $e->retrieve_config_org_unit_setting_type([
248 {flesh => 1, flesh_fields => {coust => ['view_perm']}}
251 # If there is no relevant permission, the default assumption will
252 # be, "yes, the caller can have that value."
253 if ($type && $type->view_perm) {
254 next if not $e->allowed($type->view_perm->code, $org_id);
257 $ranged_settings{$set->name} = OpenSRF::Utils::JSON->JSON2perl($set->value)
258 unless defined $ranged_settings{$set->name};
262 return \%ranged_settings;
267 __PACKAGE__->register_method(
268 api_name => 'open-ils.actor.ou_setting.ancestor_default',
269 method => 'ou_ancestor_setting',
271 desc => q/Get an org unit setting value as seen from your org unit. IF AND ONLY IF
272 you provide an authentication token, this method will make sure that the given
273 user has permission to view that setting, if there is a permission associated
276 {desc => 'org unit id', type => 'number'},
277 {desc => 'setting name', type => 'string'},
278 {desc => '(optional) authtoken', type => 'string'},
280 return => {desc => 'A value for the org unit setting, or undef'}
284 # ------------------------------------------------------------------
285 # Attempts to find the org setting value for a given org. if not
286 # found at the requested org, searches up the org tree until it
287 # finds a parent that has the requested setting.
288 # when found, returns { org => $id, value => $value }
289 # otherwise, returns NULL
290 # ------------------------------------------------------------------
291 sub ou_ancestor_setting {
292 my( $self, $client, $orgid, $name, $auth ) = @_;
293 return $U->ou_ancestor_setting($orgid, $name, undef, $auth);
296 __PACKAGE__->register_method(
297 api_name => 'open-ils.actor.ou_setting.ancestor_default.batch',
298 method => 'ou_ancestor_setting_batch',
300 desc => q/Get org unit setting name => value pairs as seen from the specified org unit.
301 IF AND ONLY IF you provide an authentication token, this method will make sure
302 that the given user has permission to view that setting, if there is a
303 permission associated with the setting./,
305 {desc => 'org unit id', type => 'number'},
306 {desc => 'setting name list', type => 'array'},
307 {desc => '(optional) authtoken', type => 'string'},
309 return => {desc => 'A hash with name => value pairs for the org unit settings'}
312 sub ou_ancestor_setting_batch {
313 my( $self, $client, $orgid, $name_list, $auth ) = @_;
315 $values{$_} = $U->ou_ancestor_setting($orgid, $_, undef, $auth) for @$name_list;
323 __PACKAGE__->register_method(
324 method => "update_patron",
325 api_name => "open-ils.actor.patron.update",);
328 my( $self, $client, $user_session, $patron ) = @_;
330 my $session = $apputils->start_db_session();
333 $logger->info("Creating new patron...") if $patron->isnew;
334 $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
336 my( $user_obj, $evt ) = $U->checkses($user_session);
339 $evt = check_group_perm($session, $user_obj, $patron);
343 # $new_patron is the patron in progress. $patron is the original patron
344 # passed in with the method. new_patron will change as the components
345 # of patron are added/updated.
349 # unflesh the real items on the patron
350 $patron->card( $patron->card->id ) if(ref($patron->card));
351 $patron->billing_address( $patron->billing_address->id )
352 if(ref($patron->billing_address));
353 $patron->mailing_address( $patron->mailing_address->id )
354 if(ref($patron->mailing_address));
356 # create/update the patron first so we can use his id
357 if($patron->isnew()) {
358 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
360 } else { $new_patron = $patron; }
362 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
365 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
368 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
371 # re-update the patron if anything has happened to him during this process
372 if($new_patron->ischanged()) {
373 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
377 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
380 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
383 $apputils->commit_db_session($session);
385 $evt = apply_invalid_addr_penalty($patron);
388 my $tses = OpenSRF::AppSession->create('open-ils.trigger');
390 $tses->request('open-ils.trigger.event.autocreate', 'au.create', $new_patron, $new_patron->home_ou);
392 $tses->request('open-ils.trigger.event.autocreate', 'au.update', $new_patron, $new_patron->home_ou);
395 return flesh_user($new_patron->id(), new_editor(requestor => $user_obj));
398 sub apply_invalid_addr_penalty {
400 my $e = new_editor(xact => 1);
402 # grab the invalid address penalty if set
403 my $penalties = OpenILS::Utils::Penalty->retrieve_usr_penalties($e, $patron->id, $patron->home_ou);
405 my ($addr_penalty) = grep
406 { $_->standing_penalty->name eq 'INVALID_PATRON_ADDRESS' } @$penalties;
408 # do we enforce invalid address penalty
409 my $enforce = $U->ou_ancestor_setting_value(
410 $patron->home_ou, 'circ.patron_invalid_address_apply_penalty') || 0;
412 my $addrs = $e->search_actor_user_address(
413 {usr => $patron->id, valid => 'f', id => {'>' => 0}}, {idlist => 1});
414 my $addr_count = scalar(@$addrs);
416 if($addr_count == 0 and $addr_penalty) {
418 # regardless of any settings, remove the penalty when the user has no invalid addresses
419 $e->delete_actor_user_standing_penalty($addr_penalty) or return $e->die_event;
422 } elsif($enforce and $addr_count > 0 and !$addr_penalty) {
424 my $ptype = $e->retrieve_config_standing_penalty(29) or return $e->die_event;
425 my $depth = $ptype->org_depth;
426 my $ctx_org = $U->org_unit_ancestor_at_depth($patron->home_ou, $depth) if defined $depth;
427 $ctx_org = $patron->home_ou unless defined $ctx_org;
429 my $penalty = Fieldmapper::actor::user_standing_penalty->new;
430 $penalty->usr($patron->id);
431 $penalty->org_unit($ctx_org);
432 $penalty->standing_penalty(OILS_PENALTY_INVALID_PATRON_ADDRESS);
434 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
448 return new_flesh_user($id, [
451 "standing_penalties",
455 "stat_cat_entries" ], $e );
463 # clone and clear stuff that would break the database
467 my $new_patron = $patron->clone;
469 $new_patron->clear_billing_address();
470 $new_patron->clear_mailing_address();
471 $new_patron->clear_addresses();
472 $new_patron->clear_card();
473 $new_patron->clear_cards();
474 $new_patron->clear_id();
475 $new_patron->clear_isnew();
476 $new_patron->clear_ischanged();
477 $new_patron->clear_isdeleted();
478 $new_patron->clear_stat_cat_entries();
479 $new_patron->clear_permissions();
480 $new_patron->clear_standing_penalties();
490 my $user_obj = shift;
492 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
493 return (undef, $evt) if $evt;
495 my $ex = $session->request(
496 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
498 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
501 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
503 my $id = $session->request(
504 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
505 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
507 $logger->info("Successfully created new user [$id] in DB");
509 return ( $session->request(
510 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
514 sub check_group_perm {
515 my( $session, $requestor, $patron ) = @_;
518 # first let's see if the requestor has
519 # priveleges to update this user in any way
520 if( ! $patron->isnew ) {
521 my $p = $session->request(
522 'open-ils.storage.direct.actor.user.retrieve', $patron->id )->gather(1);
524 # If we are the requestor (trying to update our own account)
525 # and we are not trying to change our profile, we're good
526 if( $p->id == $requestor->id and
527 $p->profile == $patron->profile ) {
532 $evt = group_perm_failed($session, $requestor, $p);
536 # They are allowed to edit this patron.. can they put the
537 # patron into the group requested?
538 $evt = group_perm_failed($session, $requestor, $patron);
544 sub group_perm_failed {
545 my( $session, $requestor, $patron ) = @_;
549 my $grpid = $patron->profile;
553 $logger->debug("user update looking for group perm for group $grpid");
554 $grp = $session->request(
555 'open-ils.storage.direct.permission.grp_tree.retrieve', $grpid )->gather(1);
556 return OpenILS::Event->new('PERMISSION_GRP_TREE_NOT_FOUND') unless $grp;
558 } while( !($perm = $grp->application_perm) and ($grpid = $grp->parent) );
560 $logger->info("user update checking perm $perm on user ".
561 $requestor->id." for update/create on user username=".$patron->usrname);
563 my $evt = $U->check_perms($requestor->id, $patron->home_ou, $perm);
571 my( $session, $patron, $user_obj, $noperm) = @_;
573 $logger->info("Updating patron ".$patron->id." in DB");
578 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
579 return (undef, $evt) if $evt;
582 # update the password by itself to avoid the password protection magic
583 if( $patron->passwd ) {
584 my $s = $session->request(
585 'open-ils.storage.direct.actor.user.remote_update',
586 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
587 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
588 $patron->clear_passwd;
591 if(!$patron->ident_type) {
592 $patron->clear_ident_type;
593 $patron->clear_ident_value;
596 $evt = verify_last_xact($session, $patron);
597 return (undef, $evt) if $evt;
599 my $stat = $session->request(
600 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
601 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
606 sub verify_last_xact {
607 my( $session, $patron ) = @_;
608 return undef unless $patron->id and $patron->id > 0;
609 my $p = $session->request(
610 'open-ils.storage.direct.actor.user.retrieve', $patron->id)->gather(1);
611 my $xact = $p->last_xact_id;
612 return undef unless $xact;
613 $logger->info("user xact = $xact, saving with xact " . $patron->last_xact_id);
614 return OpenILS::Event->new('XACT_COLLISION')
615 if $xact != $patron->last_xact_id;
620 sub _check_dup_ident {
621 my( $session, $patron ) = @_;
623 return undef unless $patron->ident_value;
626 ident_type => $patron->ident_type,
627 ident_value => $patron->ident_value,
630 $logger->debug("patron update searching for dup ident values: " .
631 $patron->ident_type . ':' . $patron->ident_value);
633 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
635 my $dups = $session->request(
636 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
639 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
646 sub _add_update_addresses {
650 my $new_patron = shift;
654 my $current_id; # id of the address before creation
656 for my $address (@{$patron->addresses()}) {
658 next unless ref $address;
659 $current_id = $address->id();
661 if( $patron->billing_address() and
662 $patron->billing_address() == $current_id ) {
663 $logger->info("setting billing addr to $current_id");
664 $new_patron->billing_address($address->id());
665 $new_patron->ischanged(1);
668 if( $patron->mailing_address() and
669 $patron->mailing_address() == $current_id ) {
670 $new_patron->mailing_address($address->id());
671 $logger->info("setting mailing addr to $current_id");
672 $new_patron->ischanged(1);
676 if($address->isnew()) {
678 $address->usr($new_patron->id());
680 ($address, $evt) = _add_address($session,$address);
681 return (undef, $evt) if $evt;
683 # we need to get the new id
684 if( $patron->billing_address() and
685 $patron->billing_address() == $current_id ) {
686 $new_patron->billing_address($address->id());
687 $logger->info("setting billing addr to $current_id");
688 $new_patron->ischanged(1);
691 if( $patron->mailing_address() and
692 $patron->mailing_address() == $current_id ) {
693 $new_patron->mailing_address($address->id());
694 $logger->info("setting mailing addr to $current_id");
695 $new_patron->ischanged(1);
698 } elsif($address->ischanged() ) {
700 ($address, $evt) = _update_address($session, $address);
701 return (undef, $evt) if $evt;
703 } elsif($address->isdeleted() ) {
705 if( $address->id() == $new_patron->mailing_address() ) {
706 $new_patron->clear_mailing_address();
707 ($new_patron, $evt) = _update_patron($session, $new_patron);
708 return (undef, $evt) if $evt;
711 if( $address->id() == $new_patron->billing_address() ) {
712 $new_patron->clear_billing_address();
713 ($new_patron, $evt) = _update_patron($session, $new_patron);
714 return (undef, $evt) if $evt;
717 $evt = _delete_address($session, $address);
718 return (undef, $evt) if $evt;
722 return ( $new_patron, undef );
726 # adds an address to the db and returns the address with new id
728 my($session, $address) = @_;
729 $address->clear_id();
731 $logger->info("Creating new address at street ".$address->street1);
733 # put the address into the database
734 my $id = $session->request(
735 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
736 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
739 return ($address, undef);
743 sub _update_address {
744 my( $session, $address ) = @_;
746 $logger->info("Updating address ".$address->id." in the DB");
748 my $stat = $session->request(
749 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
751 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
752 return ($address, undef);
757 sub _add_update_cards {
761 my $new_patron = shift;
765 my $virtual_id; #id of the card before creation
766 for my $card (@{$patron->cards()}) {
768 $card->usr($new_patron->id());
770 if(ref($card) and $card->isnew()) {
772 $virtual_id = $card->id();
773 ( $card, $evt ) = _add_card($session,$card);
774 return (undef, $evt) if $evt;
776 #if(ref($patron->card)) { $patron->card($patron->card->id); }
777 if($patron->card() == $virtual_id) {
778 $new_patron->card($card->id());
779 $new_patron->ischanged(1);
782 } elsif( ref($card) and $card->ischanged() ) {
783 $evt = _update_card($session, $card);
784 return (undef, $evt) if $evt;
788 return ( $new_patron, undef );
792 # adds an card to the db and returns the card with new id
794 my( $session, $card ) = @_;
797 $logger->info("Adding new patron card ".$card->barcode);
799 my $id = $session->request(
800 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
801 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
802 $logger->info("Successfully created patron card $id");
805 return ( $card, undef );
809 # returns event on error. returns undef otherwise
811 my( $session, $card ) = @_;
812 $logger->info("Updating patron card ".$card->id);
814 my $stat = $session->request(
815 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
816 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
823 # returns event on error. returns undef otherwise
824 sub _delete_address {
825 my( $session, $address ) = @_;
827 $logger->info("Deleting address ".$address->id." from DB");
829 my $stat = $session->request(
830 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
832 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
838 sub _add_survey_responses {
839 my ($session, $patron, $new_patron) = @_;
841 $logger->info( "Updating survey responses for patron ".$new_patron->id );
843 my $responses = $patron->survey_responses;
847 $_->usr($new_patron->id) for (@$responses);
849 my $evt = $U->simplereq( "open-ils.circ",
850 "open-ils.circ.survey.submit.user_id", $responses );
852 return (undef, $evt) if defined($U->event_code($evt));
856 return ( $new_patron, undef );
860 sub _create_stat_maps {
862 my($session, $user_session, $patron, $new_patron) = @_;
864 my $maps = $patron->stat_cat_entries();
866 for my $map (@$maps) {
868 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
870 if ($map->isdeleted()) {
871 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
873 } elsif ($map->isnew()) {
874 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
879 $map->target_usr($new_patron->id);
882 $logger->info("Updating stat entry with method $method and map $map");
884 my $stat = $session->request($method, $map)->gather(1);
885 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
889 return ($new_patron, undef);
892 sub _create_perm_maps {
894 my($session, $user_session, $patron, $new_patron) = @_;
896 my $maps = $patron->permissions;
898 for my $map (@$maps) {
900 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
901 if ($map->isdeleted()) {
902 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
903 } elsif ($map->isnew()) {
904 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
909 $map->usr($new_patron->id);
911 #warn( "Updating permissions with method $method and session $user_session and map $map" );
912 $logger->info( "Updating permissions with method $method and map $map" );
914 my $stat = $session->request($method, $map)->gather(1);
915 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
919 return ($new_patron, undef);
923 __PACKAGE__->register_method(
924 method => "set_user_work_ous",
925 api_name => "open-ils.actor.user.work_ous.update",
928 sub set_user_work_ous {
934 my( $requestor, $evt ) = $apputils->checksesperm( $ses, 'ASSIGN_WORK_ORG_UNIT' );
937 my $session = $apputils->start_db_session();
939 for my $map (@$maps) {
941 my $method = "open-ils.storage.direct.permission.usr_work_ou_map.update";
942 if ($map->isdeleted()) {
943 $method = "open-ils.storage.direct.permission.usr_work_ou_map.delete";
944 } elsif ($map->isnew()) {
945 $method = "open-ils.storage.direct.permission.usr_work_ou_map.create";
949 #warn( "Updating permissions with method $method and session $ses and map $map" );
950 $logger->info( "Updating work_ou map with method $method and map $map" );
952 my $stat = $session->request($method, $map)->gather(1);
953 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
957 $apputils->commit_db_session($session);
959 return scalar(@$maps);
963 __PACKAGE__->register_method(
964 method => "set_user_perms",
965 api_name => "open-ils.actor.user.permissions.update",
974 my $session = $apputils->start_db_session();
976 my( $user_obj, $evt ) = $U->checkses($ses);
979 my $perms = $session->request('open-ils.storage.permission.user_perms.atomic', $user_obj->id)->gather(1);
982 $all = 1 if ($U->is_true($user_obj->super_user()));
983 $all = 1 unless ($U->check_perms($user_obj->id, $user_obj->home_ou, 'EVERYTHING'));
985 for my $map (@$maps) {
987 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
988 if ($map->isdeleted()) {
989 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
990 } elsif ($map->isnew()) {
991 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
995 next if (!$all and !grep { $_->perm eq $map->perm and $U->is_true($_->grantable) and $_->depth <= $map->depth } @$perms);
996 #warn( "Updating permissions with method $method and session $ses and map $map" );
997 $logger->info( "Updating permissions with method $method and map $map" );
999 my $stat = $session->request($method, $map)->gather(1);
1000 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
1004 $apputils->commit_db_session($session);
1006 return scalar(@$maps);
1010 __PACKAGE__->register_method(
1011 method => "user_retrieve_by_barcode",
1013 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
1015 sub user_retrieve_by_barcode {
1016 my($self, $client, $user_session, $barcode) = @_;
1018 $logger->debug("Searching for user with barcode $barcode");
1019 my ($user_obj, $evt) = $apputils->checkses($user_session);
1020 return $evt if $evt;
1022 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
1024 "open-ils.cstore.direct.actor.card.search.atomic",
1025 { barcode => $barcode }
1028 if(!$card || !$card->[0]) {
1029 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
1033 my $user = flesh_user($card->usr(), new_editor(requestor => $user_obj));
1035 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
1036 return $evt if $evt;
1038 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
1045 __PACKAGE__->register_method(
1046 method => "get_user_by_id",
1048 api_name => "open-ils.actor.user.retrieve",);
1050 sub get_user_by_id {
1051 my ($self, $client, $auth, $id) = @_;
1052 my $e = new_editor(authtoken=>$auth);
1053 return $e->event unless $e->checkauth;
1054 my $user = $e->retrieve_actor_user($id)
1055 or return $e->event;
1056 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
1062 __PACKAGE__->register_method(
1063 method => "get_org_types",
1064 api_name => "open-ils.actor.org_types.retrieve",);
1067 return $U->get_org_types();
1072 __PACKAGE__->register_method(
1073 method => "get_user_ident_types",
1074 api_name => "open-ils.actor.user.ident_types.retrieve",
1077 sub get_user_ident_types {
1078 return $ident_types if $ident_types;
1079 return $ident_types =
1080 new_editor()->retrieve_all_config_identification_type();
1086 __PACKAGE__->register_method(
1087 method => "get_org_unit",
1088 api_name => "open-ils.actor.org_unit.retrieve",
1092 my( $self, $client, $user_session, $org_id ) = @_;
1093 my $e = new_editor(authtoken => $user_session);
1095 return $e->event unless $e->checkauth;
1096 $org_id = $e->requestor->ws_ou;
1098 my $o = $e->retrieve_actor_org_unit($org_id)
1099 or return $e->event;
1103 __PACKAGE__->register_method(
1104 method => "search_org_unit",
1105 api_name => "open-ils.actor.org_unit_list.search",
1108 sub search_org_unit {
1110 my( $self, $client, $field, $value ) = @_;
1112 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
1114 "open-ils.cstore.direct.actor.org_unit.search.atomic",
1115 { $field => $value } );
1121 # build the org tree
1123 __PACKAGE__->register_method(
1124 method => "get_org_tree",
1125 api_name => "open-ils.actor.org_tree.retrieve",
1127 note => "Returns the entire org tree structure",
1133 return $U->get_org_tree($client->session->session_locale);
1137 __PACKAGE__->register_method(
1138 method => "get_org_descendants",
1139 api_name => "open-ils.actor.org_tree.descendants.retrieve"
1142 # depth is optional. org_unit is the id
1143 sub get_org_descendants {
1144 my( $self, $client, $org_unit, $depth ) = @_;
1146 if(ref $org_unit eq 'ARRAY') {
1149 for my $i (0..scalar(@$org_unit)-1) {
1150 my $list = $U->simple_scalar_request(
1152 "open-ils.storage.actor.org_unit.descendants.atomic",
1153 $org_unit->[$i], $depth->[$i] );
1154 push(@trees, $U->build_org_tree($list));
1159 my $orglist = $apputils->simple_scalar_request(
1161 "open-ils.storage.actor.org_unit.descendants.atomic",
1162 $org_unit, $depth );
1163 return $U->build_org_tree($orglist);
1168 __PACKAGE__->register_method(
1169 method => "get_org_ancestors",
1170 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
1173 # depth is optional. org_unit is the id
1174 sub get_org_ancestors {
1175 my( $self, $client, $org_unit, $depth ) = @_;
1176 my $orglist = $apputils->simple_scalar_request(
1178 "open-ils.storage.actor.org_unit.ancestors.atomic",
1179 $org_unit, $depth );
1180 return $U->build_org_tree($orglist);
1184 __PACKAGE__->register_method(
1185 method => "get_standings",
1186 api_name => "open-ils.actor.standings.retrieve"
1191 return $user_standings if $user_standings;
1192 return $user_standings =
1193 $apputils->simple_scalar_request(
1195 "open-ils.cstore.direct.config.standing.search.atomic",
1196 { id => { "!=" => undef } }
1202 __PACKAGE__->register_method(
1203 method => "get_my_org_path",
1204 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1207 sub get_my_org_path {
1208 my( $self, $client, $auth, $org_id ) = @_;
1209 my $e = new_editor(authtoken=>$auth);
1210 return $e->event unless $e->checkauth;
1211 $org_id = $e->requestor->ws_ou unless defined $org_id;
1213 return $apputils->simple_scalar_request(
1215 "open-ils.storage.actor.org_unit.full_path.atomic",
1220 __PACKAGE__->register_method(
1221 method => "patron_adv_search",
1222 api_name => "open-ils.actor.patron.search.advanced" );
1223 sub patron_adv_search {
1224 my( $self, $client, $auth, $search_hash,
1225 $search_limit, $search_sort, $include_inactive, $search_depth ) = @_;
1227 my $e = new_editor(authtoken=>$auth);
1228 return $e->event unless $e->checkauth;
1229 return $e->event unless $e->allowed('VIEW_USER');
1230 return $U->storagereq(
1231 "open-ils.storage.actor.user.crazy_search", $search_hash,
1232 $search_limit, $search_sort, $include_inactive, $e->requestor->ws_ou, $search_depth);
1236 __PACKAGE__->register_method(
1237 method => "update_passwd",
1239 api_name => "open-ils.actor.user.password.update");
1241 __PACKAGE__->register_method(
1242 method => "update_passwd",
1243 api_name => "open-ils.actor.user.username.update");
1245 __PACKAGE__->register_method(
1246 method => "update_passwd",
1247 api_name => "open-ils.actor.user.email.update");
1250 my( $self, $conn, $auth, $new_val, $orig_pw ) = @_;
1251 my $e = new_editor(xact=>1, authtoken=>$auth);
1252 return $e->die_event unless $e->checkauth;
1254 my $db_user = $e->retrieve_actor_user($e->requestor->id)
1255 or return $e->die_event;
1256 my $api = $self->api_name;
1258 if( $api =~ /password/o ) {
1260 # make sure the original password matches the in-database password
1261 return OpenILS::Event->new('INCORRECT_PASSWORD')
1262 if md5_hex($orig_pw) ne $db_user->passwd;
1263 $db_user->passwd($new_val);
1267 # if we don't clear the password, the user will be updated with
1268 # a hashed version of the hashed version of their password
1269 $db_user->clear_passwd;
1271 if( $api =~ /username/o ) {
1273 # make sure no one else has this username
1274 my $exist = $e->search_actor_user({usrname=>$new_val},{idlist=>1});
1275 return OpenILS::Event->new('USERNAME_EXISTS') if @$exist;
1276 $db_user->usrname($new_val);
1278 } elsif( $api =~ /email/o ) {
1279 $db_user->email($new_val);
1283 $e->update_actor_user($db_user) or return $e->die_event;
1291 __PACKAGE__->register_method(
1292 method => "check_user_perms",
1293 api_name => "open-ils.actor.user.perm.check",
1294 notes => <<" NOTES");
1295 Takes a login session, user id, an org id, and an array of perm type strings. For each
1296 perm type, if the user does *not* have the given permission it is added
1297 to a list which is returned from the method. If all permissions
1298 are allowed, an empty list is returned
1299 if the logged in user does not match 'user_id', then the logged in user must
1300 have VIEW_PERMISSION priveleges.
1303 sub check_user_perms {
1304 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1306 my( $staff, $evt ) = $apputils->checkses($login_session);
1307 return $evt if $evt;
1309 if($staff->id ne $user_id) {
1310 if( $evt = $apputils->check_perms(
1311 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1317 for my $perm (@$perm_types) {
1318 if($apputils->check_perms($user_id, $org_id, $perm)) {
1319 push @not_allowed, $perm;
1323 return \@not_allowed
1326 __PACKAGE__->register_method(
1327 method => "check_user_perms2",
1328 api_name => "open-ils.actor.user.perm.check.multi_org",
1330 Checks the permissions on a list of perms and orgs for a user
1331 @param authtoken The login session key
1332 @param user_id The id of the user to check
1333 @param orgs The array of org ids
1334 @param perms The array of permission names
1335 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1336 if the logged in user does not match 'user_id', then the logged in user must
1337 have VIEW_PERMISSION priveleges.
1340 sub check_user_perms2 {
1341 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1343 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1344 $authtoken, $user_id, 'VIEW_PERMISSION' );
1345 return $evt if $evt;
1348 for my $org (@$orgs) {
1349 for my $perm (@$perms) {
1350 if($apputils->check_perms($user_id, $org, $perm)) {
1351 push @not_allowed, [ $org, $perm ];
1356 return \@not_allowed
1360 __PACKAGE__->register_method(
1361 method => 'check_user_perms3',
1362 api_name => 'open-ils.actor.user.perm.highest_org',
1364 Returns the highest org unit id at which a user has a given permission
1365 If the requestor does not match the target user, the requestor must have
1366 'VIEW_PERMISSION' rights at the home org unit of the target user
1367 @param authtoken The login session key
1368 @param userid The id of the user in question
1369 @param perm The permission to check
1370 @return The org unit highest in the org tree within which the user has
1371 the requested permission
1374 sub check_user_perms3 {
1375 my($self, $client, $authtoken, $user_id, $perm) = @_;
1376 my $e = new_editor(authtoken=>$authtoken);
1377 return $e->event unless $e->checkauth;
1379 my $tree = $U->get_org_tree();
1381 unless($e->requestor->id == $user_id) {
1382 my $user = $e->retrieve_actor_user($user_id)
1383 or return $e->event;
1384 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1385 return $U->find_highest_perm_org($perm, $user_id, $user->home_ou, $tree );
1388 return $U->find_highest_perm_org($perm, $user_id, $e->requestor->ws_ou, $tree);
1391 __PACKAGE__->register_method(
1392 method => 'user_has_work_perm_at',
1393 api_name => 'open-ils.actor.user.has_work_perm_at',
1397 Returns a set of org unit IDs which represent the highest orgs in
1398 the org tree where the user has the requested permission. The
1399 purpose of this method is to return the smallest set of org units
1400 which represent the full expanse of the user's ability to perform
1401 the requested action. The user whose perms this method should
1402 check is implied by the authtoken. /,
1404 {desc => 'authtoken', type => 'string'},
1405 {desc => 'permission name', type => 'string'},
1406 {desc => q/user id, optional. If present, check perms for
1407 this user instead of the logged in user/, type => 'number'},
1409 return => {desc => 'An array of org IDs'}
1413 sub user_has_work_perm_at {
1414 my($self, $conn, $auth, $perm, $user_id) = @_;
1415 my $e = new_editor(authtoken=>$auth);
1416 return $e->event unless $e->checkauth;
1417 if(defined $user_id) {
1418 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1419 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1421 return $U->user_has_work_perm_at($e, $perm, undef, $user_id);
1424 __PACKAGE__->register_method(
1425 method => 'user_has_work_perm_at_batch',
1426 api_name => 'open-ils.actor.user.has_work_perm_at.batch',
1430 sub user_has_work_perm_at_batch {
1431 my($self, $conn, $auth, $perms, $user_id) = @_;
1432 my $e = new_editor(authtoken=>$auth);
1433 return $e->event unless $e->checkauth;
1434 if(defined $user_id) {
1435 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1436 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1439 $map->{$_} = $U->user_has_work_perm_at($e, $_) for @$perms;
1445 __PACKAGE__->register_method(
1446 method => 'check_user_perms4',
1447 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1449 Returns the highest org unit id at which a user has a given permission
1450 If the requestor does not match the target user, the requestor must have
1451 'VIEW_PERMISSION' rights at the home org unit of the target user
1452 @param authtoken The login session key
1453 @param userid The id of the user in question
1454 @param perms An array of perm names to check
1455 @return An array of orgId's representing the org unit
1456 highest in the org tree within which the user has the requested permission
1457 The arrah of orgId's has matches the order of the perms array
1460 sub check_user_perms4 {
1461 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1463 my( $staff, $target, $org, $evt );
1465 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1466 $authtoken, $userid, 'VIEW_PERMISSION' );
1467 return $evt if $evt;
1470 return [] unless ref($perms);
1471 my $tree = $U->get_org_tree();
1473 for my $p (@$perms) {
1474 push( @arr, $U->find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1482 __PACKAGE__->register_method(
1483 method => "user_fines_summary",
1484 api_name => "open-ils.actor.user.fines.summary",
1486 notes => <<" NOTES");
1487 Returns a short summary of the users total open fines, excluding voided fines
1488 Params are login_session, user_id
1489 Returns a 'mous' object.
1492 sub user_fines_summary {
1493 my( $self, $client, $auth, $user_id ) = @_;
1494 my $e = new_editor(authtoken=>$auth);
1495 return $e->event unless $e->checkauth;
1496 my $user = $e->retrieve_actor_user($user_id)
1497 or return $e->event;
1499 if( $user_id ne $e->requestor->id ) {
1500 return $e->event unless
1501 $e->allowed('VIEW_USER_FINES_SUMMARY', $user->home_ou);
1504 # run this inside a transaction to prevent replication delay errors
1505 my $ses = $U->start_db_session();
1506 my $s = $ses->request(
1507 'open-ils.storage.money.open_user_summary.search', $user_id )->gather(1);
1508 $U->rollback_db_session($ses);
1515 __PACKAGE__->register_method(
1516 method => "user_transactions",
1517 api_name => "open-ils.actor.user.transactions",
1518 notes => <<" NOTES");
1519 Returns a list of open user transactions (mbts objects);
1520 Params are login_session, user_id
1521 Optional third parameter is the transactions type. defaults to all
1524 __PACKAGE__->register_method(
1525 method => "user_transactions",
1526 api_name => "open-ils.actor.user.transactions.have_charge",
1527 notes => <<" NOTES");
1528 Returns a list of all open user transactions (mbts objects) that have an initial charge
1529 Params are login_session, user_id
1530 Optional third parameter is the transactions type. defaults to all
1533 __PACKAGE__->register_method(
1534 method => "user_transactions",
1535 api_name => "open-ils.actor.user.transactions.have_balance",
1537 notes => <<" NOTES");
1538 Returns a list of all open user transactions (mbts objects) that have a balance
1539 Params are login_session, user_id
1540 Optional third parameter is the transactions type. defaults to all
1543 __PACKAGE__->register_method(
1544 method => "user_transactions",
1545 api_name => "open-ils.actor.user.transactions.fleshed",
1546 notes => <<" NOTES");
1547 Returns an object/hash of transaction, circ, title where transaction = an open
1548 user transactions (mbts objects), circ is the attached circluation, and title
1549 is the title the circ points to
1550 Params are login_session, user_id
1551 Optional third parameter is the transactions type. defaults to all
1554 __PACKAGE__->register_method(
1555 method => "user_transactions",
1556 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1557 notes => <<" NOTES");
1558 Returns an object/hash of transaction, circ, title where transaction = an open
1559 user transactions that has an initial charge (mbts objects), circ is the
1560 attached circluation, and title is the title the circ points to
1561 Params are login_session, user_id
1562 Optional third parameter is the transactions type. defaults to all
1565 __PACKAGE__->register_method(
1566 method => "user_transactions",
1567 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1569 notes => <<" NOTES");
1570 Returns an object/hash of transaction, circ, title where transaction = an open
1571 user transaction that has a balance (mbts objects), circ is the attached
1572 circluation, and title is the title the circ points to
1573 Params are login_session, user_id
1574 Optional third parameter is the transaction type. defaults to all
1577 __PACKAGE__->register_method(
1578 method => "user_transactions",
1579 api_name => "open-ils.actor.user.transactions.count",
1580 notes => <<" NOTES");
1581 Returns an object/hash of transaction, circ, title where transaction = an open
1582 user transactions (mbts objects), circ is the attached circluation, and title
1583 is the title the circ points to
1584 Params are login_session, user_id
1585 Optional third parameter is the transactions type. defaults to all
1588 __PACKAGE__->register_method(
1589 method => "user_transactions",
1590 api_name => "open-ils.actor.user.transactions.have_charge.count",
1591 notes => <<" NOTES");
1592 Returns an object/hash of transaction, circ, title where transaction = an open
1593 user transactions that has an initial charge (mbts objects), circ is the
1594 attached circluation, and title is the title the circ points to
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.have_balance.count",
1603 notes => <<" NOTES");
1604 Returns an object/hash of transaction, circ, title where transaction = an open
1605 user transaction that has a balance (mbts objects), circ is the attached
1606 circluation, and title is the title the circ points to
1607 Params are login_session, user_id
1608 Optional third parameter is the transaction type. defaults to all
1611 __PACKAGE__->register_method(
1612 method => "user_transactions",
1613 api_name => "open-ils.actor.user.transactions.have_balance.total",
1615 notes => <<" NOTES");
1616 Returns an object/hash of transaction, circ, title where transaction = an open
1617 user transaction that has a balance (mbts objects), circ is the attached
1618 circluation, and title is the title the circ points to
1619 Params are login_session, user_id
1620 Optional third parameter is the transaction type. defaults to all
1625 sub user_transactions {
1626 my( $self, $client, $login_session, $user_id, $type ) = @_;
1628 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1629 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1630 return $evt if $evt;
1632 my $api = $self->api_name();
1636 if(defined($type)) { @xact = (xact_type => $type);
1638 } else { @xact = (); }
1641 ->method_lookup('open-ils.actor.user.transactions.history.still_open')
1642 ->run($login_session => $user_id => $type);
1645 if($api =~ /have_charge/o) {
1647 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1649 } elsif($api =~ /have_balance/o) {
1651 $trans = [ grep { int($_->balance_owed * 100) != 0 } @$trans ];
1654 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1658 if($api =~ /total/o) {
1660 for my $t (@$trans) {
1661 $total += $t->balance_owed;
1664 $logger->debug("Total balance owed by user $user_id: $total");
1668 if($api =~ /count/o) { return scalar @$trans; }
1669 if($api !~ /fleshed/o) { return $trans; }
1672 for my $t (@$trans) {
1674 if( $t->xact_type ne 'circulation' ) {
1675 push @resp, {transaction => $t};
1679 my $circ = $apputils->simple_scalar_request(
1681 "open-ils.cstore.direct.action.circulation.retrieve",
1686 my $title = $apputils->simple_scalar_request(
1688 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1689 $circ->target_copy );
1693 my $u = OpenILS::Utils::ModsParser->new();
1694 $u->start_mods_batch($title->marc());
1695 my $mods = $u->finish_mods_batch();
1696 $mods->doc_id($title->id) if $mods;
1698 push @resp, {transaction => $t, circ => $circ, record => $mods };
1706 __PACKAGE__->register_method(
1707 method => "user_transaction_retrieve",
1708 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1710 notes => <<" NOTES");
1711 Returns a fleshed transaction record
1713 __PACKAGE__->register_method(
1714 method => "user_transaction_retrieve",
1715 api_name => "open-ils.actor.user.transaction.retrieve",
1717 notes => <<" NOTES");
1718 Returns a transaction record
1720 sub user_transaction_retrieve {
1721 my( $self, $client, $login_session, $bill_id ) = @_;
1723 # I think I'm deprecated... make sure. phasefx says, "No, I'll use you :)
1725 my $trans = $apputils->simple_scalar_request(
1727 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1731 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1732 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1733 return $evt if $evt;
1735 my $api = $self->api_name();
1736 if($api !~ /fleshed/o) { return $trans; }
1738 if( $trans->xact_type ne 'circulation' ) {
1739 $logger->debug("Returning non-circ transaction");
1740 return {transaction => $trans};
1743 my $circ = $apputils->simple_scalar_request(
1745 "open-ils.cstore.direct.action.circulation.retrieve",
1748 return {transaction => $trans} unless $circ;
1749 $logger->debug("Found the circ transaction");
1751 my $title = $apputils->simple_scalar_request(
1753 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1754 $circ->target_copy );
1756 return {transaction => $trans, circ => $circ } unless $title;
1757 $logger->debug("Found the circ title");
1760 my $copy = $apputils->simple_scalar_request(
1762 "open-ils.cstore.direct.asset.copy.retrieve",
1763 $circ->target_copy );
1766 my $u = OpenILS::Utils::ModsParser->new();
1767 $u->start_mods_batch($title->marc());
1768 $mods = $u->finish_mods_batch();
1770 if ($title->id == OILS_PRECAT_RECORD) {
1771 $mods = new Fieldmapper::metabib::virtual_record;
1772 $mods->doc_id(OILS_PRECAT_RECORD);
1773 $mods->title($copy->dummy_title);
1774 $mods->author($copy->dummy_author);
1778 $logger->debug("MODSized the circ title");
1780 return {transaction => $trans, circ => $circ, record => $mods, copy => $copy };
1784 __PACKAGE__->register_method(
1785 method => "hold_request_count",
1786 api_name => "open-ils.actor.user.hold_requests.count",
1789 notes => <<" NOTES");
1790 Returns hold ready/total counts
1792 sub hold_request_count {
1793 my( $self, $client, $login_session, $userid ) = @_;
1795 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1796 $login_session, $userid, 'VIEW_HOLD' );
1797 return $evt if $evt;
1800 my $holds = $apputils->simple_scalar_request(
1802 "open-ils.cstore.direct.action.hold_request.search.atomic",
1805 fulfillment_time => {"=" => undef },
1806 cancel_time => undef,
1811 for my $h (@$holds) {
1812 next unless $h->capture_time and $h->current_copy;
1814 my $copy = $apputils->simple_scalar_request(
1816 "open-ils.cstore.direct.asset.copy.retrieve",
1820 if ($copy and $copy->status == 8) {
1825 return { total => scalar(@$holds), ready => scalar(@ready) };
1829 __PACKAGE__->register_method(
1830 method => "checkedout_count",
1831 api_name => "open-ils.actor.user.checked_out.count__",
1833 notes => <<" NOTES");
1834 Returns a transaction record
1838 sub checkedout_count {
1839 my( $self, $client, $login_session, $userid ) = @_;
1841 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1842 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1843 return $evt if $evt;
1845 my $circs = $apputils->simple_scalar_request(
1847 "open-ils.cstore.direct.action.circulation.search.atomic",
1848 { usr => $userid, stop_fines => undef }
1849 #{ usr => $userid, checkin_time => {"=" => undef } }
1852 my $parser = DateTime::Format::ISO8601->new;
1855 for my $c (@$circs) {
1856 my $due_dt = $parser->parse_datetime( cleanse_ISO8601( $c->due_date ) );
1857 my $due = $due_dt->epoch;
1859 if ($due < DateTime->today->epoch) {
1864 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1868 __PACKAGE__->register_method(
1869 method => "checked_out",
1870 api_name => "open-ils.actor.user.checked_out",
1874 Returns a structure of circulations objects sorted by
1875 out, overdue, lost, claims_returned, long_overdue.
1876 A list of IDs are returned of each type.
1877 lost, long_overdue, and claims_returned circ will not
1878 be "finished" (there is an outstanding balance or some
1879 other pending action on the circ).
1881 The .count method also includes a 'total' field which
1882 sums all "open" circs
1886 __PACKAGE__->register_method(
1887 method => "checked_out",
1888 api_name => "open-ils.actor.user.checked_out.count",
1891 signature => q/@see open-ils.actor.user.checked_out/
1895 my( $self, $conn, $auth, $userid ) = @_;
1897 my $e = new_editor(authtoken=>$auth);
1898 return $e->event unless $e->checkauth;
1900 if( $userid ne $e->requestor->id ) {
1901 my $user = $e->retrieve_actor_user($userid) or return $e->event;
1902 unless($e->allowed('VIEW_CIRCULATIONS', $user->home_ou)) {
1904 # see if there is a friend link allowing circ.view perms
1905 my $allowed = OpenILS::Application::Actor::Friends->friend_perm_allowed(
1906 $e, $userid, $e->requestor->id, 'circ.view');
1907 return $e->event unless $allowed;
1911 my $count = $self->api_name =~ /count/;
1912 return _checked_out( $count, $e, $userid );
1916 my( $iscount, $e, $userid ) = @_;
1917 my $meth = 'open-ils.storage.actor.user.checked_out';
1918 $meth = "$meth.count" if $iscount;
1919 return $U->storagereq($meth, $userid);
1923 sub _checked_out_WHAT {
1924 my( $iscount, $e, $userid ) = @_;
1926 my $circs = $e->search_action_circulation(
1927 { usr => $userid, stop_fines => undef });
1929 my $mcircs = $e->search_action_circulation(
1932 checkin_time => undef,
1933 xact_finish => undef,
1937 push( @$circs, @$mcircs );
1939 my $parser = DateTime::Format::ISO8601->new;
1941 # split the circs up into overdue and not-overdue circs
1943 for my $c (@$circs) {
1944 if( $c->due_date ) {
1945 my $due_dt = $parser->parse_datetime( cleanse_ISO8601( $c->due_date ) );
1946 my $due = $due_dt->epoch;
1947 if ($due < DateTime->today->epoch) {
1948 push @overdue, $c->id;
1957 # grab all of the lost, claims-returned, and longoverdue circs
1958 #my $open = $e->search_action_circulation(
1959 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1962 # these items have stop_fines, but no xact_finish, so money
1963 # is owed on them and they have not been checked in
1964 my $open = $e->search_action_circulation(
1967 stop_fines => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] },
1968 xact_finish => undef,
1969 checkin_time => undef,
1974 my( @lost, @cr, @lo );
1975 for my $c (@$open) {
1976 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1977 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1978 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1984 total => @$circs + @lost + @cr + @lo,
1985 out => scalar(@out),
1986 overdue => scalar(@overdue),
1987 lost => scalar(@lost),
1988 claims_returned => scalar(@cr),
1989 long_overdue => scalar(@lo)
1995 overdue => \@overdue,
1997 claims_returned => \@cr,
1998 long_overdue => \@lo
2004 __PACKAGE__->register_method(
2005 method => "checked_in_with_fines",
2006 api_name => "open-ils.actor.user.checked_in_with_fines",
2009 signature => q/@see open-ils.actor.user.checked_out/
2011 sub checked_in_with_fines {
2012 my( $self, $conn, $auth, $userid ) = @_;
2014 my $e = new_editor(authtoken=>$auth);
2015 return $e->event unless $e->checkauth;
2017 if( $userid ne $e->requestor->id ) {
2018 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
2021 # money is owed on these items and they are checked in
2022 my $open = $e->search_action_circulation(
2025 xact_finish => undef,
2026 checkin_time => { "!=" => undef },
2031 my( @lost, @cr, @lo );
2032 for my $c (@$open) {
2033 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
2034 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
2035 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
2040 claims_returned => \@cr,
2041 long_overdue => \@lo
2053 __PACKAGE__->register_method(
2054 method => "user_transaction_history",
2055 api_name => "open-ils.actor.user.transactions.history",
2057 notes => <<" NOTES");
2058 Returns a list of billable transactions for a user, optionally by type
2060 __PACKAGE__->register_method(
2061 method => "user_transaction_history",
2062 api_name => "open-ils.actor.user.transactions.history.have_charge",
2064 notes => <<" NOTES");
2065 Returns a list of billable transactions for a user that have an initial charge, optionally by type
2067 __PACKAGE__->register_method(
2068 method => "user_transaction_history",
2069 api_name => "open-ils.actor.user.transactions.history.have_balance",
2072 notes => <<" NOTES");
2073 Returns a list of billable transactions for a user that have a balance, optionally by type
2075 __PACKAGE__->register_method(
2076 method => "user_transaction_history",
2077 api_name => "open-ils.actor.user.transactions.history.still_open",
2079 notes => <<" NOTES");
2080 Returns a list of billable transactions for a user that are not finished
2082 __PACKAGE__->register_method(
2083 method => "user_transaction_history",
2084 api_name => "open-ils.actor.user.transactions.history.have_bill",
2087 notes => <<" NOTES");
2088 Returns a list of billable transactions for a user that has billings
2090 __PACKAGE__->register_method(
2091 method => "user_transaction_history",
2092 api_name => "open-ils.actor.user.transactions.history.ids",
2094 notes => <<" NOTES");
2095 Returns a list of billable transaction ids for a user, optionally by type
2097 __PACKAGE__->register_method(
2098 method => "user_transaction_history",
2099 api_name => "open-ils.actor.user.transactions.history.have_charge.ids",
2101 notes => <<" NOTES");
2102 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
2104 __PACKAGE__->register_method(
2105 method => "user_transaction_history",
2106 api_name => "open-ils.actor.user.transactions.history.have_balance.ids",
2109 notes => <<" NOTES");
2110 Returns a list of billable transaction ids for a user that have a balance, optionally by type
2112 __PACKAGE__->register_method(
2113 method => "user_transaction_history",
2114 api_name => "open-ils.actor.user.transactions.history.still_open.ids",
2116 notes => <<" NOTES");
2117 Returns a list of billable transaction ids for a user that are not finished
2119 __PACKAGE__->register_method(
2120 method => "user_transaction_history",
2121 api_name => "open-ils.actor.user.transactions.history.have_bill.ids",
2124 notes => <<" NOTES");
2125 Returns a list of billable transaction ids for a user that has billings
2127 __PACKAGE__->register_method(
2128 method => "user_transaction_history",
2129 api_name => "open-ils.actor.user.transactions.history.have_bill_or_payment",
2132 notes => <<" NOTES");
2133 Returns a list of billable transactions for a user that has non-zero-sum billings or at least 1 payment
2135 __PACKAGE__->register_method(
2136 method => "user_transaction_history",
2137 api_name => "open-ils.actor.user.transactions.history.have_bill_or_payment.ids",
2140 notes => <<" NOTES");
2141 Returns a list of billable transaction ids for a user that has non-zero-sum billings or at least 1 payment
2146 sub user_transaction_history {
2147 my( $self, $conn, $auth, $userid, $type, $filter ) = @_;
2150 # run inside of a transaction to prevent replication delays
2151 my $e = new_editor(authtoken=>$auth);
2152 return $e->die_event unless $e->checkauth;
2154 if( $e->requestor->id ne $userid ) {
2155 return $e->die_event
2156 unless $e->allowed('VIEW_USER_TRANSACTIONS');
2159 my $api = $self->api_name;
2160 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
2162 my $mbts = $e->search_money_billable_transaction_summary(
2164 { usr => $userid, @xact_finish, %$filter },
2165 { order_by => { mbt => 'xact_start DESC' } }
2169 if(defined($type)) {
2170 @$mbts = grep { $_->xact_type eq $type } @$mbts;
2173 if($api =~ /have_bill_or_payment/o) {
2175 # transactions that have a non-zero sum across all billings or at least 1 payment
2177 int($_->balance_owed * 100) != 0 ||
2178 defined($_->last_payment_ts) } @$mbts;
2180 } elsif( $api =~ /have_balance/o) {
2182 # transactions that have a non-zero overall balance
2183 @$mbts = grep { int($_->balance_owed * 100) != 0 } @$mbts;
2185 } elsif( $api =~ /have_charge/o) {
2187 # transactions that have at least 1 billing, regardless of whether it was voided
2188 @$mbts = grep { defined($_->last_billing_ts) } @$mbts;
2190 } elsif( $api =~ /have_bill/o) {
2192 # transactions that have non-zero sum across all billings. This will exclude
2193 # xacts where all billings have been voided
2194 @$mbts = grep { int($_->total_owed * 100) != 0 } @$mbts;
2197 if ($api =~ /\.ids/) {
2198 return [map {$_->id} @$mbts];
2206 __PACKAGE__->register_method(
2207 method => "user_perms",
2208 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2210 notes => <<" NOTES");
2211 Returns a list of permissions
2214 my( $self, $client, $authtoken, $user ) = @_;
2216 my( $staff, $evt ) = $apputils->checkses($authtoken);
2217 return $evt if $evt;
2219 $user ||= $staff->id;
2221 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2225 return $apputils->simple_scalar_request(
2227 "open-ils.storage.permission.user_perms.atomic",
2231 __PACKAGE__->register_method(
2232 method => "retrieve_perms",
2233 api_name => "open-ils.actor.permissions.retrieve",
2234 notes => <<" NOTES");
2235 Returns a list of permissions
2237 sub retrieve_perms {
2238 my( $self, $client ) = @_;
2239 return $apputils->simple_scalar_request(
2241 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2242 { id => { '!=' => undef } }
2246 __PACKAGE__->register_method(
2247 method => "retrieve_groups",
2248 api_name => "open-ils.actor.groups.retrieve",
2249 notes => <<" NOTES");
2250 Returns a list of user groupss
2252 sub retrieve_groups {
2253 my( $self, $client ) = @_;
2254 return new_editor()->retrieve_all_permission_grp_tree();
2257 __PACKAGE__->register_method(
2258 method => "retrieve_org_address",
2259 api_name => "open-ils.actor.org_unit.address.retrieve",
2260 notes => <<' NOTES');
2261 Returns an org_unit address by ID
2262 @param An org_address ID
2264 sub retrieve_org_address {
2265 my( $self, $client, $id ) = @_;
2266 return $apputils->simple_scalar_request(
2268 "open-ils.cstore.direct.actor.org_address.retrieve",
2273 __PACKAGE__->register_method(
2274 method => "retrieve_groups_tree",
2275 api_name => "open-ils.actor.groups.tree.retrieve",
2276 notes => <<" NOTES");
2277 Returns a list of user groups
2279 sub retrieve_groups_tree {
2280 my( $self, $client ) = @_;
2281 return new_editor()->search_permission_grp_tree(
2286 flesh_fields => { pgt => ["children"] },
2287 order_by => { pgt => 'name'}
2294 __PACKAGE__->register_method(
2295 method => "add_user_to_groups",
2296 api_name => "open-ils.actor.user.set_groups",
2297 notes => <<" NOTES");
2298 Adds a user to one or more permission groups
2301 sub add_user_to_groups {
2302 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2304 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2305 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2306 return $evt if $evt;
2308 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2309 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2310 return $evt if $evt;
2312 $apputils->simplereq(
2314 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2316 for my $group (@$groups) {
2317 my $link = Fieldmapper::permission::usr_grp_map->new;
2319 $link->usr($userid);
2321 my $id = $apputils->simplereq(
2323 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2329 __PACKAGE__->register_method(
2330 method => "get_user_perm_groups",
2331 api_name => "open-ils.actor.user.get_groups",
2332 notes => <<" NOTES");
2333 Retrieve a user's permission groups.
2337 sub get_user_perm_groups {
2338 my( $self, $client, $authtoken, $userid ) = @_;
2340 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2341 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2342 return $evt if $evt;
2344 return $apputils->simplereq(
2346 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2350 __PACKAGE__->register_method(
2351 method => "get_user_work_ous",
2352 api_name => "open-ils.actor.user.get_work_ous",
2353 notes => <<" NOTES");
2354 Retrieve a user's work org units.
2356 __PACKAGE__->register_method(
2357 method => "get_user_work_ous",
2358 api_name => "open-ils.actor.user.get_work_ous.ids",
2359 notes => <<" NOTES");
2360 Retrieve a user's work org units.
2364 sub get_user_work_ous {
2365 my( $self, $client, $auth, $userid ) = @_;
2366 my $e = new_editor(authtoken=>$auth);
2367 return $e->event unless $e->checkauth;
2368 $userid ||= $e->requestor->id;
2370 if($e->requestor->id != $userid) {
2371 my $user = $e->retrieve_actor_user($userid)
2372 or return $e->event;
2373 return $e->event unless $e->allowed('ASSIGN_WORK_ORG_UNIT', $user->home_ou);
2376 return $e->search_permission_usr_work_ou_map({usr => $userid})
2377 unless $self->api_name =~ /.ids$/;
2379 # client just wants a list of org IDs
2380 return $U->get_user_work_ou_ids($e, $userid);
2386 __PACKAGE__->register_method (
2387 method => 'register_workstation',
2388 api_name => 'open-ils.actor.workstation.register.override',
2389 signature => q/@see open-ils.actor.workstation.register/);
2391 __PACKAGE__->register_method (
2392 method => 'register_workstation',
2393 api_name => 'open-ils.actor.workstation.register',
2395 Registers a new workstion in the system
2396 @param authtoken The login session key
2397 @param name The name of the workstation id
2398 @param owner The org unit that owns this workstation
2399 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2400 if the name is already in use.
2403 sub register_workstation {
2404 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2406 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2407 return $e->die_event unless $e->checkauth;
2408 return $e->die_event unless $e->allowed('REGISTER_WORKSTATION', $owner);
2409 my $existing = $e->search_actor_workstation({name => $name})->[0];
2413 if( $self->api_name =~ /override/o ) {
2414 # workstation with the given name exists.
2416 if($owner ne $existing->owning_lib) {
2417 # if necessary, update the owning_lib of the workstation
2419 $logger->info("changing owning lib of workstation ".$existing->id.
2420 " from ".$existing->owning_lib." to $owner");
2421 return $e->die_event unless
2422 $e->allowed('UPDATE_WORKSTATION', $existing->owning_lib);
2424 return $e->die_event unless $e->allowed('UPDATE_WORKSTATION', $owner);
2426 $existing->owning_lib($owner);
2427 return $e->die_event unless $e->update_actor_workstation($existing);
2433 "attempt to register an existing workstation. returning existing ID");
2436 return $existing->id;
2439 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2443 my $ws = Fieldmapper::actor::workstation->new;
2444 $ws->owning_lib($owner);
2446 $e->create_actor_workstation($ws) or return $e->die_event;
2448 return $ws->id; # note: editor sets the id on the new object for us
2451 __PACKAGE__->register_method (
2452 method => 'workstation_list',
2453 api_name => 'open-ils.actor.workstation.list',
2455 Returns a list of workstations registered at the given location
2456 @param authtoken The login session key
2457 @param ids A list of org_unit.id's for the workstation owners
2460 sub workstation_list {
2461 my( $self, $conn, $authtoken, @orgs ) = @_;
2463 my $e = new_editor(authtoken=>$authtoken);
2464 return $e->event unless $e->checkauth;
2469 unless $e->allowed('REGISTER_WORKSTATION', $o);
2470 $results{$o} = $e->search_actor_workstation({owning_lib=>$o});
2481 __PACKAGE__->register_method (
2482 method => 'fetch_patron_note',
2483 api_name => 'open-ils.actor.note.retrieve.all',
2486 Returns a list of notes for a given user
2487 Requestor must have VIEW_USER permission if pub==false and
2488 @param authtoken The login session key
2489 @param args Hash of params including
2490 patronid : the patron's id
2491 pub : true if retrieving only public notes
2495 sub fetch_patron_note {
2496 my( $self, $conn, $authtoken, $args ) = @_;
2497 my $patronid = $$args{patronid};
2499 my($reqr, $evt) = $U->checkses($authtoken);
2500 return $evt if $evt;
2503 ($patron, $evt) = $U->fetch_user($patronid);
2504 return $evt if $evt;
2507 if( $patronid ne $reqr->id ) {
2508 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2509 return $evt if $evt;
2511 return $U->cstorereq(
2512 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2513 { usr => $patronid, pub => 't' } );
2516 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2517 return $evt if $evt;
2519 return $U->cstorereq(
2520 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2523 __PACKAGE__->register_method (
2524 method => 'create_user_note',
2525 api_name => 'open-ils.actor.note.create',
2527 Creates a new note for the given user
2528 @param authtoken The login session key
2529 @param note The note object
2532 sub create_user_note {
2533 my( $self, $conn, $authtoken, $note ) = @_;
2534 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2535 return $e->die_event unless $e->checkauth;
2537 my $user = $e->retrieve_actor_user($note->usr)
2538 or return $e->die_event;
2540 return $e->die_event unless
2541 $e->allowed('UPDATE_USER',$user->home_ou);
2543 $note->creator($e->requestor->id);
2544 $e->create_actor_usr_note($note) or return $e->die_event;
2550 __PACKAGE__->register_method (
2551 method => 'delete_user_note',
2552 api_name => 'open-ils.actor.note.delete',
2554 Deletes a note for the given user
2555 @param authtoken The login session key
2556 @param noteid The note id
2559 sub delete_user_note {
2560 my( $self, $conn, $authtoken, $noteid ) = @_;
2562 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2563 return $e->die_event unless $e->checkauth;
2564 my $note = $e->retrieve_actor_usr_note($noteid)
2565 or return $e->die_event;
2566 my $user = $e->retrieve_actor_user($note->usr)
2567 or return $e->die_event;
2568 return $e->die_event unless
2569 $e->allowed('UPDATE_USER', $user->home_ou);
2571 $e->delete_actor_usr_note($note) or return $e->die_event;
2577 __PACKAGE__->register_method (
2578 method => 'update_user_note',
2579 api_name => 'open-ils.actor.note.update',
2581 @param authtoken The login session key
2582 @param note The note
2586 sub update_user_note {
2587 my( $self, $conn, $auth, $note ) = @_;
2588 my $e = new_editor(authtoken=>$auth, xact=>1);
2589 return $e->event unless $e->checkauth;
2590 my $patron = $e->retrieve_actor_user($note->usr)
2591 or return $e->event;
2592 return $e->event unless
2593 $e->allowed('UPDATE_USER', $patron->home_ou);
2594 $e->update_actor_user_note($note)
2595 or return $e->event;
2603 __PACKAGE__->register_method (
2604 method => 'create_closed_date',
2605 api_name => 'open-ils.actor.org_unit.closed_date.create',
2607 Creates a new closing entry for the given org_unit
2608 @param authtoken The login session key
2609 @param note The closed_date object
2612 sub create_closed_date {
2613 my( $self, $conn, $authtoken, $cd ) = @_;
2615 my( $user, $evt ) = $U->checkses($authtoken);
2616 return $evt if $evt;
2618 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2619 return $evt if $evt;
2621 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2623 my $id = $U->storagereq(
2624 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2625 return $U->DB_UPDATE_FAILED($cd) unless $id;
2630 __PACKAGE__->register_method (
2631 method => 'delete_closed_date',
2632 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2634 Deletes a closing entry for the given org_unit
2635 @param authtoken The login session key
2636 @param noteid The close_date id
2639 sub delete_closed_date {
2640 my( $self, $conn, $authtoken, $cd ) = @_;
2642 my( $user, $evt ) = $U->checkses($authtoken);
2643 return $evt if $evt;
2646 ($cd_obj, $evt) = fetch_closed_date($cd);
2647 return $evt if $evt;
2649 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2650 return $evt if $evt;
2652 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2654 my $stat = $U->storagereq(
2655 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2656 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2661 __PACKAGE__->register_method(
2662 method => 'usrname_exists',
2663 api_name => 'open-ils.actor.username.exists',
2665 Returns 1 if the requested username exists, returns 0 otherwise
2669 sub usrname_exists {
2670 my( $self, $conn, $auth, $usrname ) = @_;
2671 my $e = new_editor(authtoken=>$auth);
2672 return $e->event unless $e->checkauth;
2673 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2674 return $$a[0] if $a and @$a;
2678 __PACKAGE__->register_method(
2679 method => 'barcode_exists',
2680 api_name => 'open-ils.actor.barcode.exists',
2683 Returns 1 if the requested barcode exists, returns 0 otherwise
2687 sub barcode_exists {
2688 my( $self, $conn, $auth, $barcode ) = @_;
2689 my $e = new_editor(authtoken=>$auth);
2690 return $e->event unless $e->checkauth;
2691 my $card = $e->search_actor_card({barcode => $barcode});
2697 #return undef unless @$card;
2698 #return $card->[0]->usr;
2702 __PACKAGE__->register_method(
2703 method => 'retrieve_net_levels',
2704 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2707 sub retrieve_net_levels {
2708 my( $self, $conn, $auth ) = @_;
2709 my $e = new_editor(authtoken=>$auth);
2710 return $e->event unless $e->checkauth;
2711 return $e->retrieve_all_config_net_access_level();
2715 __PACKAGE__->register_method(
2716 method => 'fetch_org_by_shortname',
2717 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2719 sub fetch_org_by_shortname {
2720 my( $self, $conn, $sname ) = @_;
2721 my $e = new_editor();
2722 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2723 return $e->event unless $org;
2728 __PACKAGE__->register_method(
2729 method => 'session_home_lib',
2730 api_name => 'open-ils.actor.session.home_lib',
2733 sub session_home_lib {
2734 my( $self, $conn, $auth ) = @_;
2735 my $e = new_editor(authtoken=>$auth);
2736 return undef unless $e->checkauth;
2737 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2738 return $org->shortname;
2741 __PACKAGE__->register_method(
2742 method => 'session_safe_token',
2743 api_name => 'open-ils.actor.session.safe_token',
2745 Returns a hashed session ID that is safe for export to the world.
2746 This safe token will expire after 1 hour of non-use.
2747 @param auth Active authentication token
2751 sub session_safe_token {
2752 my( $self, $conn, $auth ) = @_;
2753 my $e = new_editor(authtoken=>$auth);
2754 return undef unless $e->checkauth;
2756 my $safe_token = md5_hex($auth);
2758 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2760 # Add more like the following if needed...
2762 "safe-token-home_lib-shortname-$safe_token",
2763 $e->retrieve_actor_org_unit(
2764 $e->requestor->home_ou
2773 __PACKAGE__->register_method(
2774 method => 'safe_token_home_lib',
2775 api_name => 'open-ils.actor.safe_token.home_lib.shortname',
2777 Returns the home library shortname from the session
2778 asscociated with a safe token from generated by
2779 open-ils.actor.session.safe_token.
2780 @param safe_token Active safe token
2784 sub safe_token_home_lib {
2785 my( $self, $conn, $safe_token ) = @_;
2787 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2788 return $cache->get_cache( 'safe-token-home_lib-shortname-'. $safe_token );
2793 __PACKAGE__->register_method(
2794 method => 'slim_tree',
2795 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2798 my $tree = new_editor()->search_actor_org_unit(
2800 {"parent_ou" => undef },
2803 flesh_fields => { aou => ['children'] },
2804 order_by => { aou => 'name'},
2805 select => { aou => ["id","shortname", "name"]},
2810 return trim_tree($tree);
2816 return undef unless $tree;
2818 code => $tree->shortname,
2819 name => $tree->name,
2821 if( $tree->children and @{$tree->children} ) {
2822 $htree->{children} = [];
2823 for my $c (@{$tree->children}) {
2824 push( @{$htree->{children}}, trim_tree($c) );
2832 __PACKAGE__->register_method(
2833 method => "update_penalties",
2834 api_name => "open-ils.actor.user.penalties.update");
2836 sub update_penalties {
2837 my($self, $conn, $auth, $user_id) = @_;
2838 my $e = new_editor(authtoken=>$auth, xact => 1);
2839 return $e->die_event unless $e->checkauth;
2840 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
2841 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2842 my $evt = OpenILS::Utils::Penalty->calculate_penalties($e, $user_id, $e->requestor->ws_ou);
2843 return $evt if $evt;
2849 __PACKAGE__->register_method(
2850 method => "apply_penalty",
2851 api_name => "open-ils.actor.user.penalty.apply");
2854 my($self, $conn, $auth, $penalty) = @_;
2856 my $e = new_editor(authtoken=>$auth, xact => 1);
2857 return $e->die_event unless $e->checkauth;
2859 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2860 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2862 my $ptype = $e->retrieve_config_standing_penalty($penalty->standing_penalty) or return $e->die_event;
2865 (defined $ptype->org_depth) ?
2866 $U->org_unit_ancestor_at_depth($penalty->org_unit, $ptype->org_depth) :
2869 $penalty->org_unit($ctx_org);
2870 $penalty->staff($e->requestor->id);
2871 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
2874 return $penalty->id;
2877 __PACKAGE__->register_method(
2878 method => "remove_penalty",
2879 api_name => "open-ils.actor.user.penalty.remove");
2881 sub remove_penalty {
2882 my($self, $conn, $auth, $penalty) = @_;
2883 my $e = new_editor(authtoken=>$auth, xact => 1);
2884 return $e->die_event unless $e->checkauth;
2885 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2886 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2888 $e->delete_actor_user_standing_penalty($penalty) or return $e->die_event;
2893 __PACKAGE__->register_method(
2894 method => "update_penalty_note",
2895 api_name => "open-ils.actor.user.penalty.note.update");
2897 sub update_penalty_note {
2898 my($self, $conn, $auth, $penalty_ids, $note) = @_;
2899 my $e = new_editor(authtoken=>$auth, xact => 1);
2900 return $e->die_event unless $e->checkauth;
2901 for my $penalty_id (@$penalty_ids) {
2902 my $penalty = $e->search_actor_user_standing_penalty( { id => $penalty_id } )->[0];
2903 if (! $penalty ) { return $e->die_event; }
2904 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2905 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2907 $penalty->note( $note ); $penalty->ischanged( 1 );
2909 $e->update_actor_user_standing_penalty($penalty) or return $e->die_event;
2915 __PACKAGE__->register_method(
2916 method => "ranged_penalty_thresholds",
2917 api_name => "open-ils.actor.grp_penalty_threshold.ranged.retrieve",
2921 sub ranged_penalty_thresholds {
2922 my($self, $conn, $auth, $context_org) = @_;
2923 my $e = new_editor(authtoken=>$auth);
2924 return $e->event unless $e->checkauth;
2925 return $e->event unless $e->allowed('VIEW_GROUP_PENALTY_THRESHOLD', $context_org);
2926 my $list = $e->search_permission_grp_penalty_threshold([
2927 {org_unit => $U->get_org_ancestors($context_org)},
2928 {order_by => {pgpt => 'id'}}
2930 $conn->respond($_) for @$list;
2936 __PACKAGE__->register_method(
2937 method => "user_retrieve_fleshed_by_id",
2939 api_name => "open-ils.actor.user.fleshed.retrieve",);
2941 sub user_retrieve_fleshed_by_id {
2942 my( $self, $client, $auth, $user_id, $fields ) = @_;
2943 my $e = new_editor(authtoken => $auth);
2944 return $e->event unless $e->checkauth;
2946 if( $e->requestor->id != $user_id ) {
2947 return $e->event unless $e->allowed('VIEW_USER');
2953 "standing_penalties",
2957 "stat_cat_entries" ];
2958 return new_flesh_user($user_id, $fields, $e);
2962 sub new_flesh_user {
2965 my $fields = shift || [];
2968 my $fetch_penalties = 0;
2969 if(grep {$_ eq 'standing_penalties'} @$fields) {
2970 $fields = [grep {$_ ne 'standing_penalties'} @$fields];
2971 $fetch_penalties = 1;
2974 my $user = $e->retrieve_actor_user(
2979 "flesh_fields" => { "au" => $fields }
2982 ) or return $e->event;
2985 if( grep { $_ eq 'addresses' } @$fields ) {
2987 $user->addresses([]) unless @{$user->addresses};
2988 # don't expose "replaced" addresses by default
2989 $user->addresses([grep {$_->id >= 0} @{$user->addresses}]);
2991 if( ref $user->billing_address ) {
2992 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2993 push( @{$user->addresses}, $user->billing_address );
2997 if( ref $user->mailing_address ) {
2998 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2999 push( @{$user->addresses}, $user->mailing_address );
3004 if($fetch_penalties) {
3005 # grab the user penalties ranged for this location
3006 $user->standing_penalties(
3007 $e->search_actor_user_standing_penalty([
3010 {stop_date => undef},
3011 {stop_date => {'>' => 'now'}}
3013 org_unit => $U->get_org_ancestors($e->requestor->ws_ou)
3016 flesh_fields => {ausp => ['standing_penalty']}
3023 $user->clear_passwd();
3030 __PACKAGE__->register_method(
3031 method => "user_retrieve_parts",
3032 api_name => "open-ils.actor.user.retrieve.parts",);
3034 sub user_retrieve_parts {
3035 my( $self, $client, $auth, $user_id, $fields ) = @_;
3036 my $e = new_editor(authtoken => $auth);
3037 return $e->event unless $e->checkauth;
3038 if( $e->requestor->id != $user_id ) {
3039 return $e->event unless $e->allowed('VIEW_USER');
3042 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3043 push(@resp, $user->$_()) for(@$fields);
3049 __PACKAGE__->register_method(
3050 method => 'user_opt_in_enabled',
3051 api_name => 'open-ils.actor.user.org_unit_opt_in.enabled',
3053 @return 1 if user opt-in is globally enabled, 0 otherwise.
3056 sub user_opt_in_enabled {
3057 my($self, $conn) = @_;
3058 my $sc = OpenSRF::Utils::SettingsClient->new;
3059 return 1 if lc($sc->config_value(share => user => 'opt_in')) eq 'true';
3064 __PACKAGE__->register_method(
3065 method => 'user_opt_in_at_org',
3066 api_name => 'open-ils.actor.user.org_unit_opt_in.check',
3068 @param $auth The auth token
3069 @param user_id The ID of the user to test
3070 @return 1 if the user has opted in at the specified org,
3071 event on error, and 0 otherwise. /);
3072 sub user_opt_in_at_org {
3073 my($self, $conn, $auth, $user_id) = @_;
3075 # see if we even need to enforce the opt-in value
3076 return 1 unless user_opt_in_enabled($self);
3078 my $e = new_editor(authtoken => $auth);
3079 return $e->event unless $e->checkauth;
3080 my $org_id = $e->requestor->ws_ou;
3082 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3083 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3085 # user is automatically opted-in at the home org
3086 return 1 if $user->home_ou eq $org_id;
3088 my $vals = $e->search_actor_usr_org_unit_opt_in(
3089 {org_unit=>$org_id, usr=>$user_id},{idlist=>1});
3095 __PACKAGE__->register_method(
3096 method => 'create_user_opt_in_at_org',
3097 api_name => 'open-ils.actor.user.org_unit_opt_in.create',
3099 @param $auth The auth token
3100 @param user_id The ID of the user to test
3101 @return The ID of the newly created object, event on error./);
3103 sub create_user_opt_in_at_org {
3104 my($self, $conn, $auth, $user_id) = @_;
3106 my $e = new_editor(authtoken => $auth, xact=>1);
3107 return $e->die_event unless $e->checkauth;
3108 my $org_id = $e->requestor->ws_ou;
3110 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3111 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3113 my $opt_in = Fieldmapper::actor::usr_org_unit_opt_in->new;
3115 $opt_in->org_unit($org_id);
3116 $opt_in->usr($user_id);
3117 $opt_in->staff($e->requestor->id);
3118 $opt_in->opt_in_ts('now');
3119 $opt_in->opt_in_ws($e->requestor->wsid);
3121 $opt_in = $e->create_actor_usr_org_unit_opt_in($opt_in)
3122 or return $e->die_event;
3130 __PACKAGE__->register_method (
3131 method => 'retrieve_org_hours',
3132 api_name => 'open-ils.actor.org_unit.hours_of_operation.retrieve',
3134 Returns the hours of operation for a specified org unit
3135 @param authtoken The login session key
3136 @param org_id The org_unit ID
3140 sub retrieve_org_hours {
3141 my($self, $conn, $auth, $org_id) = @_;
3142 my $e = new_editor(authtoken => $auth);
3143 return $e->die_event unless $e->checkauth;
3144 $org_id ||= $e->requestor->ws_ou;
3145 return $e->retrieve_actor_org_unit_hours_of_operation($org_id);
3149 __PACKAGE__->register_method (
3150 method => 'verify_user_password',
3151 api_name => 'open-ils.actor.verify_user_password',
3153 Given a barcode or username and the MD5 encoded password,
3154 returns 1 if the password is correct. Returns 0 otherwise.
3158 sub verify_user_password {
3159 my($self, $conn, $auth, $barcode, $username, $password) = @_;
3160 my $e = new_editor(authtoken => $auth);
3161 return $e->die_event unless $e->checkauth;
3163 my $user_by_barcode;
3164 my $user_by_username;
3166 my $card = $e->search_actor_card([
3167 {barcode => $barcode},
3168 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0] or return 0;
3169 $user_by_barcode = $card->usr;
3170 $user = $user_by_barcode;
3173 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return 0;
3174 $user = $user_by_username;
3176 return 0 if (!$user);
3177 return 0 if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3178 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3179 return 1 if $user->passwd eq $password;
3183 __PACKAGE__->register_method (
3184 method => 'retrieve_usr_id_via_barcode_or_usrname',
3185 api_name => "open-ils.actor.user.retrieve_id_by_barcode_or_username",
3187 Given a barcode or username returns the id for the user or
3192 sub retrieve_usr_id_via_barcode_or_usrname {
3193 my($self, $conn, $auth, $barcode, $username) = @_;
3194 my $e = new_editor(authtoken => $auth);
3195 return $e->die_event unless $e->checkauth;
3196 my $id_as_barcode= OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.actor' => app_settings => 'id_as_barcode');
3198 my $user_by_barcode;
3199 my $user_by_username;
3200 $logger->info("$id_as_barcode is the ID as BARCODE");
3202 my $card = $e->search_actor_card([
3203 {barcode => $barcode},
3204 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0];
3205 if ($id_as_barcode =~ /^t/i) {
3207 $user = $e->retrieve_actor_user($barcode);
3208 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$user);
3210 $user_by_barcode = $card->usr;
3211 $user = $user_by_barcode;
3214 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$card);
3215 $user_by_barcode = $card->usr;
3216 $user = $user_by_barcode;
3221 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return OpenILS::Event->new( 'ACTOR_USR_NOT_FOUND' );
3223 $user = $user_by_username;
3225 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if (!$user);
3226 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3227 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3232 __PACKAGE__->register_method (
3233 method => 'merge_users',
3234 api_name => 'open-ils.actor.user.merge',
3237 Given a list of source users and destination user, transfer all data from the source
3238 to the dest user and delete the source user. All user related data is
3239 transferred, including circulations, holds, bookbags, etc.
3245 my($self, $conn, $auth, $master_id, $user_ids, $options) = @_;
3246 my $e = new_editor(xact => 1, authtoken => $auth);
3247 return $e->die_event unless $e->checkauth;
3249 # disallow the merge if any subordinate accounts are in collections
3250 my $colls = $e->search_money_collections_tracker({usr => $user_ids}, {idlist => 1});
3251 return OpenILS::Event->new('MERGED_USER_IN_COLLECTIONS', payload => $user_ids) if @$colls;
3253 my $master_user = $e->retrieve_actor_user($master_id) or return $e->die_event;
3254 my $del_addrs = ($U->ou_ancestor_setting_value(
3255 $master_user->home_ou, 'circ.user_merge.delete_addresses', $e)) ? 't' : 'f';
3256 my $del_cards = ($U->ou_ancestor_setting_value(
3257 $master_user->home_ou, 'circ.user_merge.delete_cards', $e)) ? 't' : 'f';
3258 my $deactivate_cards = ($U->ou_ancestor_setting_value(
3259 $master_user->home_ou, 'circ.user_merge.deactivate_cards', $e)) ? 't' : 'f';
3261 for my $src_id (@$user_ids) {
3262 my $src_user = $e->retrieve_actor_user($src_id) or return $e->die_event;
3264 return $e->die_event unless $e->allowed('MERGE_USERS', $src_user->home_ou);
3265 if($src_user->home_ou ne $master_user->home_ou) {
3266 return $e->die_event unless $e->allowed('MERGE_USERS', $master_user->home_ou);
3269 return $e->die_event unless
3270 $e->json_query({from => [
3285 __PACKAGE__->register_method (
3286 method => 'approve_user_address',
3287 api_name => 'open-ils.actor.user.pending_address.approve',
3294 sub approve_user_address {
3295 my($self, $conn, $auth, $addr) = @_;
3296 my $e = new_editor(xact => 1, authtoken => $auth);
3297 return $e->die_event unless $e->checkauth;
3299 # if the caller passes an address object, assume they want to
3300 # update it first before approving it
3301 $e->update_actor_user_address($addr) or return $e->die_event;
3303 $addr = $e->retrieve_actor_user_address($addr) or return $e->die_event;
3305 my $user = $e->retrieve_actor_user($addr->usr);
3306 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3307 my $result = $e->json_query({from => ['actor.approve_pending_address', $addr->id]})->[0]
3308 or return $e->die_event;
3310 return [values %$result]->[0];
3314 __PACKAGE__->register_method (
3315 method => 'retrieve_friends',
3316 api_name => 'open-ils.actor.friends.retrieve',
3319 returns { confirmed: [], pending_out: [], pending_in: []}
3320 pending_out are users I'm requesting friendship with
3321 pending_in are users requesting friendship with me
3326 sub retrieve_friends {
3327 my($self, $conn, $auth, $user_id, $options) = @_;
3328 my $e = new_editor(authtoken => $auth);
3329 return $e->event unless $e->checkauth;
3330 $user_id ||= $e->requestor->id;
3332 if($user_id != $e->requestor->id) {
3333 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3334 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3337 return OpenILS::Application::Actor::Friends->retrieve_friends(
3338 $e, $user_id, $options);
3343 __PACKAGE__->register_method (
3344 method => 'apply_friend_perms',
3345 api_name => 'open-ils.actor.friends.perms.apply',
3351 sub apply_friend_perms {
3352 my($self, $conn, $auth, $user_id, $delegate_id, @perms) = @_;
3353 my $e = new_editor(authtoken => $auth, xact => 1);
3354 return $e->event unless $e->checkauth;
3356 if($user_id != $e->requestor->id) {
3357 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3358 return $e->die_event unless $e->allowed('VIEW_USER', $user->home_ou);
3361 for my $perm (@perms) {
3363 OpenILS::Application::Actor::Friends->apply_friend_perm(
3364 $e, $user_id, $delegate_id, $perm);
3365 return $evt if $evt;
3373 __PACKAGE__->register_method (
3374 method => 'update_user_pending_address',
3375 api_name => 'open-ils.actor.user.address.pending.cud'
3378 sub update_user_pending_address {
3379 my($self, $conn, $auth, $addr) = @_;
3380 my $e = new_editor(authtoken => $auth, xact => 1);
3381 return $e->event unless $e->checkauth;
3383 if($addr->usr != $e->requestor->id) {
3384 my $user = $e->retrieve_actor_user($addr->usr) or return $e->die_event;
3385 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3389 $e->create_actor_user_address($addr) or return $e->die_event;
3390 } elsif($addr->isdeleted) {
3391 $e->delete_actor_user_address($addr) or return $e->die_event;
3393 $e->update_actor_user_address($addr) or return $e->die_event;
3401 __PACKAGE__->register_method (
3402 method => 'user_events',
3403 api_name => 'open-ils.actor.user.events.circ',
3406 __PACKAGE__->register_method (
3407 method => 'user_events',
3408 api_name => 'open-ils.actor.user.events.ahr',
3413 my($self, $conn, $auth, $user_id, $filters) = @_;
3414 my $e = new_editor(authtoken => $auth);
3415 return $e->event unless $e->checkauth;
3417 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3418 my $user_field = 'usr';
3421 $filters->{target} = {
3422 select => { $obj_type => ['id'] },
3424 where => {usr => $user_id}
3427 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3428 if($e->requestor->id != $user_id) {
3429 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3432 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3433 my $req = $ses->request('open-ils.trigger.events_by_target',
3434 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3436 while(my $resp = $req->recv) {
3437 my $val = $resp->content;
3438 my $tgt = $val->target;
3440 if($obj_type eq 'circ') {
3441 $tgt->target_copy($e->retrieve_asset_copy($tgt->target_copy));
3443 } elsif($obj_type eq 'ahr') {
3444 $tgt->current_copy($e->retrieve_asset_copy($tgt->current_copy))
3445 if $tgt->current_copy;
3448 $conn->respond($val) if $val;
3454 __PACKAGE__->register_method (
3455 method => 'copy_events',
3456 api_name => 'open-ils.actor.copy.events.circ',
3459 __PACKAGE__->register_method (
3460 method => 'copy_events',
3461 api_name => 'open-ils.actor.copy.events.ahr',
3466 my($self, $conn, $auth, $copy_id, $filters) = @_;
3467 my $e = new_editor(authtoken => $auth);
3468 return $e->event unless $e->checkauth;
3470 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3472 my $copy = $e->retrieve_asset_copy($copy_id) or return $e->event;
3474 my $copy_field = 'target_copy';
3475 $copy_field = 'current_copy' if $obj_type eq 'ahr';
3478 $filters->{target} = {
3479 select => { $obj_type => ['id'] },
3481 where => {$copy_field => $copy_id}
3485 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3486 my $req = $ses->request('open-ils.trigger.events_by_target',
3487 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3489 while(my $resp = $req->recv) {
3490 my $val = $resp->content;
3491 my $tgt = $val->target;
3493 my $user = $e->retrieve_actor_user($tgt->usr);
3494 if($e->requestor->id != $user->id) {
3495 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3498 $tgt->$copy_field($copy);
3501 $conn->respond($val) if $val;
3510 __PACKAGE__->register_method (
3511 method => 'update_events',
3512 api_name => 'open-ils.actor.user.event.cancel.batch',
3515 __PACKAGE__->register_method (
3516 method => 'update_events',
3517 api_name => 'open-ils.actor.user.event.reset.batch',
3522 my($self, $conn, $auth, $event_ids) = @_;
3523 my $e = new_editor(xact => 1, authtoken => $auth);
3524 return $e->die_event unless $e->checkauth;
3527 for my $id (@$event_ids) {
3529 # do a little dance to determine what user we are ultimately affecting
3530 my $event = $e->retrieve_action_trigger_event([
3533 flesh_fields => {atev => ['event_def'], atevdef => ['hook']}
3535 ]) or return $e->die_event;
3538 if($event->event_def->hook->core_type eq 'circ') {
3539 $user_id = $e->retrieve_action_circulation($event->target)->usr;
3540 } elsif($event->event_def->hook->core_type eq 'ahr') {
3541 $user_id = $e->retrieve_action_hold_request($event->target)->usr;
3546 my $user = $e->retrieve_actor_user($user_id);
3547 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3549 if($self->api_name =~ /cancel/) {
3550 $event->state('invalid');
3551 } elsif($self->api_name =~ /reset/) {
3552 $event->clear_start_time;
3553 $event->clear_update_time;
3554 $event->state('pending');
3557 $e->update_action_trigger_event($event) or return $e->die_event;
3558 $conn->respond({maximum => scalar(@$event_ids), progress => $x++});
3562 return {complete => 1};
3566 __PACKAGE__->register_method (
3567 method => 'really_delete_user',
3568 api_name => 'open-ils.actor.user.delete',
3570 It anonymizes all personally identifiable information in actor.usr. By calling actor.usr_purge_data()
3571 it also purges related data from other tables, sometimes by transferring it to a designated destination user.
3572 The usrname field (along with first_given_name and family_name) is updated to id '-PURGED-' now().
3573 dest_usr_id is only required when deleting a user that performs staff functions.
3577 sub really_delete_user {
3578 my($self, $conn, $auth, $user_id, $dest_user_id) = @_;
3579 my $e = new_editor(authtoken => $auth, xact => 1);
3580 return $e->die_event unless $e->checkauth;
3581 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3582 return $e->die_event unless $e->allowed('DELETE_USER', $user->home_ou);
3583 my $stat = $e->json_query(
3584 {from => ['actor.usr_delete', $user_id, $dest_user_id]})->[0]
3585 or return $e->die_event;
3592 __PACKAGE__->register_method (
3593 method => 'user_payments',
3594 api_name => 'open-ils.actor.user.payments.retrieve',
3597 Returns all payments for a given user. Default order is newest payments first.
3598 @param auth Authentication token
3599 @param user_id The user ID
3600 @param filters An optional hash of filters, including limit, offset, and order_by definitions
3605 my($self, $conn, $auth, $user_id, $filters) = @_;
3608 my $e = new_editor(authtoken => $auth);
3609 return $e->die_event unless $e->checkauth;
3611 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3612 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS', $user->home_ou);
3614 # Find all payments for all transactions for user $user_id
3616 select => {mp => ['id']},
3621 select => {mbt => ['id']},
3623 where => {usr => $user_id}
3627 order_by => [{ # by default, order newest payments first
3629 field => 'payment_ts',
3634 for (qw/order_by limit offset/) {
3635 $query->{$_} = $filters->{$_} if defined $filters->{$_};
3638 if(defined $filters->{where}) {
3639 foreach (keys %{$filters->{where}}) {
3640 # don't allow the caller to expand the result set to other users
3641 $query->{where}->{$_} = $filters->{where}->{$_} unless $_ eq 'xact';
3645 my $payment_ids = $e->json_query($query);
3646 for my $pid (@$payment_ids) {
3647 my $pay = $e->retrieve_money_payment([
3652 mbt => ['summary', 'circulation', 'grocery'],
3653 circ => ['target_copy'],
3654 acp => ['call_number'],
3662 xact_type => $pay->xact->summary->xact_type,
3663 last_billing_type => $pay->xact->summary->last_billing_type,
3666 if($pay->xact->summary->xact_type eq 'circulation') {
3667 $resp->{barcode} = $pay->xact->circulation->target_copy->barcode;
3668 $resp->{title} = $U->record_to_mvr($pay->xact->circulation->target_copy->call_number->record)->title;
3671 $pay->xact($pay->xact->id); # de-flesh
3672 $conn->respond($resp);
3680 __PACKAGE__->register_method (
3681 method => 'negative_balance_users',
3682 api_name => 'open-ils.actor.users.negative_balance',
3685 Returns all users that have an overall negative balance
3686 @param auth Authentication token
3687 @param org_id The context org unit as an ID or list of IDs. This will be the home
3688 library of the user. If no org_unit is specified, no org unit filter is applied
3692 sub negative_balance_users {
3693 my($self, $conn, $auth, $org_id) = @_;
3695 my $e = new_editor(authtoken => $auth);
3696 return $e->die_event unless $e->checkauth;
3697 return $e->die_event unless $e->allowed('VIEW_USER', $org_id);
3701 mous => ['usr', 'balance_owed'],
3704 {column => 'last_billing_ts', transform => 'max', aggregate => 1},
3705 {column => 'last_payment_ts', transform => 'max', aggregate => 1},
3722 where => {'+mous' => {balance_owed => {'<' => 0}}}
3725 $query->{from}->{mous}->{au}->{filter}->{home_ou} = $org_id if $org_id;
3727 my $list = $e->json_query($query, {timeout => 600});
3729 for my $data (@$list) {
3731 usr => $e->retrieve_actor_user([$data->{usr}, {flesh => 1, flesh_fields => {au => ['card']}}]),
3732 balance_owed => $data->{balance_owed},
3733 last_billing_activity => max($data->{last_billing_ts}, $data->{last_payment_ts})