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",
117 desc => "Updates the value for a given org unit setting. The permission to update " .
118 "an org unit setting is either the UPDATE_ORG_UNIT_SETTING_ALL, or a specific " .
119 "permission specified in the update_perm column of the config.org_unit_setting_type " .
120 "table's row corresponding to the setting being changed." ,
122 {desc => 'Authentication token', type => 'string'},
123 {desc => 'Org unit ID', type => 'number'},
124 {desc => 'Hash of setting name-value pairs', type => 'object'}
126 return => {desc => '1 on success, Event on error'}
130 sub set_ou_settings {
131 my( $self, $client, $auth, $org_id, $settings ) = @_;
133 my $e = new_editor(authtoken => $auth, xact => 1);
134 return $e->die_event unless $e->checkauth;
136 my $all_allowed = $e->allowed("UPDATE_ORG_UNIT_SETTING_ALL", $org_id);
138 for my $name (keys %$settings) {
139 my $val = $$settings{$name};
141 my $type = $e->retrieve_config_org_unit_setting_type([
143 {flesh => 1, flesh_fields => {'coust' => ['update_perm']}}
144 ]) or return $e->die_event;
145 my $set = $e->search_actor_org_unit_setting({org_unit => $org_id, name => $name})->[0];
147 # If there is no relevant permission, the default assumption will
148 # be, "no, the caller cannot change that value."
149 return $e->die_event unless ($all_allowed ||
150 ($type->update_perm && $e->allowed($type->update_perm->code, $org_id)));
153 $val = OpenSRF::Utils::JSON->perl2JSON($val);
156 $e->update_actor_org_unit_setting($set) or return $e->die_event;
158 $set = Fieldmapper::actor::org_unit_setting->new;
159 $set->org_unit($org_id);
162 $e->create_actor_org_unit_setting($set) or return $e->die_event;
165 $e->delete_actor_org_unit_setting($set) or return $e->die_event;
173 __PACKAGE__->register_method(
174 method => "user_settings",
175 api_name => "open-ils.actor.patron.settings.retrieve",
178 my( $self, $client, $auth, $user_id, $setting ) = @_;
180 my $e = new_editor(authtoken => $auth);
181 return $e->event unless $e->checkauth;
182 $user_id = $e->requestor->id unless defined $user_id;
184 my $patron = $e->retrieve_actor_user($user_id) or return $e->event;
185 if($e->requestor->id != $user_id) {
186 return $e->event unless $e->allowed('VIEW_USER', $patron->home_ou);
190 my($e, $user_id, $setting) = @_;
191 my $val = $e->search_actor_user_setting({usr => $user_id, name => $setting})->[0];
192 return undef unless $val; # XXX this should really return undef, but needs testing
193 return OpenSRF::Utils::JSON->JSON2perl($val->value);
197 if(ref $setting eq 'ARRAY') {
199 $settings{$_} = get_setting($e, $user_id, $_) for @$setting;
202 return get_setting($e, $user_id, $setting);
205 my $s = $e->search_actor_user_setting({usr => $user_id});
206 return { map { ( $_->name => OpenSRF::Utils::JSON->JSON2perl($_->value) ) } @$s };
211 __PACKAGE__->register_method(
212 method => "ranged_ou_settings",
213 api_name => "open-ils.actor.org_unit_setting.values.ranged.retrieve",
215 desc => "Retrieves all org unit settings for the given org_id, up to whatever limit " .
216 "is implied for retrieving OU settings by the authenticated users' permissions.",
218 {desc => 'Authentication token', type => 'string'},
219 {desc => 'Org unit ID', type => 'number'},
221 return => {desc => 'A hashref of "ranged" settings, event on error'}
224 sub ranged_ou_settings {
225 my( $self, $client, $auth, $org_id ) = @_;
227 my $e = new_editor(authtoken => $auth);
228 return $e->event unless $e->checkauth;
231 my $org_list = $U->get_org_ancestors($org_id);
232 my $settings = $e->search_actor_org_unit_setting({org_unit => $org_list});
233 $org_list = [ reverse @$org_list ];
235 # start at the context org and capture the setting value
236 # without clobbering settings we've already captured
237 for my $this_org_id (@$org_list) {
239 my @sets = grep { $_->org_unit == $this_org_id } @$settings;
241 for my $set (@sets) {
242 my $type = $e->retrieve_config_org_unit_setting_type([
244 {flesh => 1, flesh_fields => {coust => ['view_perm']}}
247 # If there is no relevant permission, the default assumption will
248 # be, "yes, the caller can have that value."
249 if ($type && $type->view_perm) {
250 next if not $e->allowed($type->view_perm->code, $org_id);
253 $ranged_settings{$set->name} = OpenSRF::Utils::JSON->JSON2perl($set->value)
254 unless defined $ranged_settings{$set->name};
258 return \%ranged_settings;
263 __PACKAGE__->register_method(
264 api_name => 'open-ils.actor.ou_setting.ancestor_default',
265 method => 'ou_ancestor_setting',
267 desc => 'Get the org unit setting value associated with the setting name as seen from the specified org unit. ' .
268 'IF AND ONLY IF an authentication token is provided, this method will make sure that the given ' .
269 'user has permission to view that setting, if there is a permission associated with the setting.' ,
271 { desc => 'Org unit ID', type => 'number' },
272 { desc => 'setting name', type => 'string' },
273 { desc => 'authtoken (optional)', type => 'string' }
275 return => {desc => 'A value for the org unit setting, or undef'}
279 # ------------------------------------------------------------------
280 # Attempts to find the org setting value for a given org. if not
281 # found at the requested org, searches up the org tree until it
282 # finds a parent that has the requested setting.
283 # when found, returns { org => $id, value => $value }
284 # otherwise, returns NULL
285 # ------------------------------------------------------------------
286 sub ou_ancestor_setting {
287 my( $self, $client, $orgid, $name, $auth ) = @_;
288 return $U->ou_ancestor_setting($orgid, $name, undef, $auth);
291 __PACKAGE__->register_method(
292 api_name => 'open-ils.actor.ou_setting.ancestor_default.batch',
293 method => 'ou_ancestor_setting_batch',
295 desc => 'Get org unit setting name => value pairs for a list of names, as seen from the specified org unit. ' .
296 'IF AND ONLY IF an authentication token is provided, this method will make sure that the given ' .
297 'user has permission to view that setting, if there is a permission associated with the setting.' ,
299 { desc => 'Org unit ID', type => 'number' },
300 { desc => 'setting name list', type => 'array' },
301 { desc => 'authtoken (optional)', type => 'string' }
303 return => {desc => 'A hash with name => value pairs for the org unit settings'}
306 sub ou_ancestor_setting_batch {
307 my( $self, $client, $orgid, $name_list, $auth ) = @_;
309 $values{$_} = $U->ou_ancestor_setting($orgid, $_, undef, $auth) for @$name_list;
315 __PACKAGE__->register_method(
316 method => "update_patron",
317 api_name => "open-ils.actor.patron.update",
320 Update an existing user, or create a new one. Related objects,
321 like cards, addresses, survey responses, and stat cats,
322 can be updated by attaching them to the user object in their
323 respective fields. For examples, the billing address object
324 may be inserted into the 'billing_address' field, etc. For each
325 attached object, indicate if the object should be created,
326 updated, or deleted using the built-in 'isnew', 'ischanged',
327 and 'isdeleted' fields on the object.
330 { desc => 'Authentication token', type => 'string' },
331 { desc => 'Patron data object', type => 'object' }
333 return => {desc => 'A fleshed user object, event on error'}
338 my( $self, $client, $user_session, $patron ) = @_;
340 my $session = $apputils->start_db_session();
342 $logger->info($patron->isnew ? "Creating new patron..." : "Updating Patron: " . $patron->id);
344 my( $user_obj, $evt ) = $U->checkses($user_session);
347 $evt = check_group_perm($session, $user_obj, $patron);
351 # $new_patron is the patron in progress. $patron is the original patron
352 # passed in with the method. new_patron will change as the components
353 # of patron are added/updated.
357 # unflesh the real items on the patron
358 $patron->card( $patron->card->id ) if(ref($patron->card));
359 $patron->billing_address( $patron->billing_address->id )
360 if(ref($patron->billing_address));
361 $patron->mailing_address( $patron->mailing_address->id )
362 if(ref($patron->mailing_address));
364 # create/update the patron first so we can use his id
365 if($patron->isnew()) {
366 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
368 } else { $new_patron = $patron; }
370 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
373 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
376 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
379 # re-update the patron if anything has happened to him during this process
380 if($new_patron->ischanged()) {
381 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
385 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
388 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
391 $apputils->commit_db_session($session);
393 $evt = apply_invalid_addr_penalty($patron);
396 my $tses = OpenSRF::AppSession->create('open-ils.trigger');
398 $tses->request('open-ils.trigger.event.autocreate', 'au.create', $new_patron, $new_patron->home_ou);
400 $tses->request('open-ils.trigger.event.autocreate', 'au.update', $new_patron, $new_patron->home_ou);
403 return flesh_user($new_patron->id(), new_editor(requestor => $user_obj, xact => 1));
406 sub apply_invalid_addr_penalty {
408 my $e = new_editor(xact => 1);
410 # grab the invalid address penalty if set
411 my $penalties = OpenILS::Utils::Penalty->retrieve_usr_penalties($e, $patron->id, $patron->home_ou);
413 my ($addr_penalty) = grep
414 { $_->standing_penalty->name eq 'INVALID_PATRON_ADDRESS' } @$penalties;
416 # do we enforce invalid address penalty
417 my $enforce = $U->ou_ancestor_setting_value(
418 $patron->home_ou, 'circ.patron_invalid_address_apply_penalty') || 0;
420 my $addrs = $e->search_actor_user_address(
421 {usr => $patron->id, valid => 'f', id => {'>' => 0}}, {idlist => 1});
422 my $addr_count = scalar(@$addrs);
424 if($addr_count == 0 and $addr_penalty) {
426 # regardless of any settings, remove the penalty when the user has no invalid addresses
427 $e->delete_actor_user_standing_penalty($addr_penalty) or return $e->die_event;
430 } elsif($enforce and $addr_count > 0 and !$addr_penalty) {
432 my $ptype = $e->retrieve_config_standing_penalty(29) or return $e->die_event;
433 my $depth = $ptype->org_depth;
434 my $ctx_org = $U->org_unit_ancestor_at_depth($patron->home_ou, $depth) if defined $depth;
435 $ctx_org = $patron->home_ou unless defined $ctx_org;
437 my $penalty = Fieldmapper::actor::user_standing_penalty->new;
438 $penalty->usr($patron->id);
439 $penalty->org_unit($ctx_org);
440 $penalty->standing_penalty(OILS_PENALTY_INVALID_PATRON_ADDRESS);
442 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
456 return new_flesh_user($id, [
459 "standing_penalties",
463 "stat_cat_entries" ], $e );
471 # clone and clear stuff that would break the database
475 my $new_patron = $patron->clone;
477 $new_patron->clear_billing_address();
478 $new_patron->clear_mailing_address();
479 $new_patron->clear_addresses();
480 $new_patron->clear_card();
481 $new_patron->clear_cards();
482 $new_patron->clear_id();
483 $new_patron->clear_isnew();
484 $new_patron->clear_ischanged();
485 $new_patron->clear_isdeleted();
486 $new_patron->clear_stat_cat_entries();
487 $new_patron->clear_permissions();
488 $new_patron->clear_standing_penalties();
498 my $user_obj = shift;
500 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
501 return (undef, $evt) if $evt;
503 my $ex = $session->request(
504 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
506 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
509 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
511 my $id = $session->request(
512 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
513 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
515 $logger->info("Successfully created new user [$id] in DB");
517 return ( $session->request(
518 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
522 sub check_group_perm {
523 my( $session, $requestor, $patron ) = @_;
526 # first let's see if the requestor has
527 # priveleges to update this user in any way
528 if( ! $patron->isnew ) {
529 my $p = $session->request(
530 'open-ils.storage.direct.actor.user.retrieve', $patron->id )->gather(1);
532 # If we are the requestor (trying to update our own account)
533 # and we are not trying to change our profile, we're good
534 if( $p->id == $requestor->id and
535 $p->profile == $patron->profile ) {
540 $evt = group_perm_failed($session, $requestor, $p);
544 # They are allowed to edit this patron.. can they put the
545 # patron into the group requested?
546 $evt = group_perm_failed($session, $requestor, $patron);
552 sub group_perm_failed {
553 my( $session, $requestor, $patron ) = @_;
557 my $grpid = $patron->profile;
561 $logger->debug("user update looking for group perm for group $grpid");
562 $grp = $session->request(
563 'open-ils.storage.direct.permission.grp_tree.retrieve', $grpid )->gather(1);
564 return OpenILS::Event->new('PERMISSION_GRP_TREE_NOT_FOUND') unless $grp;
566 } while( !($perm = $grp->application_perm) and ($grpid = $grp->parent) );
568 $logger->info("user update checking perm $perm on user ".
569 $requestor->id." for update/create on user username=".$patron->usrname);
571 my $evt = $U->check_perms($requestor->id, $patron->home_ou, $perm);
579 my( $session, $patron, $user_obj, $noperm) = @_;
581 $logger->info("Updating patron ".$patron->id." in DB");
586 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
587 return (undef, $evt) if $evt;
590 # update the password by itself to avoid the password protection magic
591 if( $patron->passwd ) {
592 my $s = $session->request(
593 'open-ils.storage.direct.actor.user.remote_update',
594 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
595 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
596 $patron->clear_passwd;
599 if(!$patron->ident_type) {
600 $patron->clear_ident_type;
601 $patron->clear_ident_value;
604 $evt = verify_last_xact($session, $patron);
605 return (undef, $evt) if $evt;
607 my $stat = $session->request(
608 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
609 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
614 sub verify_last_xact {
615 my( $session, $patron ) = @_;
616 return undef unless $patron->id and $patron->id > 0;
617 my $p = $session->request(
618 'open-ils.storage.direct.actor.user.retrieve', $patron->id)->gather(1);
619 my $xact = $p->last_xact_id;
620 return undef unless $xact;
621 $logger->info("user xact = $xact, saving with xact " . $patron->last_xact_id);
622 return OpenILS::Event->new('XACT_COLLISION')
623 if $xact != $patron->last_xact_id;
628 sub _check_dup_ident {
629 my( $session, $patron ) = @_;
631 return undef unless $patron->ident_value;
634 ident_type => $patron->ident_type,
635 ident_value => $patron->ident_value,
638 $logger->debug("patron update searching for dup ident values: " .
639 $patron->ident_type . ':' . $patron->ident_value);
641 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
643 my $dups = $session->request(
644 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
647 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
654 sub _add_update_addresses {
658 my $new_patron = shift;
662 my $current_id; # id of the address before creation
664 for my $address (@{$patron->addresses()}) {
666 next unless ref $address;
667 $current_id = $address->id();
669 if( $patron->billing_address() and
670 $patron->billing_address() == $current_id ) {
671 $logger->info("setting billing addr to $current_id");
672 $new_patron->billing_address($address->id());
673 $new_patron->ischanged(1);
676 if( $patron->mailing_address() and
677 $patron->mailing_address() == $current_id ) {
678 $new_patron->mailing_address($address->id());
679 $logger->info("setting mailing addr to $current_id");
680 $new_patron->ischanged(1);
684 if($address->isnew()) {
686 $address->usr($new_patron->id());
688 ($address, $evt) = _add_address($session,$address);
689 return (undef, $evt) if $evt;
691 # we need to get the new id
692 if( $patron->billing_address() and
693 $patron->billing_address() == $current_id ) {
694 $new_patron->billing_address($address->id());
695 $logger->info("setting billing addr to $current_id");
696 $new_patron->ischanged(1);
699 if( $patron->mailing_address() and
700 $patron->mailing_address() == $current_id ) {
701 $new_patron->mailing_address($address->id());
702 $logger->info("setting mailing addr to $current_id");
703 $new_patron->ischanged(1);
706 } elsif($address->ischanged() ) {
708 ($address, $evt) = _update_address($session, $address);
709 return (undef, $evt) if $evt;
711 } elsif($address->isdeleted() ) {
713 if( $address->id() == $new_patron->mailing_address() ) {
714 $new_patron->clear_mailing_address();
715 ($new_patron, $evt) = _update_patron($session, $new_patron);
716 return (undef, $evt) if $evt;
719 if( $address->id() == $new_patron->billing_address() ) {
720 $new_patron->clear_billing_address();
721 ($new_patron, $evt) = _update_patron($session, $new_patron);
722 return (undef, $evt) if $evt;
725 $evt = _delete_address($session, $address);
726 return (undef, $evt) if $evt;
730 return ( $new_patron, undef );
734 # adds an address to the db and returns the address with new id
736 my($session, $address) = @_;
737 $address->clear_id();
739 $logger->info("Creating new address at street ".$address->street1);
741 # put the address into the database
742 my $id = $session->request(
743 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
744 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
747 return ($address, undef);
751 sub _update_address {
752 my( $session, $address ) = @_;
754 $logger->info("Updating address ".$address->id." in the DB");
756 my $stat = $session->request(
757 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
759 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
760 return ($address, undef);
765 sub _add_update_cards {
769 my $new_patron = shift;
773 my $virtual_id; #id of the card before creation
774 for my $card (@{$patron->cards()}) {
776 $card->usr($new_patron->id());
778 if(ref($card) and $card->isnew()) {
780 $virtual_id = $card->id();
781 ( $card, $evt ) = _add_card($session,$card);
782 return (undef, $evt) if $evt;
784 #if(ref($patron->card)) { $patron->card($patron->card->id); }
785 if($patron->card() == $virtual_id) {
786 $new_patron->card($card->id());
787 $new_patron->ischanged(1);
790 } elsif( ref($card) and $card->ischanged() ) {
791 $evt = _update_card($session, $card);
792 return (undef, $evt) if $evt;
796 return ( $new_patron, undef );
800 # adds an card to the db and returns the card with new id
802 my( $session, $card ) = @_;
805 $logger->info("Adding new patron card ".$card->barcode);
807 my $id = $session->request(
808 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
809 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
810 $logger->info("Successfully created patron card $id");
813 return ( $card, undef );
817 # returns event on error. returns undef otherwise
819 my( $session, $card ) = @_;
820 $logger->info("Updating patron card ".$card->id);
822 my $stat = $session->request(
823 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
824 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
831 # returns event on error. returns undef otherwise
832 sub _delete_address {
833 my( $session, $address ) = @_;
835 $logger->info("Deleting address ".$address->id." from DB");
837 my $stat = $session->request(
838 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
840 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
846 sub _add_survey_responses {
847 my ($session, $patron, $new_patron) = @_;
849 $logger->info( "Updating survey responses for patron ".$new_patron->id );
851 my $responses = $patron->survey_responses;
855 $_->usr($new_patron->id) for (@$responses);
857 my $evt = $U->simplereq( "open-ils.circ",
858 "open-ils.circ.survey.submit.user_id", $responses );
860 return (undef, $evt) if defined($U->event_code($evt));
864 return ( $new_patron, undef );
868 sub _create_stat_maps {
870 my($session, $user_session, $patron, $new_patron) = @_;
872 my $maps = $patron->stat_cat_entries();
874 for my $map (@$maps) {
876 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
878 if ($map->isdeleted()) {
879 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
881 } elsif ($map->isnew()) {
882 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
887 $map->target_usr($new_patron->id);
890 $logger->info("Updating stat entry with method $method and map $map");
892 my $stat = $session->request($method, $map)->gather(1);
893 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
897 return ($new_patron, undef);
900 sub _create_perm_maps {
902 my($session, $user_session, $patron, $new_patron) = @_;
904 my $maps = $patron->permissions;
906 for my $map (@$maps) {
908 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
909 if ($map->isdeleted()) {
910 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
911 } elsif ($map->isnew()) {
912 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
917 $map->usr($new_patron->id);
919 #warn( "Updating permissions with method $method and session $user_session and map $map" );
920 $logger->info( "Updating permissions with method $method and map $map" );
922 my $stat = $session->request($method, $map)->gather(1);
923 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
927 return ($new_patron, undef);
931 __PACKAGE__->register_method(
932 method => "set_user_work_ous",
933 api_name => "open-ils.actor.user.work_ous.update",
936 sub set_user_work_ous {
942 my( $requestor, $evt ) = $apputils->checksesperm( $ses, 'ASSIGN_WORK_ORG_UNIT' );
945 my $session = $apputils->start_db_session();
947 for my $map (@$maps) {
949 my $method = "open-ils.storage.direct.permission.usr_work_ou_map.update";
950 if ($map->isdeleted()) {
951 $method = "open-ils.storage.direct.permission.usr_work_ou_map.delete";
952 } elsif ($map->isnew()) {
953 $method = "open-ils.storage.direct.permission.usr_work_ou_map.create";
957 #warn( "Updating permissions with method $method and session $ses and map $map" );
958 $logger->info( "Updating work_ou map with method $method and map $map" );
960 my $stat = $session->request($method, $map)->gather(1);
961 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
965 $apputils->commit_db_session($session);
967 return scalar(@$maps);
971 __PACKAGE__->register_method(
972 method => "set_user_perms",
973 api_name => "open-ils.actor.user.permissions.update",
982 my $session = $apputils->start_db_session();
984 my( $user_obj, $evt ) = $U->checkses($ses);
987 my $perms = $session->request('open-ils.storage.permission.user_perms.atomic', $user_obj->id)->gather(1);
990 $all = 1 if ($U->is_true($user_obj->super_user()));
991 $all = 1 unless ($U->check_perms($user_obj->id, $user_obj->home_ou, 'EVERYTHING'));
993 for my $map (@$maps) {
995 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
996 if ($map->isdeleted()) {
997 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
998 } elsif ($map->isnew()) {
999 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
1003 next if (!$all and !grep { $_->perm eq $map->perm and $U->is_true($_->grantable) and $_->depth <= $map->depth } @$perms);
1004 #warn( "Updating permissions with method $method and session $ses and map $map" );
1005 $logger->info( "Updating permissions with method $method and map $map" );
1007 my $stat = $session->request($method, $map)->gather(1);
1008 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
1012 $apputils->commit_db_session($session);
1014 return scalar(@$maps);
1018 __PACKAGE__->register_method(
1019 method => "user_retrieve_by_barcode",
1021 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
1023 sub user_retrieve_by_barcode {
1024 my($self, $client, $user_session, $barcode) = @_;
1026 $logger->debug("Searching for user with barcode $barcode");
1027 my ($user_obj, $evt) = $apputils->checkses($user_session);
1028 return $evt if $evt;
1030 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
1032 "open-ils.cstore.direct.actor.card.search.atomic",
1033 { barcode => $barcode }
1036 if(!$card || !$card->[0]) {
1037 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
1041 my $user = flesh_user($card->usr(), new_editor(requestor => $user_obj));
1043 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
1044 return $evt if $evt;
1046 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
1053 __PACKAGE__->register_method(
1054 method => "get_user_by_id",
1056 api_name => "open-ils.actor.user.retrieve",);
1058 sub get_user_by_id {
1059 my ($self, $client, $auth, $id) = @_;
1060 my $e = new_editor(authtoken=>$auth);
1061 return $e->event unless $e->checkauth;
1062 my $user = $e->retrieve_actor_user($id)
1063 or return $e->event;
1064 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
1070 __PACKAGE__->register_method(
1071 method => "get_org_types",
1072 api_name => "open-ils.actor.org_types.retrieve",);
1075 return $U->get_org_types();
1080 __PACKAGE__->register_method(
1081 method => "get_user_ident_types",
1082 api_name => "open-ils.actor.user.ident_types.retrieve",
1085 sub get_user_ident_types {
1086 return $ident_types if $ident_types;
1087 return $ident_types =
1088 new_editor()->retrieve_all_config_identification_type();
1094 __PACKAGE__->register_method(
1095 method => "get_org_unit",
1096 api_name => "open-ils.actor.org_unit.retrieve",
1100 my( $self, $client, $user_session, $org_id ) = @_;
1101 my $e = new_editor(authtoken => $user_session);
1103 return $e->event unless $e->checkauth;
1104 $org_id = $e->requestor->ws_ou;
1106 my $o = $e->retrieve_actor_org_unit($org_id)
1107 or return $e->event;
1111 __PACKAGE__->register_method(
1112 method => "search_org_unit",
1113 api_name => "open-ils.actor.org_unit_list.search",
1116 sub search_org_unit {
1118 my( $self, $client, $field, $value ) = @_;
1120 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
1122 "open-ils.cstore.direct.actor.org_unit.search.atomic",
1123 { $field => $value } );
1129 # build the org tree
1131 __PACKAGE__->register_method(
1132 method => "get_org_tree",
1133 api_name => "open-ils.actor.org_tree.retrieve",
1135 note => "Returns the entire org tree structure",
1141 return $U->get_org_tree($client->session->session_locale);
1145 __PACKAGE__->register_method(
1146 method => "get_org_descendants",
1147 api_name => "open-ils.actor.org_tree.descendants.retrieve"
1150 # depth is optional. org_unit is the id
1151 sub get_org_descendants {
1152 my( $self, $client, $org_unit, $depth ) = @_;
1154 if(ref $org_unit eq 'ARRAY') {
1157 for my $i (0..scalar(@$org_unit)-1) {
1158 my $list = $U->simple_scalar_request(
1160 "open-ils.storage.actor.org_unit.descendants.atomic",
1161 $org_unit->[$i], $depth->[$i] );
1162 push(@trees, $U->build_org_tree($list));
1167 my $orglist = $apputils->simple_scalar_request(
1169 "open-ils.storage.actor.org_unit.descendants.atomic",
1170 $org_unit, $depth );
1171 return $U->build_org_tree($orglist);
1176 __PACKAGE__->register_method(
1177 method => "get_org_ancestors",
1178 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
1181 # depth is optional. org_unit is the id
1182 sub get_org_ancestors {
1183 my( $self, $client, $org_unit, $depth ) = @_;
1184 my $orglist = $apputils->simple_scalar_request(
1186 "open-ils.storage.actor.org_unit.ancestors.atomic",
1187 $org_unit, $depth );
1188 return $U->build_org_tree($orglist);
1192 __PACKAGE__->register_method(
1193 method => "get_standings",
1194 api_name => "open-ils.actor.standings.retrieve"
1199 return $user_standings if $user_standings;
1200 return $user_standings =
1201 $apputils->simple_scalar_request(
1203 "open-ils.cstore.direct.config.standing.search.atomic",
1204 { id => { "!=" => undef } }
1210 __PACKAGE__->register_method(
1211 method => "get_my_org_path",
1212 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1215 sub get_my_org_path {
1216 my( $self, $client, $auth, $org_id ) = @_;
1217 my $e = new_editor(authtoken=>$auth);
1218 return $e->event unless $e->checkauth;
1219 $org_id = $e->requestor->ws_ou unless defined $org_id;
1221 return $apputils->simple_scalar_request(
1223 "open-ils.storage.actor.org_unit.full_path.atomic",
1228 __PACKAGE__->register_method(
1229 method => "patron_adv_search",
1230 api_name => "open-ils.actor.patron.search.advanced" );
1231 sub patron_adv_search {
1232 my( $self, $client, $auth, $search_hash,
1233 $search_limit, $search_sort, $include_inactive, $search_depth ) = @_;
1235 my $e = new_editor(authtoken=>$auth);
1236 return $e->event unless $e->checkauth;
1237 return $e->event unless $e->allowed('VIEW_USER');
1238 return $U->storagereq(
1239 "open-ils.storage.actor.user.crazy_search", $search_hash,
1240 $search_limit, $search_sort, $include_inactive, $e->requestor->ws_ou, $search_depth);
1244 __PACKAGE__->register_method(
1245 method => "update_passwd",
1247 api_name => "open-ils.actor.user.password.update");
1249 __PACKAGE__->register_method(
1250 method => "update_passwd",
1251 api_name => "open-ils.actor.user.username.update");
1253 __PACKAGE__->register_method(
1254 method => "update_passwd",
1255 api_name => "open-ils.actor.user.email.update");
1258 my( $self, $conn, $auth, $new_val, $orig_pw ) = @_;
1259 my $e = new_editor(xact=>1, authtoken=>$auth);
1260 return $e->die_event unless $e->checkauth;
1262 my $db_user = $e->retrieve_actor_user($e->requestor->id)
1263 or return $e->die_event;
1264 my $api = $self->api_name;
1266 if( $api =~ /password/o ) {
1268 # make sure the original password matches the in-database password
1269 return OpenILS::Event->new('INCORRECT_PASSWORD')
1270 if md5_hex($orig_pw) ne $db_user->passwd;
1271 $db_user->passwd($new_val);
1275 # if we don't clear the password, the user will be updated with
1276 # a hashed version of the hashed version of their password
1277 $db_user->clear_passwd;
1279 if( $api =~ /username/o ) {
1281 # make sure no one else has this username
1282 my $exist = $e->search_actor_user({usrname=>$new_val},{idlist=>1});
1283 return OpenILS::Event->new('USERNAME_EXISTS') if @$exist;
1284 $db_user->usrname($new_val);
1286 } elsif( $api =~ /email/o ) {
1287 $db_user->email($new_val);
1291 $e->update_actor_user($db_user) or return $e->die_event;
1299 __PACKAGE__->register_method(
1300 method => "check_user_perms",
1301 api_name => "open-ils.actor.user.perm.check",
1302 notes => <<" NOTES");
1303 Takes a login session, user id, an org id, and an array of perm type strings. For each
1304 perm type, if the user does *not* have the given permission it is added
1305 to a list which is returned from the method. If all permissions
1306 are allowed, an empty list is returned
1307 if the logged in user does not match 'user_id', then the logged in user must
1308 have VIEW_PERMISSION priveleges.
1311 sub check_user_perms {
1312 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1314 my( $staff, $evt ) = $apputils->checkses($login_session);
1315 return $evt if $evt;
1317 if($staff->id ne $user_id) {
1318 if( $evt = $apputils->check_perms(
1319 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1325 for my $perm (@$perm_types) {
1326 if($apputils->check_perms($user_id, $org_id, $perm)) {
1327 push @not_allowed, $perm;
1331 return \@not_allowed
1334 __PACKAGE__->register_method(
1335 method => "check_user_perms2",
1336 api_name => "open-ils.actor.user.perm.check.multi_org",
1338 Checks the permissions on a list of perms and orgs for a user
1339 @param authtoken The login session key
1340 @param user_id The id of the user to check
1341 @param orgs The array of org ids
1342 @param perms The array of permission names
1343 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1344 if the logged in user does not match 'user_id', then the logged in user must
1345 have VIEW_PERMISSION priveleges.
1348 sub check_user_perms2 {
1349 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1351 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1352 $authtoken, $user_id, 'VIEW_PERMISSION' );
1353 return $evt if $evt;
1356 for my $org (@$orgs) {
1357 for my $perm (@$perms) {
1358 if($apputils->check_perms($user_id, $org, $perm)) {
1359 push @not_allowed, [ $org, $perm ];
1364 return \@not_allowed
1368 __PACKAGE__->register_method(
1369 method => 'check_user_perms3',
1370 api_name => 'open-ils.actor.user.perm.highest_org',
1372 Returns the highest org unit id at which a user has a given permission
1373 If the requestor does not match the target user, the requestor must have
1374 'VIEW_PERMISSION' rights at the home org unit of the target user
1375 @param authtoken The login session key
1376 @param userid The id of the user in question
1377 @param perm The permission to check
1378 @return The org unit highest in the org tree within which the user has
1379 the requested permission
1382 sub check_user_perms3 {
1383 my($self, $client, $authtoken, $user_id, $perm) = @_;
1384 my $e = new_editor(authtoken=>$authtoken);
1385 return $e->event unless $e->checkauth;
1387 my $tree = $U->get_org_tree();
1389 unless($e->requestor->id == $user_id) {
1390 my $user = $e->retrieve_actor_user($user_id)
1391 or return $e->event;
1392 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1393 return $U->find_highest_perm_org($perm, $user_id, $user->home_ou, $tree );
1396 return $U->find_highest_perm_org($perm, $user_id, $e->requestor->ws_ou, $tree);
1399 __PACKAGE__->register_method(
1400 method => 'user_has_work_perm_at',
1401 api_name => 'open-ils.actor.user.has_work_perm_at',
1405 Returns a set of org unit IDs which represent the highest orgs in
1406 the org tree where the user has the requested permission. The
1407 purpose of this method is to return the smallest set of org units
1408 which represent the full expanse of the user's ability to perform
1409 the requested action. The user whose perms this method should
1410 check is implied by the authtoken. /,
1412 {desc => 'authtoken', type => 'string'},
1413 {desc => 'permission name', type => 'string'},
1414 {desc => q/user id, optional. If present, check perms for
1415 this user instead of the logged in user/, type => 'number'},
1417 return => {desc => 'An array of org IDs'}
1421 sub user_has_work_perm_at {
1422 my($self, $conn, $auth, $perm, $user_id) = @_;
1423 my $e = new_editor(authtoken=>$auth);
1424 return $e->event unless $e->checkauth;
1425 if(defined $user_id) {
1426 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1427 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1429 return $U->user_has_work_perm_at($e, $perm, undef, $user_id);
1432 __PACKAGE__->register_method(
1433 method => 'user_has_work_perm_at_batch',
1434 api_name => 'open-ils.actor.user.has_work_perm_at.batch',
1438 sub user_has_work_perm_at_batch {
1439 my($self, $conn, $auth, $perms, $user_id) = @_;
1440 my $e = new_editor(authtoken=>$auth);
1441 return $e->event unless $e->checkauth;
1442 if(defined $user_id) {
1443 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1444 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1447 $map->{$_} = $U->user_has_work_perm_at($e, $_) for @$perms;
1453 __PACKAGE__->register_method(
1454 method => 'check_user_perms4',
1455 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1457 Returns the highest org unit id at which a user has a given permission
1458 If the requestor does not match the target user, the requestor must have
1459 'VIEW_PERMISSION' rights at the home org unit of the target user
1460 @param authtoken The login session key
1461 @param userid The id of the user in question
1462 @param perms An array of perm names to check
1463 @return An array of orgId's representing the org unit
1464 highest in the org tree within which the user has the requested permission
1465 The arrah of orgId's has matches the order of the perms array
1468 sub check_user_perms4 {
1469 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1471 my( $staff, $target, $org, $evt );
1473 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1474 $authtoken, $userid, 'VIEW_PERMISSION' );
1475 return $evt if $evt;
1478 return [] unless ref($perms);
1479 my $tree = $U->get_org_tree();
1481 for my $p (@$perms) {
1482 push( @arr, $U->find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1488 __PACKAGE__->register_method(
1489 method => "user_fines_summary",
1490 api_name => "open-ils.actor.user.fines.summary",
1493 desc => 'Returns a short summary of the users total open fines, ' .
1494 'excluding voided fines Params are login_session, user_id' ,
1496 {desc => 'Authentication token', type => 'string'},
1497 {desc => 'User ID', type => 'string'} # number?
1500 desc => "a 'mous' object, event on error",
1505 sub user_fines_summary {
1506 my( $self, $client, $auth, $user_id ) = @_;
1507 my $e = new_editor(authtoken=>$auth);
1508 return $e->event unless $e->checkauth;
1509 my $user = $e->retrieve_actor_user($user_id)
1510 or return $e->event;
1512 if( $user_id ne $e->requestor->id ) {
1513 return $e->event unless
1514 $e->allowed('VIEW_USER_FINES_SUMMARY', $user->home_ou);
1517 # run this inside a transaction to prevent replication delay errors
1518 my $ses = $U->start_db_session();
1519 my $s = $ses->request(
1520 'open-ils.storage.money.open_user_summary.search', $user_id )->gather(1);
1521 $U->rollback_db_session($ses);
1527 __PACKAGE__->register_method(
1528 method => "user_transactions",
1529 api_name => "open-ils.actor.user.transactions",
1530 notes => <<" NOTES");
1531 Returns a list of open user transactions (mbts objects);
1532 Params are login_session, user_id
1533 Optional third parameter is the transactions type. defaults to all
1536 __PACKAGE__->register_method(
1537 method => "user_transactions",
1538 api_name => "open-ils.actor.user.transactions.have_charge",
1539 notes => <<" NOTES");
1540 Returns a list of all open user transactions (mbts objects) that have an initial charge
1541 Params are login_session, user_id
1542 Optional third parameter is the transactions type. defaults to all
1545 __PACKAGE__->register_method(
1546 method => "user_transactions",
1547 api_name => "open-ils.actor.user.transactions.have_balance",
1549 notes => <<" NOTES");
1550 Returns a list of all open user transactions (mbts objects) that have a balance
1551 Params are login_session, user_id
1552 Optional third parameter is the transactions type. defaults to all
1555 __PACKAGE__->register_method(
1556 method => "user_transactions",
1557 api_name => "open-ils.actor.user.transactions.fleshed",
1558 notes => <<" NOTES");
1559 Returns an object/hash of transaction, circ, title where transaction = an open
1560 user transactions (mbts objects), circ is the attached circluation, and title
1561 is the title the circ points to
1562 Params are login_session, user_id
1563 Optional third parameter is the transactions type. defaults to all
1566 __PACKAGE__->register_method(
1567 method => "user_transactions",
1568 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1569 notes => <<" NOTES");
1570 Returns an object/hash of transaction, circ, title where transaction = an open
1571 user transactions that has an initial charge (mbts objects), circ is the
1572 attached circluation, and title is the title the circ points to
1573 Params are login_session, user_id
1574 Optional third parameter is the transactions type. defaults to all
1577 __PACKAGE__->register_method(
1578 method => "user_transactions",
1579 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1581 notes => <<" NOTES");
1582 Returns an object/hash of transaction, circ, title where transaction = an open
1583 user transaction that has a balance (mbts objects), circ is the attached
1584 circluation, and title is the title the circ points to
1585 Params are login_session, user_id
1586 Optional third parameter is the transaction type. defaults to all
1589 __PACKAGE__->register_method(
1590 method => "user_transactions",
1591 api_name => "open-ils.actor.user.transactions.count",
1592 notes => <<" NOTES");
1593 Returns an object/hash of transaction, circ, title where transaction = an open
1594 user transactions (mbts objects), circ is the attached circluation, and title
1595 is the title the circ points to
1596 Params are login_session, user_id
1597 Optional third parameter is the transactions type. defaults to all
1600 __PACKAGE__->register_method(
1601 method => "user_transactions",
1602 api_name => "open-ils.actor.user.transactions.have_charge.count",
1603 notes => <<" NOTES");
1604 Returns an object/hash of transaction, circ, title where transaction = an open
1605 user transactions that has an initial charge (mbts objects), circ is the
1606 attached circluation, and title is the title the circ points to
1607 Params are login_session, user_id
1608 Optional third parameter is the transactions type. defaults to all
1611 __PACKAGE__->register_method(
1612 method => "user_transactions",
1613 api_name => "open-ils.actor.user.transactions.have_balance.count",
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
1623 __PACKAGE__->register_method(
1624 method => "user_transactions",
1625 api_name => "open-ils.actor.user.transactions.have_balance.total",
1627 notes => <<" NOTES");
1628 Returns an object/hash of transaction, circ, title where transaction = an open
1629 user transaction that has a balance (mbts objects), circ is the attached
1630 circluation, and title is the title the circ points to
1631 Params are login_session, user_id
1632 Optional third parameter is the transaction type. defaults to all
1637 sub user_transactions {
1638 my( $self, $client, $login_session, $user_id, $type ) = @_;
1640 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1641 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1642 return $evt if $evt;
1644 my $api = $self->api_name();
1646 my ($trans) = $self->method_lookup(
1647 'open-ils.actor.user.transactions.history.still_open')
1648 ->run( $login_session, $user_id, $type );
1650 $trans = ($api =~ /have_balance/o) ?
1651 [ grep { int($_->balance_owed * 100) != 0 } @$trans ] :
1652 [ grep { int($_->total_owed * 100) > 0 } @$trans ] ;
1654 if($api =~ /total/o) {
1656 for my $t (@$trans) {
1657 $total += $t->balance_owed;
1660 $logger->debug("Total balance owed by user $user_id: $total");
1664 ($api =~ /count/o ) and return scalar @$trans;
1665 ($api !~ /fleshed/o) and return $trans;
1668 for my $t (@$trans) {
1670 if( $t->xact_type ne 'circulation' ) {
1671 push @resp, {transaction => $t};
1675 my $circ = $apputils->simple_scalar_request(
1677 "open-ils.cstore.direct.action.circulation.retrieve",
1682 my $title = $apputils->simple_scalar_request(
1684 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1685 $circ->target_copy );
1689 my $u = OpenILS::Utils::ModsParser->new();
1690 $u->start_mods_batch($title->marc());
1691 my $mods = $u->finish_mods_batch();
1692 $mods->doc_id($title->id) if $mods;
1694 push @resp, {transaction => $t, circ => $circ, record => $mods };
1702 __PACKAGE__->register_method(
1703 method => "user_transaction_retrieve",
1704 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1706 notes => "Returns a fleshed transaction record"
1709 __PACKAGE__->register_method(
1710 method => "user_transaction_retrieve",
1711 api_name => "open-ils.actor.user.transaction.retrieve",
1713 notes => "Returns a transaction record"
1716 sub user_transaction_retrieve {
1717 my( $self, $client, $login_session, $bill_id ) = @_;
1719 # I think I'm deprecated... make sure. phasefx says, "No, I'll use you :)
1721 my $trans = $apputils->simple_scalar_request(
1723 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1727 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1728 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1729 return $evt if $evt;
1731 my $api = $self->api_name();
1732 if($api !~ /fleshed/o) { return $trans; }
1734 if( $trans->xact_type ne 'circulation' ) {
1735 $logger->debug("Returning non-circ transaction");
1736 return {transaction => $trans};
1739 my $circ = $apputils->simple_scalar_request(
1741 "open-ils.cstore.direct.action.circulation.retrieve",
1744 return {transaction => $trans} unless $circ;
1745 $logger->debug("Found the circ transaction");
1747 my $title = $apputils->simple_scalar_request(
1749 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1750 $circ->target_copy );
1752 return {transaction => $trans, circ => $circ } unless $title;
1753 $logger->debug("Found the circ title");
1756 my $copy = $apputils->simple_scalar_request(
1758 "open-ils.cstore.direct.asset.copy.retrieve",
1759 $circ->target_copy );
1762 my $u = OpenILS::Utils::ModsParser->new();
1763 $u->start_mods_batch($title->marc());
1764 $mods = $u->finish_mods_batch();
1766 if ($title->id == OILS_PRECAT_RECORD) {
1767 $mods = new Fieldmapper::metabib::virtual_record;
1768 $mods->doc_id(OILS_PRECAT_RECORD);
1769 $mods->title($copy->dummy_title);
1770 $mods->author($copy->dummy_author);
1774 $logger->debug("MODSized the circ title");
1776 return {transaction => $trans, circ => $circ, record => $mods, copy => $copy };
1780 __PACKAGE__->register_method(
1781 method => "hold_request_count",
1782 api_name => "open-ils.actor.user.hold_requests.count",
1785 notes => 'Returns hold ready/total counts'
1788 sub hold_request_count {
1789 my( $self, $client, $login_session, $userid ) = @_;
1791 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1792 $login_session, $userid, 'VIEW_HOLD' );
1793 return $evt if $evt;
1796 my $holds = $apputils->simple_scalar_request(
1798 "open-ils.cstore.direct.action.hold_request.search.atomic",
1801 fulfillment_time => {"=" => undef },
1802 cancel_time => undef,
1807 for my $h (@$holds) {
1808 next unless $h->capture_time and $h->current_copy;
1810 my $copy = $apputils->simple_scalar_request(
1812 "open-ils.cstore.direct.asset.copy.retrieve",
1816 if ($copy and $copy->status == 8) {
1821 return { total => scalar(@$holds), ready => scalar(@ready) };
1825 __PACKAGE__->register_method(
1826 method => "checkedout_count",
1827 api_name => "open-ils.actor.user.checked_out.count__",
1829 notes => "Returns a transaction record"
1834 sub checkedout_count {
1835 my( $self, $client, $login_session, $userid ) = @_;
1837 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1838 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1839 return $evt if $evt;
1841 my $circs = $apputils->simple_scalar_request(
1843 "open-ils.cstore.direct.action.circulation.search.atomic",
1844 { usr => $userid, stop_fines => undef }
1845 #{ usr => $userid, checkin_time => {"=" => undef } }
1848 my $parser = DateTime::Format::ISO8601->new;
1851 for my $c (@$circs) {
1852 my $due_dt = $parser->parse_datetime( cleanse_ISO8601( $c->due_date ) );
1853 if ($due_dt->epoch < DateTime->today->epoch) {
1858 return { total => scalar(@$circs), overdue => $overdue };
1862 __PACKAGE__->register_method(
1863 method => "checked_out",
1864 api_name => "open-ils.actor.user.checked_out",
1868 Returns a structure of circulations objects sorted by
1869 out, overdue, lost, claims_returned, long_overdue.
1870 A list of IDs are returned of each type.
1871 lost, long_overdue, and claims_returned circ will not
1872 be "finished" (there is an outstanding balance or some
1873 other pending action on the circ).
1875 The .count method also includes a 'total' field which
1876 sums all "open" circs
1880 __PACKAGE__->register_method(
1881 method => "checked_out",
1882 api_name => "open-ils.actor.user.checked_out.count",
1885 signature => q/@see open-ils.actor.user.checked_out/
1889 my( $self, $conn, $auth, $userid ) = @_;
1891 my $e = new_editor(authtoken=>$auth);
1892 return $e->event unless $e->checkauth;
1894 if( $userid ne $e->requestor->id ) {
1895 my $user = $e->retrieve_actor_user($userid) or return $e->event;
1896 unless($e->allowed('VIEW_CIRCULATIONS', $user->home_ou)) {
1898 # see if there is a friend link allowing circ.view perms
1899 my $allowed = OpenILS::Application::Actor::Friends->friend_perm_allowed(
1900 $e, $userid, $e->requestor->id, 'circ.view');
1901 return $e->event unless $allowed;
1905 my $count = $self->api_name =~ /count/;
1906 return _checked_out( $count, $e, $userid );
1910 my( $iscount, $e, $userid ) = @_;
1911 my $meth = 'open-ils.storage.actor.user.checked_out';
1912 $meth = "$meth.count" if $iscount;
1913 return $U->storagereq($meth, $userid);
1917 sub _checked_out_WHAT {
1918 my( $iscount, $e, $userid ) = @_;
1920 my $circs = $e->search_action_circulation(
1921 { usr => $userid, stop_fines => undef });
1923 my $mcircs = $e->search_action_circulation(
1926 checkin_time => undef,
1927 xact_finish => undef,
1931 push( @$circs, @$mcircs );
1933 my $parser = DateTime::Format::ISO8601->new;
1935 # split the circs up into overdue and not-overdue circs
1937 for my $c (@$circs) {
1938 if( $c->due_date ) {
1939 my $due_dt = $parser->parse_datetime( cleanse_ISO8601( $c->due_date ) );
1940 my $due = $due_dt->epoch;
1941 if ($due < DateTime->today->epoch) {
1942 push @overdue, $c->id;
1951 # grab all of the lost, claims-returned, and longoverdue circs
1952 #my $open = $e->search_action_circulation(
1953 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1956 # these items have stop_fines, but no xact_finish, so money
1957 # is owed on them and they have not been checked in
1958 my $open = $e->search_action_circulation(
1961 stop_fines => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] },
1962 xact_finish => undef,
1963 checkin_time => undef,
1968 my( @lost, @cr, @lo );
1969 for my $c (@$open) {
1970 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1971 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1972 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1978 total => @$circs + @lost + @cr + @lo,
1979 out => scalar(@out),
1980 overdue => scalar(@overdue),
1981 lost => scalar(@lost),
1982 claims_returned => scalar(@cr),
1983 long_overdue => scalar(@lo)
1989 overdue => \@overdue,
1991 claims_returned => \@cr,
1992 long_overdue => \@lo
1998 __PACKAGE__->register_method(
1999 method => "checked_in_with_fines",
2000 api_name => "open-ils.actor.user.checked_in_with_fines",
2003 signature => q/@see open-ils.actor.user.checked_out/
2006 sub checked_in_with_fines {
2007 my( $self, $conn, $auth, $userid ) = @_;
2009 my $e = new_editor(authtoken=>$auth);
2010 return $e->event unless $e->checkauth;
2012 if( $userid ne $e->requestor->id ) {
2013 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
2016 # money is owed on these items and they are checked in
2017 my $open = $e->search_action_circulation(
2020 xact_finish => undef,
2021 checkin_time => { "!=" => undef },
2026 my( @lost, @cr, @lo );
2027 for my $c (@$open) {
2028 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
2029 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
2030 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
2035 claims_returned => \@cr,
2036 long_overdue => \@lo
2042 __PACKAGE__->register_method(
2043 method => "user_transaction_history",
2044 api_name => "open-ils.actor.user.transactions.history",
2046 notes => <<" NOTES");
2047 Returns a list of billable transactions for a user, optionally by type
2049 __PACKAGE__->register_method(
2050 method => "user_transaction_history",
2051 api_name => "open-ils.actor.user.transactions.history.have_charge",
2053 notes => <<" NOTES");
2054 Returns a list of billable transactions for a user that have an initial charge, optionally by type
2056 __PACKAGE__->register_method(
2057 method => "user_transaction_history",
2058 api_name => "open-ils.actor.user.transactions.history.have_balance",
2061 notes => <<" NOTES");
2062 Returns a list of billable transactions for a user that have a balance, optionally by type
2064 __PACKAGE__->register_method(
2065 method => "user_transaction_history",
2066 api_name => "open-ils.actor.user.transactions.history.still_open",
2068 notes => <<" NOTES");
2069 Returns a list of billable transactions for a user that are not finished
2071 __PACKAGE__->register_method(
2072 method => "user_transaction_history",
2073 api_name => "open-ils.actor.user.transactions.history.have_bill",
2076 notes => <<" NOTES");
2077 Returns a list of billable transactions for a user that has billings
2079 __PACKAGE__->register_method(
2080 method => "user_transaction_history",
2081 api_name => "open-ils.actor.user.transactions.history.ids",
2083 notes => <<" NOTES");
2084 Returns a list of billable transaction ids for a user, optionally by type
2086 __PACKAGE__->register_method(
2087 method => "user_transaction_history",
2088 api_name => "open-ils.actor.user.transactions.history.have_charge.ids",
2090 notes => <<" NOTES");
2091 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
2093 __PACKAGE__->register_method(
2094 method => "user_transaction_history",
2095 api_name => "open-ils.actor.user.transactions.history.have_balance.ids",
2098 notes => <<" NOTES");
2099 Returns a list of billable transaction ids for a user that have a balance, optionally by type
2101 __PACKAGE__->register_method(
2102 method => "user_transaction_history",
2103 api_name => "open-ils.actor.user.transactions.history.still_open.ids",
2105 notes => <<" NOTES");
2106 Returns a list of billable transaction ids for a user that are not finished
2108 __PACKAGE__->register_method(
2109 method => "user_transaction_history",
2110 api_name => "open-ils.actor.user.transactions.history.have_bill.ids",
2113 notes => <<" NOTES");
2114 Returns a list of billable transaction ids for a user that has billings
2116 __PACKAGE__->register_method(
2117 method => "user_transaction_history",
2118 api_name => "open-ils.actor.user.transactions.history.have_bill_or_payment",
2121 notes => <<" NOTES");
2122 Returns a list of billable transactions for a user that has non-zero-sum billings or at least 1 payment
2124 __PACKAGE__->register_method(
2125 method => "user_transaction_history",
2127 "open-ils.actor.user.transactions.history.have_bill_or_payment.ids",
2130 notes => <<" NOTES");
2131 Returns a list of billable transaction ids for a user that has non-zero-sum billings or at least 1 payment
2136 sub user_transaction_history {
2137 my( $self, $conn, $auth, $userid, $type, $filter ) = @_;
2140 # run inside of a transaction to prevent replication delays
2141 my $e = new_editor(authtoken=>$auth);
2142 return $e->die_event unless $e->checkauth;
2144 if( $e->requestor->id ne $userid ) {
2145 return $e->die_event
2146 unless $e->allowed('VIEW_USER_TRANSACTIONS');
2149 my $api = $self->api_name;
2150 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
2152 my $mbts = $e->search_money_billable_transaction_summary(
2154 { usr => $userid, @xact_finish, %$filter },
2155 { order_by => { mbt => 'xact_start DESC' } }
2159 if(defined($type)) {
2160 @$mbts = grep { $_->xact_type eq $type } @$mbts;
2163 if($api =~ /have_bill_or_payment/o) {
2165 # transactions that have a non-zero sum across all billings or at least 1 payment
2167 int($_->balance_owed * 100) != 0 ||
2168 defined($_->last_payment_ts) } @$mbts;
2170 } elsif( $api =~ /have_balance/o) {
2172 # transactions that have a non-zero overall balance
2173 @$mbts = grep { int($_->balance_owed * 100) != 0 } @$mbts;
2175 } elsif( $api =~ /have_charge/o) {
2177 # transactions that have at least 1 billing, regardless of whether it was voided
2178 @$mbts = grep { defined($_->last_billing_ts) } @$mbts;
2180 } elsif( $api =~ /have_bill/o) {
2182 # transactions that have non-zero sum across all billings. This will exclude
2183 # xacts where all billings have been voided
2184 @$mbts = grep { int($_->total_owed * 100) != 0 } @$mbts;
2187 if ($api =~ /\.ids/) {
2188 return [map {$_->id} @$mbts];
2196 __PACKAGE__->register_method(
2197 method => "user_perms",
2198 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2200 notes => "Returns a list of permissions"
2204 my( $self, $client, $authtoken, $user ) = @_;
2206 my( $staff, $evt ) = $apputils->checkses($authtoken);
2207 return $evt if $evt;
2209 $user ||= $staff->id;
2211 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2215 return $apputils->simple_scalar_request(
2217 "open-ils.storage.permission.user_perms.atomic",
2221 __PACKAGE__->register_method(
2222 method => "retrieve_perms",
2223 api_name => "open-ils.actor.permissions.retrieve",
2224 notes => "Returns a list of permissions"
2226 sub retrieve_perms {
2227 my( $self, $client ) = @_;
2228 return $apputils->simple_scalar_request(
2230 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2231 { id => { '!=' => undef } }
2235 __PACKAGE__->register_method(
2236 method => "retrieve_groups",
2237 api_name => "open-ils.actor.groups.retrieve",
2238 notes => "Returns a list of user groups"
2240 sub retrieve_groups {
2241 my( $self, $client ) = @_;
2242 return new_editor()->retrieve_all_permission_grp_tree();
2245 __PACKAGE__->register_method(
2246 method => "retrieve_org_address",
2247 api_name => "open-ils.actor.org_unit.address.retrieve",
2248 notes => <<' NOTES');
2249 Returns an org_unit address by ID
2250 @param An org_address ID
2252 sub retrieve_org_address {
2253 my( $self, $client, $id ) = @_;
2254 return $apputils->simple_scalar_request(
2256 "open-ils.cstore.direct.actor.org_address.retrieve",
2261 __PACKAGE__->register_method(
2262 method => "retrieve_groups_tree",
2263 api_name => "open-ils.actor.groups.tree.retrieve",
2264 notes => "Returns a list of user groups"
2267 sub retrieve_groups_tree {
2268 my( $self, $client ) = @_;
2269 return new_editor()->search_permission_grp_tree(
2274 flesh_fields => { pgt => ["children"] },
2275 order_by => { pgt => 'name'}
2282 __PACKAGE__->register_method(
2283 method => "add_user_to_groups",
2284 api_name => "open-ils.actor.user.set_groups",
2285 notes => "Adds a user to one or more permission groups"
2288 sub add_user_to_groups {
2289 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2291 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2292 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2293 return $evt if $evt;
2295 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2296 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2297 return $evt if $evt;
2299 $apputils->simplereq(
2301 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2303 for my $group (@$groups) {
2304 my $link = Fieldmapper::permission::usr_grp_map->new;
2306 $link->usr($userid);
2308 my $id = $apputils->simplereq(
2310 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2316 __PACKAGE__->register_method(
2317 method => "get_user_perm_groups",
2318 api_name => "open-ils.actor.user.get_groups",
2319 notes => "Retrieve a user's permission groups."
2323 sub get_user_perm_groups {
2324 my( $self, $client, $authtoken, $userid ) = @_;
2326 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2327 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2328 return $evt if $evt;
2330 return $apputils->simplereq(
2332 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2336 __PACKAGE__->register_method(
2337 method => "get_user_work_ous",
2338 api_name => "open-ils.actor.user.get_work_ous",
2339 notes => "Retrieve a user's work org units."
2342 __PACKAGE__->register_method(
2343 method => "get_user_work_ous",
2344 api_name => "open-ils.actor.user.get_work_ous.ids",
2345 notes => "Retrieve a user's work org units."
2348 sub get_user_work_ous {
2349 my( $self, $client, $auth, $userid ) = @_;
2350 my $e = new_editor(authtoken=>$auth);
2351 return $e->event unless $e->checkauth;
2352 $userid ||= $e->requestor->id;
2354 if($e->requestor->id != $userid) {
2355 my $user = $e->retrieve_actor_user($userid)
2356 or return $e->event;
2357 return $e->event unless $e->allowed('ASSIGN_WORK_ORG_UNIT', $user->home_ou);
2360 return $e->search_permission_usr_work_ou_map({usr => $userid})
2361 unless $self->api_name =~ /.ids$/;
2363 # client just wants a list of org IDs
2364 return $U->get_user_work_ou_ids($e, $userid);
2369 __PACKAGE__->register_method(
2370 method => 'register_workstation',
2371 api_name => 'open-ils.actor.workstation.register.override',
2372 signature => q/@see open-ils.actor.workstation.register/
2375 __PACKAGE__->register_method(
2376 method => 'register_workstation',
2377 api_name => 'open-ils.actor.workstation.register',
2379 Registers a new workstion in the system
2380 @param authtoken The login session key
2381 @param name The name of the workstation id
2382 @param owner The org unit that owns this workstation
2383 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2384 if the name is already in use.
2388 sub register_workstation {
2389 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2391 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2392 return $e->die_event unless $e->checkauth;
2393 return $e->die_event unless $e->allowed('REGISTER_WORKSTATION', $owner);
2394 my $existing = $e->search_actor_workstation({name => $name})->[0];
2398 if( $self->api_name =~ /override/o ) {
2399 # workstation with the given name exists.
2401 if($owner ne $existing->owning_lib) {
2402 # if necessary, update the owning_lib of the workstation
2404 $logger->info("changing owning lib of workstation ".$existing->id.
2405 " from ".$existing->owning_lib." to $owner");
2406 return $e->die_event unless
2407 $e->allowed('UPDATE_WORKSTATION', $existing->owning_lib);
2409 return $e->die_event unless $e->allowed('UPDATE_WORKSTATION', $owner);
2411 $existing->owning_lib($owner);
2412 return $e->die_event unless $e->update_actor_workstation($existing);
2418 "attempt to register an existing workstation. returning existing ID");
2421 return $existing->id;
2424 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2428 my $ws = Fieldmapper::actor::workstation->new;
2429 $ws->owning_lib($owner);
2431 $e->create_actor_workstation($ws) or return $e->die_event;
2433 return $ws->id; # note: editor sets the id on the new object for us
2436 __PACKAGE__->register_method(
2437 method => 'workstation_list',
2438 api_name => 'open-ils.actor.workstation.list',
2440 Returns a list of workstations registered at the given location
2441 @param authtoken The login session key
2442 @param ids A list of org_unit.id's for the workstation owners
2446 sub workstation_list {
2447 my( $self, $conn, $authtoken, @orgs ) = @_;
2449 my $e = new_editor(authtoken=>$authtoken);
2450 return $e->event unless $e->checkauth;
2455 unless $e->allowed('REGISTER_WORKSTATION', $o);
2456 $results{$o} = $e->search_actor_workstation({owning_lib=>$o});
2462 __PACKAGE__->register_method(
2463 method => 'fetch_patron_note',
2464 api_name => 'open-ils.actor.note.retrieve.all',
2467 Returns a list of notes for a given user
2468 Requestor must have VIEW_USER permission if pub==false and
2469 @param authtoken The login session key
2470 @param args Hash of params including
2471 patronid : the patron's id
2472 pub : true if retrieving only public notes
2476 sub fetch_patron_note {
2477 my( $self, $conn, $authtoken, $args ) = @_;
2478 my $patronid = $$args{patronid};
2480 my($reqr, $evt) = $U->checkses($authtoken);
2481 return $evt if $evt;
2484 ($patron, $evt) = $U->fetch_user($patronid);
2485 return $evt if $evt;
2488 if( $patronid ne $reqr->id ) {
2489 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2490 return $evt if $evt;
2492 return $U->cstorereq(
2493 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2494 { usr => $patronid, pub => 't' } );
2497 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2498 return $evt if $evt;
2500 return $U->cstorereq(
2501 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2504 __PACKAGE__->register_method(
2505 method => 'create_user_note',
2506 api_name => 'open-ils.actor.note.create',
2508 Creates a new note for the given user
2509 @param authtoken The login session key
2510 @param note The note object
2513 sub create_user_note {
2514 my( $self, $conn, $authtoken, $note ) = @_;
2515 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2516 return $e->die_event unless $e->checkauth;
2518 my $user = $e->retrieve_actor_user($note->usr)
2519 or return $e->die_event;
2521 return $e->die_event unless
2522 $e->allowed('UPDATE_USER',$user->home_ou);
2524 $note->creator($e->requestor->id);
2525 $e->create_actor_usr_note($note) or return $e->die_event;
2531 __PACKAGE__->register_method(
2532 method => 'delete_user_note',
2533 api_name => 'open-ils.actor.note.delete',
2535 Deletes a note for the given user
2536 @param authtoken The login session key
2537 @param noteid The note id
2540 sub delete_user_note {
2541 my( $self, $conn, $authtoken, $noteid ) = @_;
2543 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2544 return $e->die_event unless $e->checkauth;
2545 my $note = $e->retrieve_actor_usr_note($noteid)
2546 or return $e->die_event;
2547 my $user = $e->retrieve_actor_user($note->usr)
2548 or return $e->die_event;
2549 return $e->die_event unless
2550 $e->allowed('UPDATE_USER', $user->home_ou);
2552 $e->delete_actor_usr_note($note) or return $e->die_event;
2558 __PACKAGE__->register_method(
2559 method => 'update_user_note',
2560 api_name => 'open-ils.actor.note.update',
2562 @param authtoken The login session key
2563 @param note The note
2567 sub update_user_note {
2568 my( $self, $conn, $auth, $note ) = @_;
2569 my $e = new_editor(authtoken=>$auth, xact=>1);
2570 return $e->event unless $e->checkauth;
2571 my $patron = $e->retrieve_actor_user($note->usr)
2572 or return $e->event;
2573 return $e->event unless
2574 $e->allowed('UPDATE_USER', $patron->home_ou);
2575 $e->update_actor_user_note($note)
2576 or return $e->event;
2583 __PACKAGE__->register_method(
2584 method => 'create_closed_date',
2585 api_name => 'open-ils.actor.org_unit.closed_date.create',
2587 Creates a new closing entry for the given org_unit
2588 @param authtoken The login session key
2589 @param note The closed_date object
2592 sub create_closed_date {
2593 my( $self, $conn, $authtoken, $cd ) = @_;
2595 my( $user, $evt ) = $U->checkses($authtoken);
2596 return $evt if $evt;
2598 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2599 return $evt if $evt;
2601 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2603 my $id = $U->storagereq(
2604 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2605 return $U->DB_UPDATE_FAILED($cd) unless $id;
2610 __PACKAGE__->register_method(
2611 method => 'delete_closed_date',
2612 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2614 Deletes a closing entry for the given org_unit
2615 @param authtoken The login session key
2616 @param noteid The close_date id
2619 sub delete_closed_date {
2620 my( $self, $conn, $authtoken, $cd ) = @_;
2622 my( $user, $evt ) = $U->checkses($authtoken);
2623 return $evt if $evt;
2626 ($cd_obj, $evt) = fetch_closed_date($cd);
2627 return $evt if $evt;
2629 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2630 return $evt if $evt;
2632 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2634 my $stat = $U->storagereq(
2635 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2636 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2641 __PACKAGE__->register_method(
2642 method => 'usrname_exists',
2643 api_name => 'open-ils.actor.username.exists',
2644 signature => 'Returns 1 if the requested username exists, returns 0 otherwise'
2647 sub usrname_exists {
2648 my( $self, $conn, $auth, $usrname ) = @_;
2649 my $e = new_editor(authtoken=>$auth);
2650 return $e->event unless $e->checkauth;
2651 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2652 return $$a[0] if $a and @$a;
2656 __PACKAGE__->register_method(
2657 method => 'barcode_exists',
2658 api_name => 'open-ils.actor.barcode.exists',
2660 signature => 'Returns 1 if the requested barcode exists, returns 0 otherwise'
2663 sub barcode_exists {
2664 my( $self, $conn, $auth, $barcode ) = @_;
2665 my $e = new_editor(authtoken=>$auth);
2666 return $e->event unless $e->checkauth;
2667 my $card = $e->search_actor_card({barcode => $barcode});
2673 #return undef unless @$card;
2674 #return $card->[0]->usr;
2678 __PACKAGE__->register_method(
2679 method => 'retrieve_net_levels',
2680 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2683 sub retrieve_net_levels {
2684 my( $self, $conn, $auth ) = @_;
2685 my $e = new_editor(authtoken=>$auth);
2686 return $e->event unless $e->checkauth;
2687 return $e->retrieve_all_config_net_access_level();
2690 # Retain the old typo API name just in case
2691 __PACKAGE__->register_method(
2692 method => 'fetch_org_by_shortname',
2693 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2695 __PACKAGE__->register_method(
2696 method => 'fetch_org_by_shortname',
2697 api_name => 'open-ils.actor.org_unit.retrieve_by_shortname',
2699 sub fetch_org_by_shortname {
2700 my( $self, $conn, $sname ) = @_;
2701 my $e = new_editor();
2702 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2703 return $e->event unless $org;
2708 __PACKAGE__->register_method(
2709 method => 'session_home_lib',
2710 api_name => 'open-ils.actor.session.home_lib',
2713 sub session_home_lib {
2714 my( $self, $conn, $auth ) = @_;
2715 my $e = new_editor(authtoken=>$auth);
2716 return undef unless $e->checkauth;
2717 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2718 return $org->shortname;
2721 __PACKAGE__->register_method(
2722 method => 'session_safe_token',
2723 api_name => 'open-ils.actor.session.safe_token',
2725 Returns a hashed session ID that is safe for export to the world.
2726 This safe token will expire after 1 hour of non-use.
2727 @param auth Active authentication token
2731 sub session_safe_token {
2732 my( $self, $conn, $auth ) = @_;
2733 my $e = new_editor(authtoken=>$auth);
2734 return undef unless $e->checkauth;
2736 my $safe_token = md5_hex($auth);
2738 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2740 # Add more like the following if needed...
2742 "safe-token-home_lib-shortname-$safe_token",
2743 $e->retrieve_actor_org_unit(
2744 $e->requestor->home_ou
2753 __PACKAGE__->register_method(
2754 method => 'safe_token_home_lib',
2755 api_name => 'open-ils.actor.safe_token.home_lib.shortname',
2757 Returns the home library shortname from the session
2758 asscociated with a safe token from generated by
2759 open-ils.actor.session.safe_token.
2760 @param safe_token Active safe token
2764 sub safe_token_home_lib {
2765 my( $self, $conn, $safe_token ) = @_;
2767 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2768 return $cache->get_cache( 'safe-token-home_lib-shortname-'. $safe_token );
2773 __PACKAGE__->register_method(
2774 method => 'slim_tree',
2775 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2778 my $tree = new_editor()->search_actor_org_unit(
2780 {"parent_ou" => undef },
2783 flesh_fields => { aou => ['children'] },
2784 order_by => { aou => 'name'},
2785 select => { aou => ["id","shortname", "name"]},
2790 return trim_tree($tree);
2796 return undef unless $tree;
2798 code => $tree->shortname,
2799 name => $tree->name,
2801 if( $tree->children and @{$tree->children} ) {
2802 $htree->{children} = [];
2803 for my $c (@{$tree->children}) {
2804 push( @{$htree->{children}}, trim_tree($c) );
2812 __PACKAGE__->register_method(
2813 method => "update_penalties",
2814 api_name => "open-ils.actor.user.penalties.update"
2817 sub update_penalties {
2818 my($self, $conn, $auth, $user_id) = @_;
2819 my $e = new_editor(authtoken=>$auth, xact => 1);
2820 return $e->die_event unless $e->checkauth;
2821 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
2822 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2823 my $evt = OpenILS::Utils::Penalty->calculate_penalties($e, $user_id, $e->requestor->ws_ou);
2824 return $evt if $evt;
2830 __PACKAGE__->register_method(
2831 method => "apply_penalty",
2832 api_name => "open-ils.actor.user.penalty.apply"
2836 my($self, $conn, $auth, $penalty) = @_;
2838 my $e = new_editor(authtoken=>$auth, xact => 1);
2839 return $e->die_event unless $e->checkauth;
2841 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2842 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2844 my $ptype = $e->retrieve_config_standing_penalty($penalty->standing_penalty) or return $e->die_event;
2847 (defined $ptype->org_depth) ?
2848 $U->org_unit_ancestor_at_depth($penalty->org_unit, $ptype->org_depth) :
2851 $penalty->org_unit($ctx_org);
2852 $penalty->staff($e->requestor->id);
2853 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
2856 return $penalty->id;
2859 __PACKAGE__->register_method(
2860 method => "remove_penalty",
2861 api_name => "open-ils.actor.user.penalty.remove"
2864 sub remove_penalty {
2865 my($self, $conn, $auth, $penalty) = @_;
2866 my $e = new_editor(authtoken=>$auth, xact => 1);
2867 return $e->die_event unless $e->checkauth;
2868 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2869 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2871 $e->delete_actor_user_standing_penalty($penalty) or return $e->die_event;
2876 __PACKAGE__->register_method(
2877 method => "update_penalty_note",
2878 api_name => "open-ils.actor.user.penalty.note.update"
2881 sub update_penalty_note {
2882 my($self, $conn, $auth, $penalty_ids, $note) = @_;
2883 my $e = new_editor(authtoken=>$auth, xact => 1);
2884 return $e->die_event unless $e->checkauth;
2885 for my $penalty_id (@$penalty_ids) {
2886 my $penalty = $e->search_actor_user_standing_penalty( { id => $penalty_id } )->[0];
2887 if (! $penalty ) { return $e->die_event; }
2888 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2889 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2891 $penalty->note( $note ); $penalty->ischanged( 1 );
2893 $e->update_actor_user_standing_penalty($penalty) or return $e->die_event;
2899 __PACKAGE__->register_method(
2900 method => "ranged_penalty_thresholds",
2901 api_name => "open-ils.actor.grp_penalty_threshold.ranged.retrieve",
2905 sub ranged_penalty_thresholds {
2906 my($self, $conn, $auth, $context_org) = @_;
2907 my $e = new_editor(authtoken=>$auth);
2908 return $e->event unless $e->checkauth;
2909 return $e->event unless $e->allowed('VIEW_GROUP_PENALTY_THRESHOLD', $context_org);
2910 my $list = $e->search_permission_grp_penalty_threshold([
2911 {org_unit => $U->get_org_ancestors($context_org)},
2912 {order_by => {pgpt => 'id'}}
2914 $conn->respond($_) for @$list;
2920 __PACKAGE__->register_method(
2921 method => "user_retrieve_fleshed_by_id",
2923 api_name => "open-ils.actor.user.fleshed.retrieve",
2926 sub user_retrieve_fleshed_by_id {
2927 my( $self, $client, $auth, $user_id, $fields ) = @_;
2928 my $e = new_editor(authtoken => $auth);
2929 return $e->event unless $e->checkauth;
2931 if( $e->requestor->id != $user_id ) {
2932 return $e->event unless $e->allowed('VIEW_USER');
2938 "standing_penalties",
2942 "stat_cat_entries" ];
2943 return new_flesh_user($user_id, $fields, $e);
2947 sub new_flesh_user {
2950 my $fields = shift || [];
2953 my $fetch_penalties = 0;
2954 if(grep {$_ eq 'standing_penalties'} @$fields) {
2955 $fields = [grep {$_ ne 'standing_penalties'} @$fields];
2956 $fetch_penalties = 1;
2959 my $user = $e->retrieve_actor_user(
2964 "flesh_fields" => { "au" => $fields }
2967 ) or return $e->event;
2970 if( grep { $_ eq 'addresses' } @$fields ) {
2972 $user->addresses([]) unless @{$user->addresses};
2973 # don't expose "replaced" addresses by default
2974 $user->addresses([grep {$_->id >= 0} @{$user->addresses}]);
2976 if( ref $user->billing_address ) {
2977 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2978 push( @{$user->addresses}, $user->billing_address );
2982 if( ref $user->mailing_address ) {
2983 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2984 push( @{$user->addresses}, $user->mailing_address );
2989 if($fetch_penalties) {
2990 # grab the user penalties ranged for this location
2991 $user->standing_penalties(
2992 $e->search_actor_user_standing_penalty([
2995 {stop_date => undef},
2996 {stop_date => {'>' => 'now'}}
2998 org_unit => $U->get_org_ancestors($e->requestor->ws_ou)
3001 flesh_fields => {ausp => ['standing_penalty']}
3008 $user->clear_passwd();
3015 __PACKAGE__->register_method(
3016 method => "user_retrieve_parts",
3017 api_name => "open-ils.actor.user.retrieve.parts",
3020 sub user_retrieve_parts {
3021 my( $self, $client, $auth, $user_id, $fields ) = @_;
3022 my $e = new_editor(authtoken => $auth);
3023 return $e->event unless $e->checkauth;
3024 if( $e->requestor->id != $user_id ) {
3025 return $e->event unless $e->allowed('VIEW_USER');
3028 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3029 push(@resp, $user->$_()) for(@$fields);
3035 __PACKAGE__->register_method(
3036 method => 'user_opt_in_enabled',
3037 api_name => 'open-ils.actor.user.org_unit_opt_in.enabled',
3038 signature => '@return 1 if user opt-in is globally enabled, 0 otherwise.'
3041 sub user_opt_in_enabled {
3042 my($self, $conn) = @_;
3043 my $sc = OpenSRF::Utils::SettingsClient->new;
3044 return 1 if lc($sc->config_value(share => user => 'opt_in')) eq 'true';
3049 __PACKAGE__->register_method(
3050 method => 'user_opt_in_at_org',
3051 api_name => 'open-ils.actor.user.org_unit_opt_in.check',
3053 @param $auth The auth token
3054 @param user_id The ID of the user to test
3055 @return 1 if the user has opted in at the specified org,
3056 event on error, and 0 otherwise. /
3058 sub user_opt_in_at_org {
3059 my($self, $conn, $auth, $user_id) = @_;
3061 # see if we even need to enforce the opt-in value
3062 return 1 unless user_opt_in_enabled($self);
3064 my $e = new_editor(authtoken => $auth);
3065 return $e->event unless $e->checkauth;
3066 my $org_id = $e->requestor->ws_ou;
3068 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3069 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3071 # user is automatically opted-in at the home org
3072 return 1 if $user->home_ou eq $org_id;
3074 my $vals = $e->search_actor_usr_org_unit_opt_in(
3075 {org_unit=>$org_id, usr=>$user_id},{idlist=>1});
3081 __PACKAGE__->register_method(
3082 method => 'create_user_opt_in_at_org',
3083 api_name => 'open-ils.actor.user.org_unit_opt_in.create',
3085 @param $auth The auth token
3086 @param user_id The ID of the user to test
3087 @return The ID of the newly created object, event on error./
3090 sub create_user_opt_in_at_org {
3091 my($self, $conn, $auth, $user_id) = @_;
3093 my $e = new_editor(authtoken => $auth, xact=>1);
3094 return $e->die_event unless $e->checkauth;
3095 my $org_id = $e->requestor->ws_ou;
3097 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3098 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3100 my $opt_in = Fieldmapper::actor::usr_org_unit_opt_in->new;
3102 $opt_in->org_unit($org_id);
3103 $opt_in->usr($user_id);
3104 $opt_in->staff($e->requestor->id);
3105 $opt_in->opt_in_ts('now');
3106 $opt_in->opt_in_ws($e->requestor->wsid);
3108 $opt_in = $e->create_actor_usr_org_unit_opt_in($opt_in)
3109 or return $e->die_event;
3117 __PACKAGE__->register_method (
3118 method => 'retrieve_org_hours',
3119 api_name => 'open-ils.actor.org_unit.hours_of_operation.retrieve',
3121 Returns the hours of operation for a specified org unit
3122 @param authtoken The login session key
3123 @param org_id The org_unit ID
3127 sub retrieve_org_hours {
3128 my($self, $conn, $auth, $org_id) = @_;
3129 my $e = new_editor(authtoken => $auth);
3130 return $e->die_event unless $e->checkauth;
3131 $org_id ||= $e->requestor->ws_ou;
3132 return $e->retrieve_actor_org_unit_hours_of_operation($org_id);
3136 __PACKAGE__->register_method (
3137 method => 'verify_user_password',
3138 api_name => 'open-ils.actor.verify_user_password',
3140 Given a barcode or username and the MD5 encoded password,
3141 returns 1 if the password is correct. Returns 0 otherwise.
3145 sub verify_user_password {
3146 my($self, $conn, $auth, $barcode, $username, $password) = @_;
3147 my $e = new_editor(authtoken => $auth);
3148 return $e->die_event unless $e->checkauth;
3150 my $user_by_barcode;
3151 my $user_by_username;
3153 my $card = $e->search_actor_card([
3154 {barcode => $barcode},
3155 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0] or return 0;
3156 $user_by_barcode = $card->usr;
3157 $user = $user_by_barcode;
3160 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return 0;
3161 $user = $user_by_username;
3163 return 0 if (!$user);
3164 return 0 if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3165 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3166 return 1 if $user->passwd eq $password;
3170 __PACKAGE__->register_method (
3171 method => 'retrieve_usr_id_via_barcode_or_usrname',
3172 api_name => "open-ils.actor.user.retrieve_id_by_barcode_or_username",
3174 Given a barcode or username returns the id for the user or
3179 sub retrieve_usr_id_via_barcode_or_usrname {
3180 my($self, $conn, $auth, $barcode, $username) = @_;
3181 my $e = new_editor(authtoken => $auth);
3182 return $e->die_event unless $e->checkauth;
3183 my $id_as_barcode= OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.actor' => app_settings => 'id_as_barcode');
3185 my $user_by_barcode;
3186 my $user_by_username;
3187 $logger->info("$id_as_barcode is the ID as BARCODE");
3189 my $card = $e->search_actor_card([
3190 {barcode => $barcode},
3191 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0];
3192 if ($id_as_barcode =~ /^t/i) {
3194 $user = $e->retrieve_actor_user($barcode);
3195 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$user);
3197 $user_by_barcode = $card->usr;
3198 $user = $user_by_barcode;
3201 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$card);
3202 $user_by_barcode = $card->usr;
3203 $user = $user_by_barcode;
3208 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return OpenILS::Event->new( 'ACTOR_USR_NOT_FOUND' );
3210 $user = $user_by_username;
3212 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if (!$user);
3213 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3214 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3219 __PACKAGE__->register_method (
3220 method => 'merge_users',
3221 api_name => 'open-ils.actor.user.merge',
3224 Given a list of source users and destination user, transfer all data from the source
3225 to the dest user and delete the source user. All user related data is
3226 transferred, including circulations, holds, bookbags, etc.
3232 my($self, $conn, $auth, $master_id, $user_ids, $options) = @_;
3233 my $e = new_editor(xact => 1, authtoken => $auth);
3234 return $e->die_event unless $e->checkauth;
3236 # disallow the merge if any subordinate accounts are in collections
3237 my $colls = $e->search_money_collections_tracker({usr => $user_ids}, {idlist => 1});
3238 return OpenILS::Event->new('MERGED_USER_IN_COLLECTIONS', payload => $user_ids) if @$colls;
3240 my $master_user = $e->retrieve_actor_user($master_id) or return $e->die_event;
3241 my $del_addrs = ($U->ou_ancestor_setting_value(
3242 $master_user->home_ou, 'circ.user_merge.delete_addresses', $e)) ? 't' : 'f';
3243 my $del_cards = ($U->ou_ancestor_setting_value(
3244 $master_user->home_ou, 'circ.user_merge.delete_cards', $e)) ? 't' : 'f';
3245 my $deactivate_cards = ($U->ou_ancestor_setting_value(
3246 $master_user->home_ou, 'circ.user_merge.deactivate_cards', $e)) ? 't' : 'f';
3248 for my $src_id (@$user_ids) {
3249 my $src_user = $e->retrieve_actor_user($src_id) or return $e->die_event;
3251 return $e->die_event unless $e->allowed('MERGE_USERS', $src_user->home_ou);
3252 if($src_user->home_ou ne $master_user->home_ou) {
3253 return $e->die_event unless $e->allowed('MERGE_USERS', $master_user->home_ou);
3256 return $e->die_event unless
3257 $e->json_query({from => [
3272 __PACKAGE__->register_method (
3273 method => 'approve_user_address',
3274 api_name => 'open-ils.actor.user.pending_address.approve',
3281 sub approve_user_address {
3282 my($self, $conn, $auth, $addr) = @_;
3283 my $e = new_editor(xact => 1, authtoken => $auth);
3284 return $e->die_event unless $e->checkauth;
3286 # if the caller passes an address object, assume they want to
3287 # update it first before approving it
3288 $e->update_actor_user_address($addr) or return $e->die_event;
3290 $addr = $e->retrieve_actor_user_address($addr) or return $e->die_event;
3292 my $user = $e->retrieve_actor_user($addr->usr);
3293 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3294 my $result = $e->json_query({from => ['actor.approve_pending_address', $addr->id]})->[0]
3295 or return $e->die_event;
3297 return [values %$result]->[0];
3301 __PACKAGE__->register_method (
3302 method => 'retrieve_friends',
3303 api_name => 'open-ils.actor.friends.retrieve',
3306 returns { confirmed: [], pending_out: [], pending_in: []}
3307 pending_out are users I'm requesting friendship with
3308 pending_in are users requesting friendship with me
3313 sub retrieve_friends {
3314 my($self, $conn, $auth, $user_id, $options) = @_;
3315 my $e = new_editor(authtoken => $auth);
3316 return $e->event unless $e->checkauth;
3317 $user_id ||= $e->requestor->id;
3319 if($user_id != $e->requestor->id) {
3320 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3321 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3324 return OpenILS::Application::Actor::Friends->retrieve_friends(
3325 $e, $user_id, $options);
3330 __PACKAGE__->register_method (
3331 method => 'apply_friend_perms',
3332 api_name => 'open-ils.actor.friends.perms.apply',
3338 sub apply_friend_perms {
3339 my($self, $conn, $auth, $user_id, $delegate_id, @perms) = @_;
3340 my $e = new_editor(authtoken => $auth, xact => 1);
3341 return $e->event unless $e->checkauth;
3343 if($user_id != $e->requestor->id) {
3344 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3345 return $e->die_event unless $e->allowed('VIEW_USER', $user->home_ou);
3348 for my $perm (@perms) {
3350 OpenILS::Application::Actor::Friends->apply_friend_perm(
3351 $e, $user_id, $delegate_id, $perm);
3352 return $evt if $evt;
3360 __PACKAGE__->register_method (
3361 method => 'update_user_pending_address',
3362 api_name => 'open-ils.actor.user.address.pending.cud'
3365 sub update_user_pending_address {
3366 my($self, $conn, $auth, $addr) = @_;
3367 my $e = new_editor(authtoken => $auth, xact => 1);
3368 return $e->event unless $e->checkauth;
3370 if($addr->usr != $e->requestor->id) {
3371 my $user = $e->retrieve_actor_user($addr->usr) or return $e->die_event;
3372 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3376 $e->create_actor_user_address($addr) or return $e->die_event;
3377 } elsif($addr->isdeleted) {
3378 $e->delete_actor_user_address($addr) or return $e->die_event;
3380 $e->update_actor_user_address($addr) or return $e->die_event;
3388 __PACKAGE__->register_method (
3389 method => 'user_events',
3390 api_name => 'open-ils.actor.user.events.circ',
3393 __PACKAGE__->register_method (
3394 method => 'user_events',
3395 api_name => 'open-ils.actor.user.events.ahr',
3400 my($self, $conn, $auth, $user_id, $filters) = @_;
3401 my $e = new_editor(authtoken => $auth);
3402 return $e->event unless $e->checkauth;
3404 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3405 my $user_field = 'usr';
3408 $filters->{target} = {
3409 select => { $obj_type => ['id'] },
3411 where => {usr => $user_id}
3414 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3415 if($e->requestor->id != $user_id) {
3416 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3419 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3420 my $req = $ses->request('open-ils.trigger.events_by_target',
3421 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3423 while(my $resp = $req->recv) {
3424 my $val = $resp->content;
3425 my $tgt = $val->target;
3427 if($obj_type eq 'circ') {
3428 $tgt->target_copy($e->retrieve_asset_copy($tgt->target_copy));
3430 } elsif($obj_type eq 'ahr') {
3431 $tgt->current_copy($e->retrieve_asset_copy($tgt->current_copy))
3432 if $tgt->current_copy;
3435 $conn->respond($val) if $val;
3441 __PACKAGE__->register_method (
3442 method => 'copy_events',
3443 api_name => 'open-ils.actor.copy.events.circ',
3446 __PACKAGE__->register_method (
3447 method => 'copy_events',
3448 api_name => 'open-ils.actor.copy.events.ahr',
3453 my($self, $conn, $auth, $copy_id, $filters) = @_;
3454 my $e = new_editor(authtoken => $auth);
3455 return $e->event unless $e->checkauth;
3457 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3459 my $copy = $e->retrieve_asset_copy($copy_id) or return $e->event;
3461 my $copy_field = 'target_copy';
3462 $copy_field = 'current_copy' if $obj_type eq 'ahr';
3465 $filters->{target} = {
3466 select => { $obj_type => ['id'] },
3468 where => {$copy_field => $copy_id}
3472 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3473 my $req = $ses->request('open-ils.trigger.events_by_target',
3474 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3476 while(my $resp = $req->recv) {
3477 my $val = $resp->content;
3478 my $tgt = $val->target;
3480 my $user = $e->retrieve_actor_user($tgt->usr);
3481 if($e->requestor->id != $user->id) {
3482 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3485 $tgt->$copy_field($copy);
3488 $conn->respond($val) if $val;
3497 __PACKAGE__->register_method (
3498 method => 'update_events',
3499 api_name => 'open-ils.actor.user.event.cancel.batch',
3502 __PACKAGE__->register_method (
3503 method => 'update_events',
3504 api_name => 'open-ils.actor.user.event.reset.batch',
3509 my($self, $conn, $auth, $event_ids) = @_;
3510 my $e = new_editor(xact => 1, authtoken => $auth);
3511 return $e->die_event unless $e->checkauth;
3514 for my $id (@$event_ids) {
3516 # do a little dance to determine what user we are ultimately affecting
3517 my $event = $e->retrieve_action_trigger_event([
3520 flesh_fields => {atev => ['event_def'], atevdef => ['hook']}
3522 ]) or return $e->die_event;
3525 if($event->event_def->hook->core_type eq 'circ') {
3526 $user_id = $e->retrieve_action_circulation($event->target)->usr;
3527 } elsif($event->event_def->hook->core_type eq 'ahr') {
3528 $user_id = $e->retrieve_action_hold_request($event->target)->usr;
3533 my $user = $e->retrieve_actor_user($user_id);
3534 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3536 if($self->api_name =~ /cancel/) {
3537 $event->state('invalid');
3538 } elsif($self->api_name =~ /reset/) {
3539 $event->clear_start_time;
3540 $event->clear_update_time;
3541 $event->state('pending');
3544 $e->update_action_trigger_event($event) or return $e->die_event;
3545 $conn->respond({maximum => scalar(@$event_ids), progress => $x++});
3549 return {complete => 1};
3553 __PACKAGE__->register_method (
3554 method => 'really_delete_user',
3555 api_name => 'open-ils.actor.user.delete',
3557 It anonymizes all personally identifiable information in actor.usr. By calling actor.usr_purge_data()
3558 it also purges related data from other tables, sometimes by transferring it to a designated destination user.
3559 The usrname field (along with first_given_name and family_name) is updated to id '-PURGED-' now().
3560 dest_usr_id is only required when deleting a user that performs staff functions.
3564 sub really_delete_user {
3565 my($self, $conn, $auth, $user_id, $dest_user_id) = @_;
3566 my $e = new_editor(authtoken => $auth, xact => 1);
3567 return $e->die_event unless $e->checkauth;
3568 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3569 return $e->die_event unless $e->allowed('DELETE_USER', $user->home_ou);
3570 my $stat = $e->json_query(
3571 {from => ['actor.usr_delete', $user_id, $dest_user_id]})->[0]
3572 or return $e->die_event;
3579 __PACKAGE__->register_method (
3580 method => 'user_payments',
3581 api_name => 'open-ils.actor.user.payments.retrieve',
3584 Returns all payments for a given user. Default order is newest payments first.
3585 @param auth Authentication token
3586 @param user_id The user ID
3587 @param filters An optional hash of filters, including limit, offset, and order_by definitions
3592 my($self, $conn, $auth, $user_id, $filters) = @_;
3595 my $e = new_editor(authtoken => $auth);
3596 return $e->die_event unless $e->checkauth;
3598 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3599 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS', $user->home_ou);
3601 # Find all payments for all transactions for user $user_id
3603 select => {mp => ['id']},
3608 select => {mbt => ['id']},
3610 where => {usr => $user_id}
3614 order_by => [{ # by default, order newest payments first
3616 field => 'payment_ts',
3621 for (qw/order_by limit offset/) {
3622 $query->{$_} = $filters->{$_} if defined $filters->{$_};
3625 if(defined $filters->{where}) {
3626 foreach (keys %{$filters->{where}}) {
3627 # don't allow the caller to expand the result set to other users
3628 $query->{where}->{$_} = $filters->{where}->{$_} unless $_ eq 'xact';
3632 my $payment_ids = $e->json_query($query);
3633 for my $pid (@$payment_ids) {
3634 my $pay = $e->retrieve_money_payment([
3639 mbt => ['summary', 'circulation', 'grocery'],
3640 circ => ['target_copy'],
3641 acp => ['call_number'],
3649 xact_type => $pay->xact->summary->xact_type,
3650 last_billing_type => $pay->xact->summary->last_billing_type,
3653 if($pay->xact->summary->xact_type eq 'circulation') {
3654 $resp->{barcode} = $pay->xact->circulation->target_copy->barcode;
3655 $resp->{title} = $U->record_to_mvr($pay->xact->circulation->target_copy->call_number->record)->title;
3658 $pay->xact($pay->xact->id); # de-flesh
3659 $conn->respond($resp);
3667 __PACKAGE__->register_method (
3668 method => 'negative_balance_users',
3669 api_name => 'open-ils.actor.users.negative_balance',
3672 Returns all users that have an overall negative balance
3673 @param auth Authentication token
3674 @param org_id The context org unit as an ID or list of IDs. This will be the home
3675 library of the user. If no org_unit is specified, no org unit filter is applied
3679 sub negative_balance_users {
3680 my($self, $conn, $auth, $org_id) = @_;
3682 my $e = new_editor(authtoken => $auth);
3683 return $e->die_event unless $e->checkauth;
3684 return $e->die_event unless $e->allowed('VIEW_USER', $org_id);
3688 mous => ['usr', 'balance_owed'],
3691 {column => 'last_billing_ts', transform => 'max', aggregate => 1},
3692 {column => 'last_payment_ts', transform => 'max', aggregate => 1},
3709 where => {'+mous' => {balance_owed => {'<' => 0}}}
3712 $query->{from}->{mous}->{au}->{filter}->{home_ou} = $org_id if $org_id;
3714 my $list = $e->json_query($query, {timeout => 600});
3716 for my $data (@$list) {
3718 usr => $e->retrieve_actor_user([$data->{usr}, {flesh => 1, flesh_fields => {au => ['card']}}]),
3719 balance_owed => $data->{balance_owed},
3720 last_billing_activity => max($data->{last_billing_ts}, $data->{last_payment_ts})