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",
319 desc => 'Update an existing user, or create a new one.',
321 { desc => 'Authentication token', type => 'string' },
322 { desc => 'Patron data object', type => 'object' }
324 return => {desc => 'A fleshed user object, event on error'}
329 my( $self, $client, $user_session, $patron ) = @_;
331 my $session = $apputils->start_db_session();
333 $logger->info($patron->isnew ? "Creating new patron..." : "Updating Patron: " . $patron->id);
335 my( $user_obj, $evt ) = $U->checkses($user_session);
338 $evt = check_group_perm($session, $user_obj, $patron);
342 # $new_patron is the patron in progress. $patron is the original patron
343 # passed in with the method. new_patron will change as the components
344 # of patron are added/updated.
348 # unflesh the real items on the patron
349 $patron->card( $patron->card->id ) if(ref($patron->card));
350 $patron->billing_address( $patron->billing_address->id )
351 if(ref($patron->billing_address));
352 $patron->mailing_address( $patron->mailing_address->id )
353 if(ref($patron->mailing_address));
355 # create/update the patron first so we can use his id
356 if($patron->isnew()) {
357 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
359 } else { $new_patron = $patron; }
361 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
364 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
367 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
370 # re-update the patron if anything has happened to him during this process
371 if($new_patron->ischanged()) {
372 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
376 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
379 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
382 $apputils->commit_db_session($session);
384 $evt = apply_invalid_addr_penalty($patron);
387 my $tses = OpenSRF::AppSession->create('open-ils.trigger');
389 $tses->request('open-ils.trigger.event.autocreate', 'au.create', $new_patron, $new_patron->home_ou);
391 $tses->request('open-ils.trigger.event.autocreate', 'au.update', $new_patron, $new_patron->home_ou);
394 return flesh_user($new_patron->id(), new_editor(requestor => $user_obj, xact => 1));
397 sub apply_invalid_addr_penalty {
399 my $e = new_editor(xact => 1);
401 # grab the invalid address penalty if set
402 my $penalties = OpenILS::Utils::Penalty->retrieve_usr_penalties($e, $patron->id, $patron->home_ou);
404 my ($addr_penalty) = grep
405 { $_->standing_penalty->name eq 'INVALID_PATRON_ADDRESS' } @$penalties;
407 # do we enforce invalid address penalty
408 my $enforce = $U->ou_ancestor_setting_value(
409 $patron->home_ou, 'circ.patron_invalid_address_apply_penalty') || 0;
411 my $addrs = $e->search_actor_user_address(
412 {usr => $patron->id, valid => 'f', id => {'>' => 0}}, {idlist => 1});
413 my $addr_count = scalar(@$addrs);
415 if($addr_count == 0 and $addr_penalty) {
417 # regardless of any settings, remove the penalty when the user has no invalid addresses
418 $e->delete_actor_user_standing_penalty($addr_penalty) or return $e->die_event;
421 } elsif($enforce and $addr_count > 0 and !$addr_penalty) {
423 my $ptype = $e->retrieve_config_standing_penalty(29) or return $e->die_event;
424 my $depth = $ptype->org_depth;
425 my $ctx_org = $U->org_unit_ancestor_at_depth($patron->home_ou, $depth) if defined $depth;
426 $ctx_org = $patron->home_ou unless defined $ctx_org;
428 my $penalty = Fieldmapper::actor::user_standing_penalty->new;
429 $penalty->usr($patron->id);
430 $penalty->org_unit($ctx_org);
431 $penalty->standing_penalty(OILS_PENALTY_INVALID_PATRON_ADDRESS);
433 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
447 return new_flesh_user($id, [
450 "standing_penalties",
454 "stat_cat_entries" ], $e );
462 # clone and clear stuff that would break the database
466 my $new_patron = $patron->clone;
468 $new_patron->clear_billing_address();
469 $new_patron->clear_mailing_address();
470 $new_patron->clear_addresses();
471 $new_patron->clear_card();
472 $new_patron->clear_cards();
473 $new_patron->clear_id();
474 $new_patron->clear_isnew();
475 $new_patron->clear_ischanged();
476 $new_patron->clear_isdeleted();
477 $new_patron->clear_stat_cat_entries();
478 $new_patron->clear_permissions();
479 $new_patron->clear_standing_penalties();
489 my $user_obj = shift;
491 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
492 return (undef, $evt) if $evt;
494 my $ex = $session->request(
495 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
497 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
500 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
502 my $id = $session->request(
503 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
504 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
506 $logger->info("Successfully created new user [$id] in DB");
508 return ( $session->request(
509 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
513 sub check_group_perm {
514 my( $session, $requestor, $patron ) = @_;
517 # first let's see if the requestor has
518 # priveleges to update this user in any way
519 if( ! $patron->isnew ) {
520 my $p = $session->request(
521 'open-ils.storage.direct.actor.user.retrieve', $patron->id )->gather(1);
523 # If we are the requestor (trying to update our own account)
524 # and we are not trying to change our profile, we're good
525 if( $p->id == $requestor->id and
526 $p->profile == $patron->profile ) {
531 $evt = group_perm_failed($session, $requestor, $p);
535 # They are allowed to edit this patron.. can they put the
536 # patron into the group requested?
537 $evt = group_perm_failed($session, $requestor, $patron);
543 sub group_perm_failed {
544 my( $session, $requestor, $patron ) = @_;
548 my $grpid = $patron->profile;
552 $logger->debug("user update looking for group perm for group $grpid");
553 $grp = $session->request(
554 'open-ils.storage.direct.permission.grp_tree.retrieve', $grpid )->gather(1);
555 return OpenILS::Event->new('PERMISSION_GRP_TREE_NOT_FOUND') unless $grp;
557 } while( !($perm = $grp->application_perm) and ($grpid = $grp->parent) );
559 $logger->info("user update checking perm $perm on user ".
560 $requestor->id." for update/create on user username=".$patron->usrname);
562 my $evt = $U->check_perms($requestor->id, $patron->home_ou, $perm);
570 my( $session, $patron, $user_obj, $noperm) = @_;
572 $logger->info("Updating patron ".$patron->id." in DB");
577 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
578 return (undef, $evt) if $evt;
581 # update the password by itself to avoid the password protection magic
582 if( $patron->passwd ) {
583 my $s = $session->request(
584 'open-ils.storage.direct.actor.user.remote_update',
585 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
586 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
587 $patron->clear_passwd;
590 if(!$patron->ident_type) {
591 $patron->clear_ident_type;
592 $patron->clear_ident_value;
595 $evt = verify_last_xact($session, $patron);
596 return (undef, $evt) if $evt;
598 my $stat = $session->request(
599 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
600 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
605 sub verify_last_xact {
606 my( $session, $patron ) = @_;
607 return undef unless $patron->id and $patron->id > 0;
608 my $p = $session->request(
609 'open-ils.storage.direct.actor.user.retrieve', $patron->id)->gather(1);
610 my $xact = $p->last_xact_id;
611 return undef unless $xact;
612 $logger->info("user xact = $xact, saving with xact " . $patron->last_xact_id);
613 return OpenILS::Event->new('XACT_COLLISION')
614 if $xact != $patron->last_xact_id;
619 sub _check_dup_ident {
620 my( $session, $patron ) = @_;
622 return undef unless $patron->ident_value;
625 ident_type => $patron->ident_type,
626 ident_value => $patron->ident_value,
629 $logger->debug("patron update searching for dup ident values: " .
630 $patron->ident_type . ':' . $patron->ident_value);
632 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
634 my $dups = $session->request(
635 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
638 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
645 sub _add_update_addresses {
649 my $new_patron = shift;
653 my $current_id; # id of the address before creation
655 for my $address (@{$patron->addresses()}) {
657 next unless ref $address;
658 $current_id = $address->id();
660 if( $patron->billing_address() and
661 $patron->billing_address() == $current_id ) {
662 $logger->info("setting billing addr to $current_id");
663 $new_patron->billing_address($address->id());
664 $new_patron->ischanged(1);
667 if( $patron->mailing_address() and
668 $patron->mailing_address() == $current_id ) {
669 $new_patron->mailing_address($address->id());
670 $logger->info("setting mailing addr to $current_id");
671 $new_patron->ischanged(1);
675 if($address->isnew()) {
677 $address->usr($new_patron->id());
679 ($address, $evt) = _add_address($session,$address);
680 return (undef, $evt) if $evt;
682 # we need to get the new id
683 if( $patron->billing_address() and
684 $patron->billing_address() == $current_id ) {
685 $new_patron->billing_address($address->id());
686 $logger->info("setting billing addr to $current_id");
687 $new_patron->ischanged(1);
690 if( $patron->mailing_address() and
691 $patron->mailing_address() == $current_id ) {
692 $new_patron->mailing_address($address->id());
693 $logger->info("setting mailing addr to $current_id");
694 $new_patron->ischanged(1);
697 } elsif($address->ischanged() ) {
699 ($address, $evt) = _update_address($session, $address);
700 return (undef, $evt) if $evt;
702 } elsif($address->isdeleted() ) {
704 if( $address->id() == $new_patron->mailing_address() ) {
705 $new_patron->clear_mailing_address();
706 ($new_patron, $evt) = _update_patron($session, $new_patron);
707 return (undef, $evt) if $evt;
710 if( $address->id() == $new_patron->billing_address() ) {
711 $new_patron->clear_billing_address();
712 ($new_patron, $evt) = _update_patron($session, $new_patron);
713 return (undef, $evt) if $evt;
716 $evt = _delete_address($session, $address);
717 return (undef, $evt) if $evt;
721 return ( $new_patron, undef );
725 # adds an address to the db and returns the address with new id
727 my($session, $address) = @_;
728 $address->clear_id();
730 $logger->info("Creating new address at street ".$address->street1);
732 # put the address into the database
733 my $id = $session->request(
734 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
735 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
738 return ($address, undef);
742 sub _update_address {
743 my( $session, $address ) = @_;
745 $logger->info("Updating address ".$address->id." in the DB");
747 my $stat = $session->request(
748 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
750 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
751 return ($address, undef);
756 sub _add_update_cards {
760 my $new_patron = shift;
764 my $virtual_id; #id of the card before creation
765 for my $card (@{$patron->cards()}) {
767 $card->usr($new_patron->id());
769 if(ref($card) and $card->isnew()) {
771 $virtual_id = $card->id();
772 ( $card, $evt ) = _add_card($session,$card);
773 return (undef, $evt) if $evt;
775 #if(ref($patron->card)) { $patron->card($patron->card->id); }
776 if($patron->card() == $virtual_id) {
777 $new_patron->card($card->id());
778 $new_patron->ischanged(1);
781 } elsif( ref($card) and $card->ischanged() ) {
782 $evt = _update_card($session, $card);
783 return (undef, $evt) if $evt;
787 return ( $new_patron, undef );
791 # adds an card to the db and returns the card with new id
793 my( $session, $card ) = @_;
796 $logger->info("Adding new patron card ".$card->barcode);
798 my $id = $session->request(
799 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
800 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
801 $logger->info("Successfully created patron card $id");
804 return ( $card, undef );
808 # returns event on error. returns undef otherwise
810 my( $session, $card ) = @_;
811 $logger->info("Updating patron card ".$card->id);
813 my $stat = $session->request(
814 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
815 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
822 # returns event on error. returns undef otherwise
823 sub _delete_address {
824 my( $session, $address ) = @_;
826 $logger->info("Deleting address ".$address->id." from DB");
828 my $stat = $session->request(
829 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
831 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
837 sub _add_survey_responses {
838 my ($session, $patron, $new_patron) = @_;
840 $logger->info( "Updating survey responses for patron ".$new_patron->id );
842 my $responses = $patron->survey_responses;
846 $_->usr($new_patron->id) for (@$responses);
848 my $evt = $U->simplereq( "open-ils.circ",
849 "open-ils.circ.survey.submit.user_id", $responses );
851 return (undef, $evt) if defined($U->event_code($evt));
855 return ( $new_patron, undef );
859 sub _create_stat_maps {
861 my($session, $user_session, $patron, $new_patron) = @_;
863 my $maps = $patron->stat_cat_entries();
865 for my $map (@$maps) {
867 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
869 if ($map->isdeleted()) {
870 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
872 } elsif ($map->isnew()) {
873 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
878 $map->target_usr($new_patron->id);
881 $logger->info("Updating stat entry with method $method and map $map");
883 my $stat = $session->request($method, $map)->gather(1);
884 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
888 return ($new_patron, undef);
891 sub _create_perm_maps {
893 my($session, $user_session, $patron, $new_patron) = @_;
895 my $maps = $patron->permissions;
897 for my $map (@$maps) {
899 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
900 if ($map->isdeleted()) {
901 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
902 } elsif ($map->isnew()) {
903 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
908 $map->usr($new_patron->id);
910 #warn( "Updating permissions with method $method and session $user_session and map $map" );
911 $logger->info( "Updating permissions with method $method and map $map" );
913 my $stat = $session->request($method, $map)->gather(1);
914 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
918 return ($new_patron, undef);
922 __PACKAGE__->register_method(
923 method => "set_user_work_ous",
924 api_name => "open-ils.actor.user.work_ous.update",
927 sub set_user_work_ous {
933 my( $requestor, $evt ) = $apputils->checksesperm( $ses, 'ASSIGN_WORK_ORG_UNIT' );
936 my $session = $apputils->start_db_session();
938 for my $map (@$maps) {
940 my $method = "open-ils.storage.direct.permission.usr_work_ou_map.update";
941 if ($map->isdeleted()) {
942 $method = "open-ils.storage.direct.permission.usr_work_ou_map.delete";
943 } elsif ($map->isnew()) {
944 $method = "open-ils.storage.direct.permission.usr_work_ou_map.create";
948 #warn( "Updating permissions with method $method and session $ses and map $map" );
949 $logger->info( "Updating work_ou map with method $method and map $map" );
951 my $stat = $session->request($method, $map)->gather(1);
952 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
956 $apputils->commit_db_session($session);
958 return scalar(@$maps);
962 __PACKAGE__->register_method(
963 method => "set_user_perms",
964 api_name => "open-ils.actor.user.permissions.update",
973 my $session = $apputils->start_db_session();
975 my( $user_obj, $evt ) = $U->checkses($ses);
978 my $perms = $session->request('open-ils.storage.permission.user_perms.atomic', $user_obj->id)->gather(1);
981 $all = 1 if ($U->is_true($user_obj->super_user()));
982 $all = 1 unless ($U->check_perms($user_obj->id, $user_obj->home_ou, 'EVERYTHING'));
984 for my $map (@$maps) {
986 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
987 if ($map->isdeleted()) {
988 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
989 } elsif ($map->isnew()) {
990 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
994 next if (!$all and !grep { $_->perm eq $map->perm and $U->is_true($_->grantable) and $_->depth <= $map->depth } @$perms);
995 #warn( "Updating permissions with method $method and session $ses and map $map" );
996 $logger->info( "Updating permissions with method $method and map $map" );
998 my $stat = $session->request($method, $map)->gather(1);
999 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
1003 $apputils->commit_db_session($session);
1005 return scalar(@$maps);
1009 __PACKAGE__->register_method(
1010 method => "user_retrieve_by_barcode",
1012 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
1014 sub user_retrieve_by_barcode {
1015 my($self, $client, $user_session, $barcode) = @_;
1017 $logger->debug("Searching for user with barcode $barcode");
1018 my ($user_obj, $evt) = $apputils->checkses($user_session);
1019 return $evt if $evt;
1021 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
1023 "open-ils.cstore.direct.actor.card.search.atomic",
1024 { barcode => $barcode }
1027 if(!$card || !$card->[0]) {
1028 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
1032 my $user = flesh_user($card->usr(), new_editor(requestor => $user_obj));
1034 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
1035 return $evt if $evt;
1037 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
1044 __PACKAGE__->register_method(
1045 method => "get_user_by_id",
1047 api_name => "open-ils.actor.user.retrieve",);
1049 sub get_user_by_id {
1050 my ($self, $client, $auth, $id) = @_;
1051 my $e = new_editor(authtoken=>$auth);
1052 return $e->event unless $e->checkauth;
1053 my $user = $e->retrieve_actor_user($id)
1054 or return $e->event;
1055 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
1061 __PACKAGE__->register_method(
1062 method => "get_org_types",
1063 api_name => "open-ils.actor.org_types.retrieve",);
1066 return $U->get_org_types();
1071 __PACKAGE__->register_method(
1072 method => "get_user_ident_types",
1073 api_name => "open-ils.actor.user.ident_types.retrieve",
1076 sub get_user_ident_types {
1077 return $ident_types if $ident_types;
1078 return $ident_types =
1079 new_editor()->retrieve_all_config_identification_type();
1085 __PACKAGE__->register_method(
1086 method => "get_org_unit",
1087 api_name => "open-ils.actor.org_unit.retrieve",
1091 my( $self, $client, $user_session, $org_id ) = @_;
1092 my $e = new_editor(authtoken => $user_session);
1094 return $e->event unless $e->checkauth;
1095 $org_id = $e->requestor->ws_ou;
1097 my $o = $e->retrieve_actor_org_unit($org_id)
1098 or return $e->event;
1102 __PACKAGE__->register_method(
1103 method => "search_org_unit",
1104 api_name => "open-ils.actor.org_unit_list.search",
1107 sub search_org_unit {
1109 my( $self, $client, $field, $value ) = @_;
1111 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
1113 "open-ils.cstore.direct.actor.org_unit.search.atomic",
1114 { $field => $value } );
1120 # build the org tree
1122 __PACKAGE__->register_method(
1123 method => "get_org_tree",
1124 api_name => "open-ils.actor.org_tree.retrieve",
1126 note => "Returns the entire org tree structure",
1132 return $U->get_org_tree($client->session->session_locale);
1136 __PACKAGE__->register_method(
1137 method => "get_org_descendants",
1138 api_name => "open-ils.actor.org_tree.descendants.retrieve"
1141 # depth is optional. org_unit is the id
1142 sub get_org_descendants {
1143 my( $self, $client, $org_unit, $depth ) = @_;
1145 if(ref $org_unit eq 'ARRAY') {
1148 for my $i (0..scalar(@$org_unit)-1) {
1149 my $list = $U->simple_scalar_request(
1151 "open-ils.storage.actor.org_unit.descendants.atomic",
1152 $org_unit->[$i], $depth->[$i] );
1153 push(@trees, $U->build_org_tree($list));
1158 my $orglist = $apputils->simple_scalar_request(
1160 "open-ils.storage.actor.org_unit.descendants.atomic",
1161 $org_unit, $depth );
1162 return $U->build_org_tree($orglist);
1167 __PACKAGE__->register_method(
1168 method => "get_org_ancestors",
1169 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
1172 # depth is optional. org_unit is the id
1173 sub get_org_ancestors {
1174 my( $self, $client, $org_unit, $depth ) = @_;
1175 my $orglist = $apputils->simple_scalar_request(
1177 "open-ils.storage.actor.org_unit.ancestors.atomic",
1178 $org_unit, $depth );
1179 return $U->build_org_tree($orglist);
1183 __PACKAGE__->register_method(
1184 method => "get_standings",
1185 api_name => "open-ils.actor.standings.retrieve"
1190 return $user_standings if $user_standings;
1191 return $user_standings =
1192 $apputils->simple_scalar_request(
1194 "open-ils.cstore.direct.config.standing.search.atomic",
1195 { id => { "!=" => undef } }
1201 __PACKAGE__->register_method(
1202 method => "get_my_org_path",
1203 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1206 sub get_my_org_path {
1207 my( $self, $client, $auth, $org_id ) = @_;
1208 my $e = new_editor(authtoken=>$auth);
1209 return $e->event unless $e->checkauth;
1210 $org_id = $e->requestor->ws_ou unless defined $org_id;
1212 return $apputils->simple_scalar_request(
1214 "open-ils.storage.actor.org_unit.full_path.atomic",
1219 __PACKAGE__->register_method(
1220 method => "patron_adv_search",
1221 api_name => "open-ils.actor.patron.search.advanced" );
1222 sub patron_adv_search {
1223 my( $self, $client, $auth, $search_hash,
1224 $search_limit, $search_sort, $include_inactive, $search_depth ) = @_;
1226 my $e = new_editor(authtoken=>$auth);
1227 return $e->event unless $e->checkauth;
1228 return $e->event unless $e->allowed('VIEW_USER');
1229 return $U->storagereq(
1230 "open-ils.storage.actor.user.crazy_search", $search_hash,
1231 $search_limit, $search_sort, $include_inactive, $e->requestor->ws_ou, $search_depth);
1235 __PACKAGE__->register_method(
1236 method => "update_passwd",
1238 api_name => "open-ils.actor.user.password.update");
1240 __PACKAGE__->register_method(
1241 method => "update_passwd",
1242 api_name => "open-ils.actor.user.username.update");
1244 __PACKAGE__->register_method(
1245 method => "update_passwd",
1246 api_name => "open-ils.actor.user.email.update");
1249 my( $self, $conn, $auth, $new_val, $orig_pw ) = @_;
1250 my $e = new_editor(xact=>1, authtoken=>$auth);
1251 return $e->die_event unless $e->checkauth;
1253 my $db_user = $e->retrieve_actor_user($e->requestor->id)
1254 or return $e->die_event;
1255 my $api = $self->api_name;
1257 if( $api =~ /password/o ) {
1259 # make sure the original password matches the in-database password
1260 return OpenILS::Event->new('INCORRECT_PASSWORD')
1261 if md5_hex($orig_pw) ne $db_user->passwd;
1262 $db_user->passwd($new_val);
1266 # if we don't clear the password, the user will be updated with
1267 # a hashed version of the hashed version of their password
1268 $db_user->clear_passwd;
1270 if( $api =~ /username/o ) {
1272 # make sure no one else has this username
1273 my $exist = $e->search_actor_user({usrname=>$new_val},{idlist=>1});
1274 return OpenILS::Event->new('USERNAME_EXISTS') if @$exist;
1275 $db_user->usrname($new_val);
1277 } elsif( $api =~ /email/o ) {
1278 $db_user->email($new_val);
1282 $e->update_actor_user($db_user) or return $e->die_event;
1290 __PACKAGE__->register_method(
1291 method => "check_user_perms",
1292 api_name => "open-ils.actor.user.perm.check",
1293 notes => <<" NOTES");
1294 Takes a login session, user id, an org id, and an array of perm type strings. For each
1295 perm type, if the user does *not* have the given permission it is added
1296 to a list which is returned from the method. If all permissions
1297 are allowed, an empty list is returned
1298 if the logged in user does not match 'user_id', then the logged in user must
1299 have VIEW_PERMISSION priveleges.
1302 sub check_user_perms {
1303 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1305 my( $staff, $evt ) = $apputils->checkses($login_session);
1306 return $evt if $evt;
1308 if($staff->id ne $user_id) {
1309 if( $evt = $apputils->check_perms(
1310 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1316 for my $perm (@$perm_types) {
1317 if($apputils->check_perms($user_id, $org_id, $perm)) {
1318 push @not_allowed, $perm;
1322 return \@not_allowed
1325 __PACKAGE__->register_method(
1326 method => "check_user_perms2",
1327 api_name => "open-ils.actor.user.perm.check.multi_org",
1329 Checks the permissions on a list of perms and orgs for a user
1330 @param authtoken The login session key
1331 @param user_id The id of the user to check
1332 @param orgs The array of org ids
1333 @param perms The array of permission names
1334 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1335 if the logged in user does not match 'user_id', then the logged in user must
1336 have VIEW_PERMISSION priveleges.
1339 sub check_user_perms2 {
1340 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1342 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1343 $authtoken, $user_id, 'VIEW_PERMISSION' );
1344 return $evt if $evt;
1347 for my $org (@$orgs) {
1348 for my $perm (@$perms) {
1349 if($apputils->check_perms($user_id, $org, $perm)) {
1350 push @not_allowed, [ $org, $perm ];
1355 return \@not_allowed
1359 __PACKAGE__->register_method(
1360 method => 'check_user_perms3',
1361 api_name => 'open-ils.actor.user.perm.highest_org',
1363 Returns the highest org unit id at which a user has a given permission
1364 If the requestor does not match the target user, the requestor must have
1365 'VIEW_PERMISSION' rights at the home org unit of the target user
1366 @param authtoken The login session key
1367 @param userid The id of the user in question
1368 @param perm The permission to check
1369 @return The org unit highest in the org tree within which the user has
1370 the requested permission
1373 sub check_user_perms3 {
1374 my($self, $client, $authtoken, $user_id, $perm) = @_;
1375 my $e = new_editor(authtoken=>$authtoken);
1376 return $e->event unless $e->checkauth;
1378 my $tree = $U->get_org_tree();
1380 unless($e->requestor->id == $user_id) {
1381 my $user = $e->retrieve_actor_user($user_id)
1382 or return $e->event;
1383 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1384 return $U->find_highest_perm_org($perm, $user_id, $user->home_ou, $tree );
1387 return $U->find_highest_perm_org($perm, $user_id, $e->requestor->ws_ou, $tree);
1390 __PACKAGE__->register_method(
1391 method => 'user_has_work_perm_at',
1392 api_name => 'open-ils.actor.user.has_work_perm_at',
1396 Returns a set of org unit IDs which represent the highest orgs in
1397 the org tree where the user has the requested permission. The
1398 purpose of this method is to return the smallest set of org units
1399 which represent the full expanse of the user's ability to perform
1400 the requested action. The user whose perms this method should
1401 check is implied by the authtoken. /,
1403 {desc => 'authtoken', type => 'string'},
1404 {desc => 'permission name', type => 'string'},
1405 {desc => q/user id, optional. If present, check perms for
1406 this user instead of the logged in user/, type => 'number'},
1408 return => {desc => 'An array of org IDs'}
1412 sub user_has_work_perm_at {
1413 my($self, $conn, $auth, $perm, $user_id) = @_;
1414 my $e = new_editor(authtoken=>$auth);
1415 return $e->event unless $e->checkauth;
1416 if(defined $user_id) {
1417 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1418 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1420 return $U->user_has_work_perm_at($e, $perm, undef, $user_id);
1423 __PACKAGE__->register_method(
1424 method => 'user_has_work_perm_at_batch',
1425 api_name => 'open-ils.actor.user.has_work_perm_at.batch',
1429 sub user_has_work_perm_at_batch {
1430 my($self, $conn, $auth, $perms, $user_id) = @_;
1431 my $e = new_editor(authtoken=>$auth);
1432 return $e->event unless $e->checkauth;
1433 if(defined $user_id) {
1434 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1435 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1438 $map->{$_} = $U->user_has_work_perm_at($e, $_) for @$perms;
1444 __PACKAGE__->register_method(
1445 method => 'check_user_perms4',
1446 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1448 Returns the highest org unit id at which a user has a given permission
1449 If the requestor does not match the target user, the requestor must have
1450 'VIEW_PERMISSION' rights at the home org unit of the target user
1451 @param authtoken The login session key
1452 @param userid The id of the user in question
1453 @param perms An array of perm names to check
1454 @return An array of orgId's representing the org unit
1455 highest in the org tree within which the user has the requested permission
1456 The arrah of orgId's has matches the order of the perms array
1459 sub check_user_perms4 {
1460 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1462 my( $staff, $target, $org, $evt );
1464 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1465 $authtoken, $userid, 'VIEW_PERMISSION' );
1466 return $evt if $evt;
1469 return [] unless ref($perms);
1470 my $tree = $U->get_org_tree();
1472 for my $p (@$perms) {
1473 push( @arr, $U->find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1481 __PACKAGE__->register_method(
1482 method => "user_fines_summary",
1483 api_name => "open-ils.actor.user.fines.summary",
1485 notes => <<" NOTES");
1486 Returns a short summary of the users total open fines, excluding voided fines
1487 Params are login_session, user_id
1488 Returns a 'mous' object.
1491 sub user_fines_summary {
1492 my( $self, $client, $auth, $user_id ) = @_;
1493 my $e = new_editor(authtoken=>$auth);
1494 return $e->event unless $e->checkauth;
1495 my $user = $e->retrieve_actor_user($user_id)
1496 or return $e->event;
1498 if( $user_id ne $e->requestor->id ) {
1499 return $e->event unless
1500 $e->allowed('VIEW_USER_FINES_SUMMARY', $user->home_ou);
1503 # run this inside a transaction to prevent replication delay errors
1504 my $ses = $U->start_db_session();
1505 my $s = $ses->request(
1506 'open-ils.storage.money.open_user_summary.search', $user_id )->gather(1);
1507 $U->rollback_db_session($ses);
1514 __PACKAGE__->register_method(
1515 method => "user_transactions",
1516 api_name => "open-ils.actor.user.transactions",
1517 notes => <<" NOTES");
1518 Returns a list of open user transactions (mbts objects);
1519 Params are login_session, user_id
1520 Optional third parameter is the transactions type. defaults to all
1523 __PACKAGE__->register_method(
1524 method => "user_transactions",
1525 api_name => "open-ils.actor.user.transactions.have_charge",
1526 notes => <<" NOTES");
1527 Returns a list of all open user transactions (mbts objects) that have an initial charge
1528 Params are login_session, user_id
1529 Optional third parameter is the transactions type. defaults to all
1532 __PACKAGE__->register_method(
1533 method => "user_transactions",
1534 api_name => "open-ils.actor.user.transactions.have_balance",
1536 notes => <<" NOTES");
1537 Returns a list of all open user transactions (mbts objects) that have a balance
1538 Params are login_session, user_id
1539 Optional third parameter is the transactions type. defaults to all
1542 __PACKAGE__->register_method(
1543 method => "user_transactions",
1544 api_name => "open-ils.actor.user.transactions.fleshed",
1545 notes => <<" NOTES");
1546 Returns an object/hash of transaction, circ, title where transaction = an open
1547 user transactions (mbts objects), circ is the attached circluation, and title
1548 is the title the circ points to
1549 Params are login_session, user_id
1550 Optional third parameter is the transactions type. defaults to all
1553 __PACKAGE__->register_method(
1554 method => "user_transactions",
1555 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1556 notes => <<" NOTES");
1557 Returns an object/hash of transaction, circ, title where transaction = an open
1558 user transactions that has an initial charge (mbts objects), circ is the
1559 attached circluation, and title is the title the circ points to
1560 Params are login_session, user_id
1561 Optional third parameter is the transactions type. defaults to all
1564 __PACKAGE__->register_method(
1565 method => "user_transactions",
1566 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1568 notes => <<" NOTES");
1569 Returns an object/hash of transaction, circ, title where transaction = an open
1570 user transaction that has a balance (mbts objects), circ is the attached
1571 circluation, and title is the title the circ points to
1572 Params are login_session, user_id
1573 Optional third parameter is the transaction type. defaults to all
1576 __PACKAGE__->register_method(
1577 method => "user_transactions",
1578 api_name => "open-ils.actor.user.transactions.count",
1579 notes => <<" NOTES");
1580 Returns an object/hash of transaction, circ, title where transaction = an open
1581 user transactions (mbts objects), circ is the attached circluation, and title
1582 is the title the circ points to
1583 Params are login_session, user_id
1584 Optional third parameter is the transactions type. defaults to all
1587 __PACKAGE__->register_method(
1588 method => "user_transactions",
1589 api_name => "open-ils.actor.user.transactions.have_charge.count",
1590 notes => <<" NOTES");
1591 Returns an object/hash of transaction, circ, title where transaction = an open
1592 user transactions that has an initial charge (mbts objects), circ is the
1593 attached circluation, and title is the title the circ points to
1594 Params are login_session, user_id
1595 Optional third parameter is the transactions type. defaults to all
1598 __PACKAGE__->register_method(
1599 method => "user_transactions",
1600 api_name => "open-ils.actor.user.transactions.have_balance.count",
1602 notes => <<" NOTES");
1603 Returns an object/hash of transaction, circ, title where transaction = an open
1604 user transaction that has a balance (mbts objects), circ is the attached
1605 circluation, and title is the title the circ points to
1606 Params are login_session, user_id
1607 Optional third parameter is the transaction type. defaults to all
1610 __PACKAGE__->register_method(
1611 method => "user_transactions",
1612 api_name => "open-ils.actor.user.transactions.have_balance.total",
1614 notes => <<" NOTES");
1615 Returns an object/hash of transaction, circ, title where transaction = an open
1616 user transaction that has a balance (mbts objects), circ is the attached
1617 circluation, and title is the title the circ points to
1618 Params are login_session, user_id
1619 Optional third parameter is the transaction type. defaults to all
1624 sub user_transactions {
1625 my( $self, $client, $login_session, $user_id, $type ) = @_;
1627 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1628 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1629 return $evt if $evt;
1631 my $api = $self->api_name();
1635 if(defined($type)) { @xact = (xact_type => $type);
1637 } else { @xact = (); }
1640 ->method_lookup('open-ils.actor.user.transactions.history.still_open')
1641 ->run($login_session => $user_id => $type);
1644 if($api =~ /have_charge/o) {
1646 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1648 } elsif($api =~ /have_balance/o) {
1650 $trans = [ grep { int($_->balance_owed * 100) != 0 } @$trans ];
1653 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1657 if($api =~ /total/o) {
1659 for my $t (@$trans) {
1660 $total += $t->balance_owed;
1663 $logger->debug("Total balance owed by user $user_id: $total");
1667 if($api =~ /count/o) { return scalar @$trans; }
1668 if($api !~ /fleshed/o) { return $trans; }
1671 for my $t (@$trans) {
1673 if( $t->xact_type ne 'circulation' ) {
1674 push @resp, {transaction => $t};
1678 my $circ = $apputils->simple_scalar_request(
1680 "open-ils.cstore.direct.action.circulation.retrieve",
1685 my $title = $apputils->simple_scalar_request(
1687 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1688 $circ->target_copy );
1692 my $u = OpenILS::Utils::ModsParser->new();
1693 $u->start_mods_batch($title->marc());
1694 my $mods = $u->finish_mods_batch();
1695 $mods->doc_id($title->id) if $mods;
1697 push @resp, {transaction => $t, circ => $circ, record => $mods };
1705 __PACKAGE__->register_method(
1706 method => "user_transaction_retrieve",
1707 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1709 notes => <<" NOTES");
1710 Returns a fleshed transaction record
1712 __PACKAGE__->register_method(
1713 method => "user_transaction_retrieve",
1714 api_name => "open-ils.actor.user.transaction.retrieve",
1716 notes => <<" NOTES");
1717 Returns a transaction record
1719 sub user_transaction_retrieve {
1720 my( $self, $client, $login_session, $bill_id ) = @_;
1722 # I think I'm deprecated... make sure. phasefx says, "No, I'll use you :)
1724 my $trans = $apputils->simple_scalar_request(
1726 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1730 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1731 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1732 return $evt if $evt;
1734 my $api = $self->api_name();
1735 if($api !~ /fleshed/o) { return $trans; }
1737 if( $trans->xact_type ne 'circulation' ) {
1738 $logger->debug("Returning non-circ transaction");
1739 return {transaction => $trans};
1742 my $circ = $apputils->simple_scalar_request(
1744 "open-ils.cstore.direct.action.circulation.retrieve",
1747 return {transaction => $trans} unless $circ;
1748 $logger->debug("Found the circ transaction");
1750 my $title = $apputils->simple_scalar_request(
1752 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1753 $circ->target_copy );
1755 return {transaction => $trans, circ => $circ } unless $title;
1756 $logger->debug("Found the circ title");
1759 my $copy = $apputils->simple_scalar_request(
1761 "open-ils.cstore.direct.asset.copy.retrieve",
1762 $circ->target_copy );
1765 my $u = OpenILS::Utils::ModsParser->new();
1766 $u->start_mods_batch($title->marc());
1767 $mods = $u->finish_mods_batch();
1769 if ($title->id == OILS_PRECAT_RECORD) {
1770 $mods = new Fieldmapper::metabib::virtual_record;
1771 $mods->doc_id(OILS_PRECAT_RECORD);
1772 $mods->title($copy->dummy_title);
1773 $mods->author($copy->dummy_author);
1777 $logger->debug("MODSized the circ title");
1779 return {transaction => $trans, circ => $circ, record => $mods, copy => $copy };
1783 __PACKAGE__->register_method(
1784 method => "hold_request_count",
1785 api_name => "open-ils.actor.user.hold_requests.count",
1788 notes => <<" NOTES");
1789 Returns hold ready/total counts
1791 sub hold_request_count {
1792 my( $self, $client, $login_session, $userid ) = @_;
1794 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1795 $login_session, $userid, 'VIEW_HOLD' );
1796 return $evt if $evt;
1799 my $holds = $apputils->simple_scalar_request(
1801 "open-ils.cstore.direct.action.hold_request.search.atomic",
1804 fulfillment_time => {"=" => undef },
1805 cancel_time => undef,
1810 for my $h (@$holds) {
1811 next unless $h->capture_time and $h->current_copy;
1813 my $copy = $apputils->simple_scalar_request(
1815 "open-ils.cstore.direct.asset.copy.retrieve",
1819 if ($copy and $copy->status == 8) {
1824 return { total => scalar(@$holds), ready => scalar(@ready) };
1828 __PACKAGE__->register_method(
1829 method => "checkedout_count",
1830 api_name => "open-ils.actor.user.checked_out.count__",
1832 notes => <<" NOTES");
1833 Returns a transaction record
1837 sub checkedout_count {
1838 my( $self, $client, $login_session, $userid ) = @_;
1840 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1841 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1842 return $evt if $evt;
1844 my $circs = $apputils->simple_scalar_request(
1846 "open-ils.cstore.direct.action.circulation.search.atomic",
1847 { usr => $userid, stop_fines => undef }
1848 #{ usr => $userid, checkin_time => {"=" => undef } }
1851 my $parser = DateTime::Format::ISO8601->new;
1854 for my $c (@$circs) {
1855 my $due_dt = $parser->parse_datetime( cleanse_ISO8601( $c->due_date ) );
1856 my $due = $due_dt->epoch;
1858 if ($due < DateTime->today->epoch) {
1863 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1867 __PACKAGE__->register_method(
1868 method => "checked_out",
1869 api_name => "open-ils.actor.user.checked_out",
1873 Returns a structure of circulations objects sorted by
1874 out, overdue, lost, claims_returned, long_overdue.
1875 A list of IDs are returned of each type.
1876 lost, long_overdue, and claims_returned circ will not
1877 be "finished" (there is an outstanding balance or some
1878 other pending action on the circ).
1880 The .count method also includes a 'total' field which
1881 sums all "open" circs
1885 __PACKAGE__->register_method(
1886 method => "checked_out",
1887 api_name => "open-ils.actor.user.checked_out.count",
1890 signature => q/@see open-ils.actor.user.checked_out/
1894 my( $self, $conn, $auth, $userid ) = @_;
1896 my $e = new_editor(authtoken=>$auth);
1897 return $e->event unless $e->checkauth;
1899 if( $userid ne $e->requestor->id ) {
1900 my $user = $e->retrieve_actor_user($userid) or return $e->event;
1901 unless($e->allowed('VIEW_CIRCULATIONS', $user->home_ou)) {
1903 # see if there is a friend link allowing circ.view perms
1904 my $allowed = OpenILS::Application::Actor::Friends->friend_perm_allowed(
1905 $e, $userid, $e->requestor->id, 'circ.view');
1906 return $e->event unless $allowed;
1910 my $count = $self->api_name =~ /count/;
1911 return _checked_out( $count, $e, $userid );
1915 my( $iscount, $e, $userid ) = @_;
1916 my $meth = 'open-ils.storage.actor.user.checked_out';
1917 $meth = "$meth.count" if $iscount;
1918 return $U->storagereq($meth, $userid);
1922 sub _checked_out_WHAT {
1923 my( $iscount, $e, $userid ) = @_;
1925 my $circs = $e->search_action_circulation(
1926 { usr => $userid, stop_fines => undef });
1928 my $mcircs = $e->search_action_circulation(
1931 checkin_time => undef,
1932 xact_finish => undef,
1936 push( @$circs, @$mcircs );
1938 my $parser = DateTime::Format::ISO8601->new;
1940 # split the circs up into overdue and not-overdue circs
1942 for my $c (@$circs) {
1943 if( $c->due_date ) {
1944 my $due_dt = $parser->parse_datetime( cleanse_ISO8601( $c->due_date ) );
1945 my $due = $due_dt->epoch;
1946 if ($due < DateTime->today->epoch) {
1947 push @overdue, $c->id;
1956 # grab all of the lost, claims-returned, and longoverdue circs
1957 #my $open = $e->search_action_circulation(
1958 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1961 # these items have stop_fines, but no xact_finish, so money
1962 # is owed on them and they have not been checked in
1963 my $open = $e->search_action_circulation(
1966 stop_fines => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] },
1967 xact_finish => undef,
1968 checkin_time => undef,
1973 my( @lost, @cr, @lo );
1974 for my $c (@$open) {
1975 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1976 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1977 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1983 total => @$circs + @lost + @cr + @lo,
1984 out => scalar(@out),
1985 overdue => scalar(@overdue),
1986 lost => scalar(@lost),
1987 claims_returned => scalar(@cr),
1988 long_overdue => scalar(@lo)
1994 overdue => \@overdue,
1996 claims_returned => \@cr,
1997 long_overdue => \@lo
2003 __PACKAGE__->register_method(
2004 method => "checked_in_with_fines",
2005 api_name => "open-ils.actor.user.checked_in_with_fines",
2008 signature => q/@see open-ils.actor.user.checked_out/
2010 sub checked_in_with_fines {
2011 my( $self, $conn, $auth, $userid ) = @_;
2013 my $e = new_editor(authtoken=>$auth);
2014 return $e->event unless $e->checkauth;
2016 if( $userid ne $e->requestor->id ) {
2017 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
2020 # money is owed on these items and they are checked in
2021 my $open = $e->search_action_circulation(
2024 xact_finish => undef,
2025 checkin_time => { "!=" => undef },
2030 my( @lost, @cr, @lo );
2031 for my $c (@$open) {
2032 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
2033 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
2034 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
2039 claims_returned => \@cr,
2040 long_overdue => \@lo
2052 __PACKAGE__->register_method(
2053 method => "user_transaction_history",
2054 api_name => "open-ils.actor.user.transactions.history",
2056 notes => <<" NOTES");
2057 Returns a list of billable transactions for a user, optionally by type
2059 __PACKAGE__->register_method(
2060 method => "user_transaction_history",
2061 api_name => "open-ils.actor.user.transactions.history.have_charge",
2063 notes => <<" NOTES");
2064 Returns a list of billable transactions for a user that have an initial charge, optionally by type
2066 __PACKAGE__->register_method(
2067 method => "user_transaction_history",
2068 api_name => "open-ils.actor.user.transactions.history.have_balance",
2071 notes => <<" NOTES");
2072 Returns a list of billable transactions for a user that have a balance, optionally by type
2074 __PACKAGE__->register_method(
2075 method => "user_transaction_history",
2076 api_name => "open-ils.actor.user.transactions.history.still_open",
2078 notes => <<" NOTES");
2079 Returns a list of billable transactions for a user that are not finished
2081 __PACKAGE__->register_method(
2082 method => "user_transaction_history",
2083 api_name => "open-ils.actor.user.transactions.history.have_bill",
2086 notes => <<" NOTES");
2087 Returns a list of billable transactions for a user that has billings
2089 __PACKAGE__->register_method(
2090 method => "user_transaction_history",
2091 api_name => "open-ils.actor.user.transactions.history.ids",
2093 notes => <<" NOTES");
2094 Returns a list of billable transaction ids for a user, optionally by type
2096 __PACKAGE__->register_method(
2097 method => "user_transaction_history",
2098 api_name => "open-ils.actor.user.transactions.history.have_charge.ids",
2100 notes => <<" NOTES");
2101 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
2103 __PACKAGE__->register_method(
2104 method => "user_transaction_history",
2105 api_name => "open-ils.actor.user.transactions.history.have_balance.ids",
2108 notes => <<" NOTES");
2109 Returns a list of billable transaction ids for a user that have a balance, optionally by type
2111 __PACKAGE__->register_method(
2112 method => "user_transaction_history",
2113 api_name => "open-ils.actor.user.transactions.history.still_open.ids",
2115 notes => <<" NOTES");
2116 Returns a list of billable transaction ids for a user that are not finished
2118 __PACKAGE__->register_method(
2119 method => "user_transaction_history",
2120 api_name => "open-ils.actor.user.transactions.history.have_bill.ids",
2123 notes => <<" NOTES");
2124 Returns a list of billable transaction ids for a user that has billings
2126 __PACKAGE__->register_method(
2127 method => "user_transaction_history",
2128 api_name => "open-ils.actor.user.transactions.history.have_bill_or_payment",
2131 notes => <<" NOTES");
2132 Returns a list of billable transactions for a user that has non-zero-sum billings or at least 1 payment
2134 __PACKAGE__->register_method(
2135 method => "user_transaction_history",
2136 api_name => "open-ils.actor.user.transactions.history.have_bill_or_payment.ids",
2139 notes => <<" NOTES");
2140 Returns a list of billable transaction ids for a user that has non-zero-sum billings or at least 1 payment
2145 sub user_transaction_history {
2146 my( $self, $conn, $auth, $userid, $type, $filter ) = @_;
2149 # run inside of a transaction to prevent replication delays
2150 my $e = new_editor(authtoken=>$auth);
2151 return $e->die_event unless $e->checkauth;
2153 if( $e->requestor->id ne $userid ) {
2154 return $e->die_event
2155 unless $e->allowed('VIEW_USER_TRANSACTIONS');
2158 my $api = $self->api_name;
2159 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
2161 my $mbts = $e->search_money_billable_transaction_summary(
2163 { usr => $userid, @xact_finish, %$filter },
2164 { order_by => { mbt => 'xact_start DESC' } }
2168 if(defined($type)) {
2169 @$mbts = grep { $_->xact_type eq $type } @$mbts;
2172 if($api =~ /have_bill_or_payment/o) {
2174 # transactions that have a non-zero sum across all billings or at least 1 payment
2176 int($_->balance_owed * 100) != 0 ||
2177 defined($_->last_payment_ts) } @$mbts;
2179 } elsif( $api =~ /have_balance/o) {
2181 # transactions that have a non-zero overall balance
2182 @$mbts = grep { int($_->balance_owed * 100) != 0 } @$mbts;
2184 } elsif( $api =~ /have_charge/o) {
2186 # transactions that have at least 1 billing, regardless of whether it was voided
2187 @$mbts = grep { defined($_->last_billing_ts) } @$mbts;
2189 } elsif( $api =~ /have_bill/o) {
2191 # transactions that have non-zero sum across all billings. This will exclude
2192 # xacts where all billings have been voided
2193 @$mbts = grep { int($_->total_owed * 100) != 0 } @$mbts;
2196 if ($api =~ /\.ids/) {
2197 return [map {$_->id} @$mbts];
2205 __PACKAGE__->register_method(
2206 method => "user_perms",
2207 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2209 notes => <<" NOTES");
2210 Returns a list of permissions
2213 my( $self, $client, $authtoken, $user ) = @_;
2215 my( $staff, $evt ) = $apputils->checkses($authtoken);
2216 return $evt if $evt;
2218 $user ||= $staff->id;
2220 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2224 return $apputils->simple_scalar_request(
2226 "open-ils.storage.permission.user_perms.atomic",
2230 __PACKAGE__->register_method(
2231 method => "retrieve_perms",
2232 api_name => "open-ils.actor.permissions.retrieve",
2233 notes => <<" NOTES");
2234 Returns a list of permissions
2236 sub retrieve_perms {
2237 my( $self, $client ) = @_;
2238 return $apputils->simple_scalar_request(
2240 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2241 { id => { '!=' => undef } }
2245 __PACKAGE__->register_method(
2246 method => "retrieve_groups",
2247 api_name => "open-ils.actor.groups.retrieve",
2248 notes => <<" NOTES");
2249 Returns a list of user groupss
2251 sub retrieve_groups {
2252 my( $self, $client ) = @_;
2253 return new_editor()->retrieve_all_permission_grp_tree();
2256 __PACKAGE__->register_method(
2257 method => "retrieve_org_address",
2258 api_name => "open-ils.actor.org_unit.address.retrieve",
2259 notes => <<' NOTES');
2260 Returns an org_unit address by ID
2261 @param An org_address ID
2263 sub retrieve_org_address {
2264 my( $self, $client, $id ) = @_;
2265 return $apputils->simple_scalar_request(
2267 "open-ils.cstore.direct.actor.org_address.retrieve",
2272 __PACKAGE__->register_method(
2273 method => "retrieve_groups_tree",
2274 api_name => "open-ils.actor.groups.tree.retrieve",
2275 notes => <<" NOTES");
2276 Returns a list of user groups
2278 sub retrieve_groups_tree {
2279 my( $self, $client ) = @_;
2280 return new_editor()->search_permission_grp_tree(
2285 flesh_fields => { pgt => ["children"] },
2286 order_by => { pgt => 'name'}
2293 __PACKAGE__->register_method(
2294 method => "add_user_to_groups",
2295 api_name => "open-ils.actor.user.set_groups",
2296 notes => <<" NOTES");
2297 Adds a user to one or more permission groups
2300 sub add_user_to_groups {
2301 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2303 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2304 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2305 return $evt if $evt;
2307 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2308 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2309 return $evt if $evt;
2311 $apputils->simplereq(
2313 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2315 for my $group (@$groups) {
2316 my $link = Fieldmapper::permission::usr_grp_map->new;
2318 $link->usr($userid);
2320 my $id = $apputils->simplereq(
2322 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2328 __PACKAGE__->register_method(
2329 method => "get_user_perm_groups",
2330 api_name => "open-ils.actor.user.get_groups",
2331 notes => <<" NOTES");
2332 Retrieve a user's permission groups.
2336 sub get_user_perm_groups {
2337 my( $self, $client, $authtoken, $userid ) = @_;
2339 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2340 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2341 return $evt if $evt;
2343 return $apputils->simplereq(
2345 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2349 __PACKAGE__->register_method(
2350 method => "get_user_work_ous",
2351 api_name => "open-ils.actor.user.get_work_ous",
2352 notes => <<" NOTES");
2353 Retrieve a user's work org units.
2355 __PACKAGE__->register_method(
2356 method => "get_user_work_ous",
2357 api_name => "open-ils.actor.user.get_work_ous.ids",
2358 notes => <<" NOTES");
2359 Retrieve a user's work org units.
2363 sub get_user_work_ous {
2364 my( $self, $client, $auth, $userid ) = @_;
2365 my $e = new_editor(authtoken=>$auth);
2366 return $e->event unless $e->checkauth;
2367 $userid ||= $e->requestor->id;
2369 if($e->requestor->id != $userid) {
2370 my $user = $e->retrieve_actor_user($userid)
2371 or return $e->event;
2372 return $e->event unless $e->allowed('ASSIGN_WORK_ORG_UNIT', $user->home_ou);
2375 return $e->search_permission_usr_work_ou_map({usr => $userid})
2376 unless $self->api_name =~ /.ids$/;
2378 # client just wants a list of org IDs
2379 return $U->get_user_work_ou_ids($e, $userid);
2385 __PACKAGE__->register_method (
2386 method => 'register_workstation',
2387 api_name => 'open-ils.actor.workstation.register.override',
2388 signature => q/@see open-ils.actor.workstation.register/);
2390 __PACKAGE__->register_method (
2391 method => 'register_workstation',
2392 api_name => 'open-ils.actor.workstation.register',
2394 Registers a new workstion in the system
2395 @param authtoken The login session key
2396 @param name The name of the workstation id
2397 @param owner The org unit that owns this workstation
2398 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2399 if the name is already in use.
2402 sub register_workstation {
2403 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2405 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2406 return $e->die_event unless $e->checkauth;
2407 return $e->die_event unless $e->allowed('REGISTER_WORKSTATION', $owner);
2408 my $existing = $e->search_actor_workstation({name => $name})->[0];
2412 if( $self->api_name =~ /override/o ) {
2413 # workstation with the given name exists.
2415 if($owner ne $existing->owning_lib) {
2416 # if necessary, update the owning_lib of the workstation
2418 $logger->info("changing owning lib of workstation ".$existing->id.
2419 " from ".$existing->owning_lib." to $owner");
2420 return $e->die_event unless
2421 $e->allowed('UPDATE_WORKSTATION', $existing->owning_lib);
2423 return $e->die_event unless $e->allowed('UPDATE_WORKSTATION', $owner);
2425 $existing->owning_lib($owner);
2426 return $e->die_event unless $e->update_actor_workstation($existing);
2432 "attempt to register an existing workstation. returning existing ID");
2435 return $existing->id;
2438 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2442 my $ws = Fieldmapper::actor::workstation->new;
2443 $ws->owning_lib($owner);
2445 $e->create_actor_workstation($ws) or return $e->die_event;
2447 return $ws->id; # note: editor sets the id on the new object for us
2450 __PACKAGE__->register_method (
2451 method => 'workstation_list',
2452 api_name => 'open-ils.actor.workstation.list',
2454 Returns a list of workstations registered at the given location
2455 @param authtoken The login session key
2456 @param ids A list of org_unit.id's for the workstation owners
2459 sub workstation_list {
2460 my( $self, $conn, $authtoken, @orgs ) = @_;
2462 my $e = new_editor(authtoken=>$authtoken);
2463 return $e->event unless $e->checkauth;
2468 unless $e->allowed('REGISTER_WORKSTATION', $o);
2469 $results{$o} = $e->search_actor_workstation({owning_lib=>$o});
2480 __PACKAGE__->register_method (
2481 method => 'fetch_patron_note',
2482 api_name => 'open-ils.actor.note.retrieve.all',
2485 Returns a list of notes for a given user
2486 Requestor must have VIEW_USER permission if pub==false and
2487 @param authtoken The login session key
2488 @param args Hash of params including
2489 patronid : the patron's id
2490 pub : true if retrieving only public notes
2494 sub fetch_patron_note {
2495 my( $self, $conn, $authtoken, $args ) = @_;
2496 my $patronid = $$args{patronid};
2498 my($reqr, $evt) = $U->checkses($authtoken);
2499 return $evt if $evt;
2502 ($patron, $evt) = $U->fetch_user($patronid);
2503 return $evt if $evt;
2506 if( $patronid ne $reqr->id ) {
2507 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2508 return $evt if $evt;
2510 return $U->cstorereq(
2511 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2512 { usr => $patronid, pub => 't' } );
2515 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2516 return $evt if $evt;
2518 return $U->cstorereq(
2519 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2522 __PACKAGE__->register_method (
2523 method => 'create_user_note',
2524 api_name => 'open-ils.actor.note.create',
2526 Creates a new note for the given user
2527 @param authtoken The login session key
2528 @param note The note object
2531 sub create_user_note {
2532 my( $self, $conn, $authtoken, $note ) = @_;
2533 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2534 return $e->die_event unless $e->checkauth;
2536 my $user = $e->retrieve_actor_user($note->usr)
2537 or return $e->die_event;
2539 return $e->die_event unless
2540 $e->allowed('UPDATE_USER',$user->home_ou);
2542 $note->creator($e->requestor->id);
2543 $e->create_actor_usr_note($note) or return $e->die_event;
2549 __PACKAGE__->register_method (
2550 method => 'delete_user_note',
2551 api_name => 'open-ils.actor.note.delete',
2553 Deletes a note for the given user
2554 @param authtoken The login session key
2555 @param noteid The note id
2558 sub delete_user_note {
2559 my( $self, $conn, $authtoken, $noteid ) = @_;
2561 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2562 return $e->die_event unless $e->checkauth;
2563 my $note = $e->retrieve_actor_usr_note($noteid)
2564 or return $e->die_event;
2565 my $user = $e->retrieve_actor_user($note->usr)
2566 or return $e->die_event;
2567 return $e->die_event unless
2568 $e->allowed('UPDATE_USER', $user->home_ou);
2570 $e->delete_actor_usr_note($note) or return $e->die_event;
2576 __PACKAGE__->register_method (
2577 method => 'update_user_note',
2578 api_name => 'open-ils.actor.note.update',
2580 @param authtoken The login session key
2581 @param note The note
2585 sub update_user_note {
2586 my( $self, $conn, $auth, $note ) = @_;
2587 my $e = new_editor(authtoken=>$auth, xact=>1);
2588 return $e->event unless $e->checkauth;
2589 my $patron = $e->retrieve_actor_user($note->usr)
2590 or return $e->event;
2591 return $e->event unless
2592 $e->allowed('UPDATE_USER', $patron->home_ou);
2593 $e->update_actor_user_note($note)
2594 or return $e->event;
2602 __PACKAGE__->register_method (
2603 method => 'create_closed_date',
2604 api_name => 'open-ils.actor.org_unit.closed_date.create',
2606 Creates a new closing entry for the given org_unit
2607 @param authtoken The login session key
2608 @param note The closed_date object
2611 sub create_closed_date {
2612 my( $self, $conn, $authtoken, $cd ) = @_;
2614 my( $user, $evt ) = $U->checkses($authtoken);
2615 return $evt if $evt;
2617 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2618 return $evt if $evt;
2620 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2622 my $id = $U->storagereq(
2623 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2624 return $U->DB_UPDATE_FAILED($cd) unless $id;
2629 __PACKAGE__->register_method (
2630 method => 'delete_closed_date',
2631 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2633 Deletes a closing entry for the given org_unit
2634 @param authtoken The login session key
2635 @param noteid The close_date id
2638 sub delete_closed_date {
2639 my( $self, $conn, $authtoken, $cd ) = @_;
2641 my( $user, $evt ) = $U->checkses($authtoken);
2642 return $evt if $evt;
2645 ($cd_obj, $evt) = fetch_closed_date($cd);
2646 return $evt if $evt;
2648 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2649 return $evt if $evt;
2651 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2653 my $stat = $U->storagereq(
2654 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2655 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2660 __PACKAGE__->register_method(
2661 method => 'usrname_exists',
2662 api_name => 'open-ils.actor.username.exists',
2664 Returns 1 if the requested username exists, returns 0 otherwise
2668 sub usrname_exists {
2669 my( $self, $conn, $auth, $usrname ) = @_;
2670 my $e = new_editor(authtoken=>$auth);
2671 return $e->event unless $e->checkauth;
2672 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2673 return $$a[0] if $a and @$a;
2677 __PACKAGE__->register_method(
2678 method => 'barcode_exists',
2679 api_name => 'open-ils.actor.barcode.exists',
2682 Returns 1 if the requested barcode exists, returns 0 otherwise
2686 sub barcode_exists {
2687 my( $self, $conn, $auth, $barcode ) = @_;
2688 my $e = new_editor(authtoken=>$auth);
2689 return $e->event unless $e->checkauth;
2690 my $card = $e->search_actor_card({barcode => $barcode});
2696 #return undef unless @$card;
2697 #return $card->[0]->usr;
2701 __PACKAGE__->register_method(
2702 method => 'retrieve_net_levels',
2703 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2706 sub retrieve_net_levels {
2707 my( $self, $conn, $auth ) = @_;
2708 my $e = new_editor(authtoken=>$auth);
2709 return $e->event unless $e->checkauth;
2710 return $e->retrieve_all_config_net_access_level();
2713 # Retain the old typo API name just in case
2714 __PACKAGE__->register_method(
2715 method => 'fetch_org_by_shortname',
2716 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2718 __PACKAGE__->register_method(
2719 method => 'fetch_org_by_shortname',
2720 api_name => 'open-ils.actor.org_unit.retrieve_by_shortname',
2722 sub fetch_org_by_shortname {
2723 my( $self, $conn, $sname ) = @_;
2724 my $e = new_editor();
2725 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2726 return $e->event unless $org;
2731 __PACKAGE__->register_method(
2732 method => 'session_home_lib',
2733 api_name => 'open-ils.actor.session.home_lib',
2736 sub session_home_lib {
2737 my( $self, $conn, $auth ) = @_;
2738 my $e = new_editor(authtoken=>$auth);
2739 return undef unless $e->checkauth;
2740 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2741 return $org->shortname;
2744 __PACKAGE__->register_method(
2745 method => 'session_safe_token',
2746 api_name => 'open-ils.actor.session.safe_token',
2748 Returns a hashed session ID that is safe for export to the world.
2749 This safe token will expire after 1 hour of non-use.
2750 @param auth Active authentication token
2754 sub session_safe_token {
2755 my( $self, $conn, $auth ) = @_;
2756 my $e = new_editor(authtoken=>$auth);
2757 return undef unless $e->checkauth;
2759 my $safe_token = md5_hex($auth);
2761 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2763 # Add more like the following if needed...
2765 "safe-token-home_lib-shortname-$safe_token",
2766 $e->retrieve_actor_org_unit(
2767 $e->requestor->home_ou
2776 __PACKAGE__->register_method(
2777 method => 'safe_token_home_lib',
2778 api_name => 'open-ils.actor.safe_token.home_lib.shortname',
2780 Returns the home library shortname from the session
2781 asscociated with a safe token from generated by
2782 open-ils.actor.session.safe_token.
2783 @param safe_token Active safe token
2787 sub safe_token_home_lib {
2788 my( $self, $conn, $safe_token ) = @_;
2790 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2791 return $cache->get_cache( 'safe-token-home_lib-shortname-'. $safe_token );
2796 __PACKAGE__->register_method(
2797 method => 'slim_tree',
2798 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2801 my $tree = new_editor()->search_actor_org_unit(
2803 {"parent_ou" => undef },
2806 flesh_fields => { aou => ['children'] },
2807 order_by => { aou => 'name'},
2808 select => { aou => ["id","shortname", "name"]},
2813 return trim_tree($tree);
2819 return undef unless $tree;
2821 code => $tree->shortname,
2822 name => $tree->name,
2824 if( $tree->children and @{$tree->children} ) {
2825 $htree->{children} = [];
2826 for my $c (@{$tree->children}) {
2827 push( @{$htree->{children}}, trim_tree($c) );
2835 __PACKAGE__->register_method(
2836 method => "update_penalties",
2837 api_name => "open-ils.actor.user.penalties.update");
2839 sub update_penalties {
2840 my($self, $conn, $auth, $user_id) = @_;
2841 my $e = new_editor(authtoken=>$auth, xact => 1);
2842 return $e->die_event unless $e->checkauth;
2843 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
2844 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2845 my $evt = OpenILS::Utils::Penalty->calculate_penalties($e, $user_id, $e->requestor->ws_ou);
2846 return $evt if $evt;
2852 __PACKAGE__->register_method(
2853 method => "apply_penalty",
2854 api_name => "open-ils.actor.user.penalty.apply");
2857 my($self, $conn, $auth, $penalty) = @_;
2859 my $e = new_editor(authtoken=>$auth, xact => 1);
2860 return $e->die_event unless $e->checkauth;
2862 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2863 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2865 my $ptype = $e->retrieve_config_standing_penalty($penalty->standing_penalty) or return $e->die_event;
2868 (defined $ptype->org_depth) ?
2869 $U->org_unit_ancestor_at_depth($penalty->org_unit, $ptype->org_depth) :
2872 $penalty->org_unit($ctx_org);
2873 $penalty->staff($e->requestor->id);
2874 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
2877 return $penalty->id;
2880 __PACKAGE__->register_method(
2881 method => "remove_penalty",
2882 api_name => "open-ils.actor.user.penalty.remove");
2884 sub remove_penalty {
2885 my($self, $conn, $auth, $penalty) = @_;
2886 my $e = new_editor(authtoken=>$auth, xact => 1);
2887 return $e->die_event unless $e->checkauth;
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 $e->delete_actor_user_standing_penalty($penalty) or return $e->die_event;
2896 __PACKAGE__->register_method(
2897 method => "update_penalty_note",
2898 api_name => "open-ils.actor.user.penalty.note.update");
2900 sub update_penalty_note {
2901 my($self, $conn, $auth, $penalty_ids, $note) = @_;
2902 my $e = new_editor(authtoken=>$auth, xact => 1);
2903 return $e->die_event unless $e->checkauth;
2904 for my $penalty_id (@$penalty_ids) {
2905 my $penalty = $e->search_actor_user_standing_penalty( { id => $penalty_id } )->[0];
2906 if (! $penalty ) { return $e->die_event; }
2907 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2908 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2910 $penalty->note( $note ); $penalty->ischanged( 1 );
2912 $e->update_actor_user_standing_penalty($penalty) or return $e->die_event;
2918 __PACKAGE__->register_method(
2919 method => "ranged_penalty_thresholds",
2920 api_name => "open-ils.actor.grp_penalty_threshold.ranged.retrieve",
2924 sub ranged_penalty_thresholds {
2925 my($self, $conn, $auth, $context_org) = @_;
2926 my $e = new_editor(authtoken=>$auth);
2927 return $e->event unless $e->checkauth;
2928 return $e->event unless $e->allowed('VIEW_GROUP_PENALTY_THRESHOLD', $context_org);
2929 my $list = $e->search_permission_grp_penalty_threshold([
2930 {org_unit => $U->get_org_ancestors($context_org)},
2931 {order_by => {pgpt => 'id'}}
2933 $conn->respond($_) for @$list;
2939 __PACKAGE__->register_method(
2940 method => "user_retrieve_fleshed_by_id",
2942 api_name => "open-ils.actor.user.fleshed.retrieve",);
2944 sub user_retrieve_fleshed_by_id {
2945 my( $self, $client, $auth, $user_id, $fields ) = @_;
2946 my $e = new_editor(authtoken => $auth);
2947 return $e->event unless $e->checkauth;
2949 if( $e->requestor->id != $user_id ) {
2950 return $e->event unless $e->allowed('VIEW_USER');
2956 "standing_penalties",
2960 "stat_cat_entries" ];
2961 return new_flesh_user($user_id, $fields, $e);
2965 sub new_flesh_user {
2968 my $fields = shift || [];
2971 my $fetch_penalties = 0;
2972 if(grep {$_ eq 'standing_penalties'} @$fields) {
2973 $fields = [grep {$_ ne 'standing_penalties'} @$fields];
2974 $fetch_penalties = 1;
2977 my $user = $e->retrieve_actor_user(
2982 "flesh_fields" => { "au" => $fields }
2985 ) or return $e->event;
2988 if( grep { $_ eq 'addresses' } @$fields ) {
2990 $user->addresses([]) unless @{$user->addresses};
2991 # don't expose "replaced" addresses by default
2992 $user->addresses([grep {$_->id >= 0} @{$user->addresses}]);
2994 if( ref $user->billing_address ) {
2995 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2996 push( @{$user->addresses}, $user->billing_address );
3000 if( ref $user->mailing_address ) {
3001 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
3002 push( @{$user->addresses}, $user->mailing_address );
3007 if($fetch_penalties) {
3008 # grab the user penalties ranged for this location
3009 $user->standing_penalties(
3010 $e->search_actor_user_standing_penalty([
3013 {stop_date => undef},
3014 {stop_date => {'>' => 'now'}}
3016 org_unit => $U->get_org_ancestors($e->requestor->ws_ou)
3019 flesh_fields => {ausp => ['standing_penalty']}
3026 $user->clear_passwd();
3033 __PACKAGE__->register_method(
3034 method => "user_retrieve_parts",
3035 api_name => "open-ils.actor.user.retrieve.parts",);
3037 sub user_retrieve_parts {
3038 my( $self, $client, $auth, $user_id, $fields ) = @_;
3039 my $e = new_editor(authtoken => $auth);
3040 return $e->event unless $e->checkauth;
3041 if( $e->requestor->id != $user_id ) {
3042 return $e->event unless $e->allowed('VIEW_USER');
3045 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3046 push(@resp, $user->$_()) for(@$fields);
3052 __PACKAGE__->register_method(
3053 method => 'user_opt_in_enabled',
3054 api_name => 'open-ils.actor.user.org_unit_opt_in.enabled',
3056 @return 1 if user opt-in is globally enabled, 0 otherwise.
3059 sub user_opt_in_enabled {
3060 my($self, $conn) = @_;
3061 my $sc = OpenSRF::Utils::SettingsClient->new;
3062 return 1 if lc($sc->config_value(share => user => 'opt_in')) eq 'true';
3067 __PACKAGE__->register_method(
3068 method => 'user_opt_in_at_org',
3069 api_name => 'open-ils.actor.user.org_unit_opt_in.check',
3071 @param $auth The auth token
3072 @param user_id The ID of the user to test
3073 @return 1 if the user has opted in at the specified org,
3074 event on error, and 0 otherwise. /);
3075 sub user_opt_in_at_org {
3076 my($self, $conn, $auth, $user_id) = @_;
3078 # see if we even need to enforce the opt-in value
3079 return 1 unless user_opt_in_enabled($self);
3081 my $e = new_editor(authtoken => $auth);
3082 return $e->event unless $e->checkauth;
3083 my $org_id = $e->requestor->ws_ou;
3085 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3086 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3088 # user is automatically opted-in at the home org
3089 return 1 if $user->home_ou eq $org_id;
3091 my $vals = $e->search_actor_usr_org_unit_opt_in(
3092 {org_unit=>$org_id, usr=>$user_id},{idlist=>1});
3098 __PACKAGE__->register_method(
3099 method => 'create_user_opt_in_at_org',
3100 api_name => 'open-ils.actor.user.org_unit_opt_in.create',
3102 @param $auth The auth token
3103 @param user_id The ID of the user to test
3104 @return The ID of the newly created object, event on error./);
3106 sub create_user_opt_in_at_org {
3107 my($self, $conn, $auth, $user_id) = @_;
3109 my $e = new_editor(authtoken => $auth, xact=>1);
3110 return $e->die_event unless $e->checkauth;
3111 my $org_id = $e->requestor->ws_ou;
3113 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3114 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3116 my $opt_in = Fieldmapper::actor::usr_org_unit_opt_in->new;
3118 $opt_in->org_unit($org_id);
3119 $opt_in->usr($user_id);
3120 $opt_in->staff($e->requestor->id);
3121 $opt_in->opt_in_ts('now');
3122 $opt_in->opt_in_ws($e->requestor->wsid);
3124 $opt_in = $e->create_actor_usr_org_unit_opt_in($opt_in)
3125 or return $e->die_event;
3133 __PACKAGE__->register_method (
3134 method => 'retrieve_org_hours',
3135 api_name => 'open-ils.actor.org_unit.hours_of_operation.retrieve',
3137 Returns the hours of operation for a specified org unit
3138 @param authtoken The login session key
3139 @param org_id The org_unit ID
3143 sub retrieve_org_hours {
3144 my($self, $conn, $auth, $org_id) = @_;
3145 my $e = new_editor(authtoken => $auth);
3146 return $e->die_event unless $e->checkauth;
3147 $org_id ||= $e->requestor->ws_ou;
3148 return $e->retrieve_actor_org_unit_hours_of_operation($org_id);
3152 __PACKAGE__->register_method (
3153 method => 'verify_user_password',
3154 api_name => 'open-ils.actor.verify_user_password',
3156 Given a barcode or username and the MD5 encoded password,
3157 returns 1 if the password is correct. Returns 0 otherwise.
3161 sub verify_user_password {
3162 my($self, $conn, $auth, $barcode, $username, $password) = @_;
3163 my $e = new_editor(authtoken => $auth);
3164 return $e->die_event unless $e->checkauth;
3166 my $user_by_barcode;
3167 my $user_by_username;
3169 my $card = $e->search_actor_card([
3170 {barcode => $barcode},
3171 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0] or return 0;
3172 $user_by_barcode = $card->usr;
3173 $user = $user_by_barcode;
3176 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return 0;
3177 $user = $user_by_username;
3179 return 0 if (!$user);
3180 return 0 if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3181 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3182 return 1 if $user->passwd eq $password;
3186 __PACKAGE__->register_method (
3187 method => 'retrieve_usr_id_via_barcode_or_usrname',
3188 api_name => "open-ils.actor.user.retrieve_id_by_barcode_or_username",
3190 Given a barcode or username returns the id for the user or
3195 sub retrieve_usr_id_via_barcode_or_usrname {
3196 my($self, $conn, $auth, $barcode, $username) = @_;
3197 my $e = new_editor(authtoken => $auth);
3198 return $e->die_event unless $e->checkauth;
3199 my $id_as_barcode= OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.actor' => app_settings => 'id_as_barcode');
3201 my $user_by_barcode;
3202 my $user_by_username;
3203 $logger->info("$id_as_barcode is the ID as BARCODE");
3205 my $card = $e->search_actor_card([
3206 {barcode => $barcode},
3207 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0];
3208 if ($id_as_barcode =~ /^t/i) {
3210 $user = $e->retrieve_actor_user($barcode);
3211 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$user);
3213 $user_by_barcode = $card->usr;
3214 $user = $user_by_barcode;
3217 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$card);
3218 $user_by_barcode = $card->usr;
3219 $user = $user_by_barcode;
3224 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return OpenILS::Event->new( 'ACTOR_USR_NOT_FOUND' );
3226 $user = $user_by_username;
3228 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if (!$user);
3229 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3230 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3235 __PACKAGE__->register_method (
3236 method => 'merge_users',
3237 api_name => 'open-ils.actor.user.merge',
3240 Given a list of source users and destination user, transfer all data from the source
3241 to the dest user and delete the source user. All user related data is
3242 transferred, including circulations, holds, bookbags, etc.
3248 my($self, $conn, $auth, $master_id, $user_ids, $options) = @_;
3249 my $e = new_editor(xact => 1, authtoken => $auth);
3250 return $e->die_event unless $e->checkauth;
3252 # disallow the merge if any subordinate accounts are in collections
3253 my $colls = $e->search_money_collections_tracker({usr => $user_ids}, {idlist => 1});
3254 return OpenILS::Event->new('MERGED_USER_IN_COLLECTIONS', payload => $user_ids) if @$colls;
3256 my $master_user = $e->retrieve_actor_user($master_id) or return $e->die_event;
3257 my $del_addrs = ($U->ou_ancestor_setting_value(
3258 $master_user->home_ou, 'circ.user_merge.delete_addresses', $e)) ? 't' : 'f';
3259 my $del_cards = ($U->ou_ancestor_setting_value(
3260 $master_user->home_ou, 'circ.user_merge.delete_cards', $e)) ? 't' : 'f';
3261 my $deactivate_cards = ($U->ou_ancestor_setting_value(
3262 $master_user->home_ou, 'circ.user_merge.deactivate_cards', $e)) ? 't' : 'f';
3264 for my $src_id (@$user_ids) {
3265 my $src_user = $e->retrieve_actor_user($src_id) or return $e->die_event;
3267 return $e->die_event unless $e->allowed('MERGE_USERS', $src_user->home_ou);
3268 if($src_user->home_ou ne $master_user->home_ou) {
3269 return $e->die_event unless $e->allowed('MERGE_USERS', $master_user->home_ou);
3272 return $e->die_event unless
3273 $e->json_query({from => [
3288 __PACKAGE__->register_method (
3289 method => 'approve_user_address',
3290 api_name => 'open-ils.actor.user.pending_address.approve',
3297 sub approve_user_address {
3298 my($self, $conn, $auth, $addr) = @_;
3299 my $e = new_editor(xact => 1, authtoken => $auth);
3300 return $e->die_event unless $e->checkauth;
3302 # if the caller passes an address object, assume they want to
3303 # update it first before approving it
3304 $e->update_actor_user_address($addr) or return $e->die_event;
3306 $addr = $e->retrieve_actor_user_address($addr) or return $e->die_event;
3308 my $user = $e->retrieve_actor_user($addr->usr);
3309 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3310 my $result = $e->json_query({from => ['actor.approve_pending_address', $addr->id]})->[0]
3311 or return $e->die_event;
3313 return [values %$result]->[0];
3317 __PACKAGE__->register_method (
3318 method => 'retrieve_friends',
3319 api_name => 'open-ils.actor.friends.retrieve',
3322 returns { confirmed: [], pending_out: [], pending_in: []}
3323 pending_out are users I'm requesting friendship with
3324 pending_in are users requesting friendship with me
3329 sub retrieve_friends {
3330 my($self, $conn, $auth, $user_id, $options) = @_;
3331 my $e = new_editor(authtoken => $auth);
3332 return $e->event unless $e->checkauth;
3333 $user_id ||= $e->requestor->id;
3335 if($user_id != $e->requestor->id) {
3336 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3337 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3340 return OpenILS::Application::Actor::Friends->retrieve_friends(
3341 $e, $user_id, $options);
3346 __PACKAGE__->register_method (
3347 method => 'apply_friend_perms',
3348 api_name => 'open-ils.actor.friends.perms.apply',
3354 sub apply_friend_perms {
3355 my($self, $conn, $auth, $user_id, $delegate_id, @perms) = @_;
3356 my $e = new_editor(authtoken => $auth, xact => 1);
3357 return $e->event unless $e->checkauth;
3359 if($user_id != $e->requestor->id) {
3360 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3361 return $e->die_event unless $e->allowed('VIEW_USER', $user->home_ou);
3364 for my $perm (@perms) {
3366 OpenILS::Application::Actor::Friends->apply_friend_perm(
3367 $e, $user_id, $delegate_id, $perm);
3368 return $evt if $evt;
3376 __PACKAGE__->register_method (
3377 method => 'update_user_pending_address',
3378 api_name => 'open-ils.actor.user.address.pending.cud'
3381 sub update_user_pending_address {
3382 my($self, $conn, $auth, $addr) = @_;
3383 my $e = new_editor(authtoken => $auth, xact => 1);
3384 return $e->event unless $e->checkauth;
3386 if($addr->usr != $e->requestor->id) {
3387 my $user = $e->retrieve_actor_user($addr->usr) or return $e->die_event;
3388 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3392 $e->create_actor_user_address($addr) or return $e->die_event;
3393 } elsif($addr->isdeleted) {
3394 $e->delete_actor_user_address($addr) or return $e->die_event;
3396 $e->update_actor_user_address($addr) or return $e->die_event;
3404 __PACKAGE__->register_method (
3405 method => 'user_events',
3406 api_name => 'open-ils.actor.user.events.circ',
3409 __PACKAGE__->register_method (
3410 method => 'user_events',
3411 api_name => 'open-ils.actor.user.events.ahr',
3416 my($self, $conn, $auth, $user_id, $filters) = @_;
3417 my $e = new_editor(authtoken => $auth);
3418 return $e->event unless $e->checkauth;
3420 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3421 my $user_field = 'usr';
3424 $filters->{target} = {
3425 select => { $obj_type => ['id'] },
3427 where => {usr => $user_id}
3430 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3431 if($e->requestor->id != $user_id) {
3432 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3435 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3436 my $req = $ses->request('open-ils.trigger.events_by_target',
3437 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3439 while(my $resp = $req->recv) {
3440 my $val = $resp->content;
3441 my $tgt = $val->target;
3443 if($obj_type eq 'circ') {
3444 $tgt->target_copy($e->retrieve_asset_copy($tgt->target_copy));
3446 } elsif($obj_type eq 'ahr') {
3447 $tgt->current_copy($e->retrieve_asset_copy($tgt->current_copy))
3448 if $tgt->current_copy;
3451 $conn->respond($val) if $val;
3457 __PACKAGE__->register_method (
3458 method => 'copy_events',
3459 api_name => 'open-ils.actor.copy.events.circ',
3462 __PACKAGE__->register_method (
3463 method => 'copy_events',
3464 api_name => 'open-ils.actor.copy.events.ahr',
3469 my($self, $conn, $auth, $copy_id, $filters) = @_;
3470 my $e = new_editor(authtoken => $auth);
3471 return $e->event unless $e->checkauth;
3473 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3475 my $copy = $e->retrieve_asset_copy($copy_id) or return $e->event;
3477 my $copy_field = 'target_copy';
3478 $copy_field = 'current_copy' if $obj_type eq 'ahr';
3481 $filters->{target} = {
3482 select => { $obj_type => ['id'] },
3484 where => {$copy_field => $copy_id}
3488 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3489 my $req = $ses->request('open-ils.trigger.events_by_target',
3490 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3492 while(my $resp = $req->recv) {
3493 my $val = $resp->content;
3494 my $tgt = $val->target;
3496 my $user = $e->retrieve_actor_user($tgt->usr);
3497 if($e->requestor->id != $user->id) {
3498 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3501 $tgt->$copy_field($copy);
3504 $conn->respond($val) if $val;
3513 __PACKAGE__->register_method (
3514 method => 'update_events',
3515 api_name => 'open-ils.actor.user.event.cancel.batch',
3518 __PACKAGE__->register_method (
3519 method => 'update_events',
3520 api_name => 'open-ils.actor.user.event.reset.batch',
3525 my($self, $conn, $auth, $event_ids) = @_;
3526 my $e = new_editor(xact => 1, authtoken => $auth);
3527 return $e->die_event unless $e->checkauth;
3530 for my $id (@$event_ids) {
3532 # do a little dance to determine what user we are ultimately affecting
3533 my $event = $e->retrieve_action_trigger_event([
3536 flesh_fields => {atev => ['event_def'], atevdef => ['hook']}
3538 ]) or return $e->die_event;
3541 if($event->event_def->hook->core_type eq 'circ') {
3542 $user_id = $e->retrieve_action_circulation($event->target)->usr;
3543 } elsif($event->event_def->hook->core_type eq 'ahr') {
3544 $user_id = $e->retrieve_action_hold_request($event->target)->usr;
3549 my $user = $e->retrieve_actor_user($user_id);
3550 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3552 if($self->api_name =~ /cancel/) {
3553 $event->state('invalid');
3554 } elsif($self->api_name =~ /reset/) {
3555 $event->clear_start_time;
3556 $event->clear_update_time;
3557 $event->state('pending');
3560 $e->update_action_trigger_event($event) or return $e->die_event;
3561 $conn->respond({maximum => scalar(@$event_ids), progress => $x++});
3565 return {complete => 1};
3569 __PACKAGE__->register_method (
3570 method => 'really_delete_user',
3571 api_name => 'open-ils.actor.user.delete',
3573 It anonymizes all personally identifiable information in actor.usr. By calling actor.usr_purge_data()
3574 it also purges related data from other tables, sometimes by transferring it to a designated destination user.
3575 The usrname field (along with first_given_name and family_name) is updated to id '-PURGED-' now().
3576 dest_usr_id is only required when deleting a user that performs staff functions.
3580 sub really_delete_user {
3581 my($self, $conn, $auth, $user_id, $dest_user_id) = @_;
3582 my $e = new_editor(authtoken => $auth, xact => 1);
3583 return $e->die_event unless $e->checkauth;
3584 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3585 return $e->die_event unless $e->allowed('DELETE_USER', $user->home_ou);
3586 my $stat = $e->json_query(
3587 {from => ['actor.usr_delete', $user_id, $dest_user_id]})->[0]
3588 or return $e->die_event;
3595 __PACKAGE__->register_method (
3596 method => 'user_payments',
3597 api_name => 'open-ils.actor.user.payments.retrieve',
3600 Returns all payments for a given user. Default order is newest payments first.
3601 @param auth Authentication token
3602 @param user_id The user ID
3603 @param filters An optional hash of filters, including limit, offset, and order_by definitions
3608 my($self, $conn, $auth, $user_id, $filters) = @_;
3611 my $e = new_editor(authtoken => $auth);
3612 return $e->die_event unless $e->checkauth;
3614 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3615 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS', $user->home_ou);
3617 # Find all payments for all transactions for user $user_id
3619 select => {mp => ['id']},
3624 select => {mbt => ['id']},
3626 where => {usr => $user_id}
3630 order_by => [{ # by default, order newest payments first
3632 field => 'payment_ts',
3637 for (qw/order_by limit offset/) {
3638 $query->{$_} = $filters->{$_} if defined $filters->{$_};
3641 if(defined $filters->{where}) {
3642 foreach (keys %{$filters->{where}}) {
3643 # don't allow the caller to expand the result set to other users
3644 $query->{where}->{$_} = $filters->{where}->{$_} unless $_ eq 'xact';
3648 my $payment_ids = $e->json_query($query);
3649 for my $pid (@$payment_ids) {
3650 my $pay = $e->retrieve_money_payment([
3655 mbt => ['summary', 'circulation', 'grocery'],
3656 circ => ['target_copy'],
3657 acp => ['call_number'],
3665 xact_type => $pay->xact->summary->xact_type,
3666 last_billing_type => $pay->xact->summary->last_billing_type,
3669 if($pay->xact->summary->xact_type eq 'circulation') {
3670 $resp->{barcode} = $pay->xact->circulation->target_copy->barcode;
3671 $resp->{title} = $U->record_to_mvr($pay->xact->circulation->target_copy->call_number->record)->title;
3674 $pay->xact($pay->xact->id); # de-flesh
3675 $conn->respond($resp);
3683 __PACKAGE__->register_method (
3684 method => 'negative_balance_users',
3685 api_name => 'open-ils.actor.users.negative_balance',
3688 Returns all users that have an overall negative balance
3689 @param auth Authentication token
3690 @param org_id The context org unit as an ID or list of IDs. This will be the home
3691 library of the user. If no org_unit is specified, no org unit filter is applied
3695 sub negative_balance_users {
3696 my($self, $conn, $auth, $org_id) = @_;
3698 my $e = new_editor(authtoken => $auth);
3699 return $e->die_event unless $e->checkauth;
3700 return $e->die_event unless $e->allowed('VIEW_USER', $org_id);
3704 mous => ['usr', 'balance_owed'],
3707 {column => 'last_billing_ts', transform => 'max', aggregate => 1},
3708 {column => 'last_payment_ts', transform => 'max', aggregate => 1},
3725 where => {'+mous' => {balance_owed => {'<' => 0}}}
3728 $query->{from}->{mous}->{au}->{filter}->{home_ou} = $org_id if $org_id;
3730 my $list = $e->json_query($query, {timeout => 600});
3732 for my $data (@$list) {
3734 usr => $e->retrieve_actor_user([$data->{usr}, {flesh => 1, flesh_fields => {au => ['card']}}]),
3735 balance_owed => $data->{balance_owed},
3736 last_billing_activity => max($data->{last_billing_ts}, $data->{last_payment_ts})