1 package OpenILS::Application::Actor;
2 use OpenILS::Application;
3 use base qw/OpenILS::Application/;
4 use strict; use warnings;
6 $Data::Dumper::Indent = 0;
9 use Digest::MD5 qw(md5_hex);
11 use OpenSRF::EX qw(:try);
14 use OpenILS::Application::AppUtils;
16 use OpenILS::Utils::Fieldmapper;
17 use OpenILS::Utils::ModsParser;
18 use OpenSRF::Utils::Logger qw/$logger/;
19 use OpenSRF::Utils qw/:datetime/;
20 use OpenSRF::Utils::SettingsClient;
22 use OpenSRF::Utils::Cache;
24 use OpenSRF::Utils::JSON;
26 use DateTime::Format::ISO8601;
27 use OpenILS::Const qw/:const/;
29 use OpenILS::Application::Actor::Container;
30 use OpenILS::Application::Actor::ClosedDates;
31 use OpenILS::Application::Actor::UserGroups;
32 use OpenILS::Application::Actor::Friends;
33 use OpenILS::Application::Actor::Stage;
35 use OpenILS::Utils::CStoreEditor qw/:funcs/;
36 use OpenILS::Utils::Penalty;
37 use List::Util qw/max/;
40 OpenILS::Application::Actor::Container->initialize();
41 OpenILS::Application::Actor::UserGroups->initialize();
42 OpenILS::Application::Actor::ClosedDates->initialize();
45 my $apputils = "OpenILS::Application::AppUtils";
48 sub _d { warn "Patron:\n" . Dumper(shift()); }
51 my $set_user_settings;
55 #__PACKAGE__->register_method(
56 # method => "allowed_test",
57 # api_name => "open-ils.actor.allowed_test",
60 # my($self, $conn, $auth, $orgid, $permcode) = @_;
61 # my $e = new_editor(authtoken => $auth);
62 # return $e->die_event unless $e->checkauth;
66 # permcode => $permcode,
67 # result => $e->allowed($permcode, $orgid)
71 __PACKAGE__->register_method(
72 method => "update_user_setting",
73 api_name => "open-ils.actor.patron.settings.update",
75 sub update_user_setting {
76 my($self, $conn, $auth, $user_id, $settings) = @_;
77 my $e = new_editor(xact => 1, authtoken => $auth);
78 return $e->die_event unless $e->checkauth;
80 $user_id = $e->requestor->id unless defined $user_id;
82 unless($e->requestor->id == $user_id) {
83 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
84 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
87 for my $name (keys %$settings) {
88 my $val = $$settings{$name};
89 my $set = $e->search_actor_user_setting({usr => $user_id, name => $name})->[0];
92 $val = OpenSRF::Utils::JSON->perl2JSON($val);
95 $e->update_actor_user_setting($set) or return $e->die_event;
97 $set = Fieldmapper::actor::user_setting->new;
101 $e->create_actor_user_setting($set) or return $e->die_event;
104 $e->delete_actor_user_setting($set) or return $e->die_event;
113 __PACKAGE__->register_method(
114 method => "set_ou_settings",
115 api_name => "open-ils.actor.org_unit.settings.update",
118 Updates the value for a given org unit setting. The permission to update
119 an org unit setting is either the UPDATE_ORG_UNIT_SETTING_ALL, or a specific
120 permission specified in the update_perm column of the
121 config.org_unit_setting_type table's row corresponding to the setting being
124 {desc => 'authtoken', type => 'string'},
125 {desc => 'org unit id', type => 'number'},
126 {desc => q/Hash of setting name-value pairs/, type => 'hash'},
128 return => {desc => '1 on success, Event on error'}
132 sub set_ou_settings {
133 my( $self, $client, $auth, $org_id, $settings ) = @_;
135 my $e = new_editor(authtoken => $auth, xact => 1);
136 return $e->die_event unless $e->checkauth;
138 my $all_allowed = $e->allowed("UPDATE_ORG_UNIT_SETTING_ALL", $org_id);
140 for my $name (keys %$settings) {
141 my $val = $$settings{$name};
143 my $type = $e->retrieve_config_org_unit_setting_type([
145 {flesh => 1, flesh_fields => {'coust' => ['update_perm']}}
146 ]) or return $e->die_event;
147 my $set = $e->search_actor_org_unit_setting({org_unit => $org_id, name => $name})->[0];
149 # If there is no relevant permission, the default assumption will
150 # be, "no, the caller cannot change that value."
151 return $e->die_event unless ($all_allowed ||
152 ($type->update_perm && $e->allowed($type->update_perm->code, $org_id)));
155 $val = OpenSRF::Utils::JSON->perl2JSON($val);
158 $e->update_actor_org_unit_setting($set) or return $e->die_event;
160 $set = Fieldmapper::actor::org_unit_setting->new;
161 $set->org_unit($org_id);
164 $e->create_actor_org_unit_setting($set) or return $e->die_event;
167 $e->delete_actor_org_unit_setting($set) or return $e->die_event;
175 __PACKAGE__->register_method(
176 method => "user_settings",
177 api_name => "open-ils.actor.patron.settings.retrieve",
180 my( $self, $client, $auth, $user_id, $setting ) = @_;
182 my $e = new_editor(authtoken => $auth);
183 return $e->event unless $e->checkauth;
184 $user_id = $e->requestor->id unless defined $user_id;
186 my $patron = $e->retrieve_actor_user($user_id) or return $e->event;
187 if($e->requestor->id != $user_id) {
188 return $e->event unless $e->allowed('VIEW_USER', $patron->home_ou);
192 my($e, $user_id, $setting) = @_;
193 my $val = $e->search_actor_user_setting({usr => $user_id, name => $setting})->[0];
194 return undef unless $val; # XXX this should really return undef, but needs testing
195 return OpenSRF::Utils::JSON->JSON2perl($val->value);
199 if(ref $setting eq 'ARRAY') {
201 $settings{$_} = get_setting($e, $user_id, $_) for @$setting;
204 return get_setting($e, $user_id, $setting);
207 my $s = $e->search_actor_user_setting({usr => $user_id});
208 return { map { ( $_->name => OpenSRF::Utils::JSON->JSON2perl($_->value) ) } @$s };
214 __PACKAGE__->register_method(
215 method => "ranged_ou_settings",
216 api_name => "open-ils.actor.org_unit_setting.values.ranged.retrieve",
219 Retrieves all org unit settings for the given org_id, up to whatever limit
220 is implied for retrieving OU settings by the authenticated users' permissions./,
222 {desc => 'authtoken', type => 'string'},
223 {desc => 'org unit id', type => 'number'},
225 return => {desc => 'A hashref of "ranged" settings'}
228 sub ranged_ou_settings {
229 my( $self, $client, $auth, $org_id ) = @_;
231 my $e = new_editor(authtoken => $auth);
232 return $e->event unless $e->checkauth;
235 my $org_list = $U->get_org_ancestors($org_id);
236 my $settings = $e->search_actor_org_unit_setting({org_unit => $org_list});
237 $org_list = [ reverse @$org_list ];
239 # start at the context org and capture the setting value
240 # without clobbering settings we've already captured
241 for my $this_org_id (@$org_list) {
243 my @sets = grep { $_->org_unit == $this_org_id } @$settings;
245 for my $set (@sets) {
246 my $type = $e->retrieve_config_org_unit_setting_type([
248 {flesh => 1, flesh_fields => {coust => ['view_perm']}}
251 # If there is no relevant permission, the default assumption will
252 # be, "yes, the caller can have that value."
253 if ($type && $type->view_perm) {
254 next if not $e->allowed($type->view_perm->code, $org_id);
257 $ranged_settings{$set->name} = OpenSRF::Utils::JSON->JSON2perl($set->value)
258 unless defined $ranged_settings{$set->name};
262 return \%ranged_settings;
267 __PACKAGE__->register_method(
268 api_name => 'open-ils.actor.ou_setting.ancestor_default',
269 method => 'ou_ancestor_setting',
271 desc => 'Get the org unit setting value associated with the setting name as seen from the specified org unit. ' .
272 'IF AND ONLY IF an authentication token is provided, this method will make sure that the given ' .
273 'user has permission to view that setting, if there is a permission associated with the setting.' ,
275 { desc => 'org unit id', type => 'number' },
276 { desc => 'setting name', type => 'string' },
277 { desc => 'authtoken (optional)', type => 'string' }
279 return => {desc => 'A value for the org unit setting, or undef'}
283 # ------------------------------------------------------------------
284 # Attempts to find the org setting value for a given org. if not
285 # found at the requested org, searches up the org tree until it
286 # finds a parent that has the requested setting.
287 # when found, returns { org => $id, value => $value }
288 # otherwise, returns NULL
289 # ------------------------------------------------------------------
290 sub ou_ancestor_setting {
291 my( $self, $client, $orgid, $name, $auth ) = @_;
292 return $U->ou_ancestor_setting($orgid, $name, undef, $auth);
295 __PACKAGE__->register_method(
296 api_name => 'open-ils.actor.ou_setting.ancestor_default.batch',
297 method => 'ou_ancestor_setting_batch',
299 desc => 'Get org unit setting name => value pairs for a list of names, as seen from the specified org unit. ' .
300 'IF AND ONLY IF an authentication token is provided, this method will make sure that the given ' .
301 'user has permission to view that setting, if there is a permission associated with the setting.' ,
303 { desc => 'org unit id', type => 'number' },
304 { desc => 'setting name list', type => 'array' },
305 { desc => 'authtoken (optional)', type => 'string' }
307 return => {desc => 'A hash with name => value pairs for the org unit settings'}
310 sub ou_ancestor_setting_batch {
311 my( $self, $client, $orgid, $name_list, $auth ) = @_;
313 $values{$_} = $U->ou_ancestor_setting($orgid, $_, undef, $auth) for @$name_list;
319 __PACKAGE__->register_method(
320 method => "update_patron",
321 api_name => "open-ils.actor.patron.update",);
324 my( $self, $client, $user_session, $patron ) = @_;
326 my $session = $apputils->start_db_session();
329 $logger->info("Creating new patron...") if $patron->isnew;
330 $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
332 my( $user_obj, $evt ) = $U->checkses($user_session);
335 $evt = check_group_perm($session, $user_obj, $patron);
339 # $new_patron is the patron in progress. $patron is the original patron
340 # passed in with the method. new_patron will change as the components
341 # of patron are added/updated.
345 # unflesh the real items on the patron
346 $patron->card( $patron->card->id ) if(ref($patron->card));
347 $patron->billing_address( $patron->billing_address->id )
348 if(ref($patron->billing_address));
349 $patron->mailing_address( $patron->mailing_address->id )
350 if(ref($patron->mailing_address));
352 # create/update the patron first so we can use his id
353 if($patron->isnew()) {
354 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
356 } else { $new_patron = $patron; }
358 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
361 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
364 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
367 # re-update the patron if anything has happened to him during this process
368 if($new_patron->ischanged()) {
369 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
373 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
376 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
379 $apputils->commit_db_session($session);
381 $evt = apply_invalid_addr_penalty($patron);
384 my $tses = OpenSRF::AppSession->create('open-ils.trigger');
386 $tses->request('open-ils.trigger.event.autocreate', 'au.create', $new_patron, $new_patron->home_ou);
388 $tses->request('open-ils.trigger.event.autocreate', 'au.update', $new_patron, $new_patron->home_ou);
391 return flesh_user($new_patron->id(), new_editor(requestor => $user_obj, xact => 1));
394 sub apply_invalid_addr_penalty {
396 my $e = new_editor(xact => 1);
398 # grab the invalid address penalty if set
399 my $penalties = OpenILS::Utils::Penalty->retrieve_usr_penalties($e, $patron->id, $patron->home_ou);
401 my ($addr_penalty) = grep
402 { $_->standing_penalty->name eq 'INVALID_PATRON_ADDRESS' } @$penalties;
404 # do we enforce invalid address penalty
405 my $enforce = $U->ou_ancestor_setting_value(
406 $patron->home_ou, 'circ.patron_invalid_address_apply_penalty') || 0;
408 my $addrs = $e->search_actor_user_address(
409 {usr => $patron->id, valid => 'f', id => {'>' => 0}}, {idlist => 1});
410 my $addr_count = scalar(@$addrs);
412 if($addr_count == 0 and $addr_penalty) {
414 # regardless of any settings, remove the penalty when the user has no invalid addresses
415 $e->delete_actor_user_standing_penalty($addr_penalty) or return $e->die_event;
418 } elsif($enforce and $addr_count > 0 and !$addr_penalty) {
420 my $ptype = $e->retrieve_config_standing_penalty(29) or return $e->die_event;
421 my $depth = $ptype->org_depth;
422 my $ctx_org = $U->org_unit_ancestor_at_depth($patron->home_ou, $depth) if defined $depth;
423 $ctx_org = $patron->home_ou unless defined $ctx_org;
425 my $penalty = Fieldmapper::actor::user_standing_penalty->new;
426 $penalty->usr($patron->id);
427 $penalty->org_unit($ctx_org);
428 $penalty->standing_penalty(OILS_PENALTY_INVALID_PATRON_ADDRESS);
430 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
444 return new_flesh_user($id, [
447 "standing_penalties",
451 "stat_cat_entries" ], $e );
459 # clone and clear stuff that would break the database
463 my $new_patron = $patron->clone;
465 $new_patron->clear_billing_address();
466 $new_patron->clear_mailing_address();
467 $new_patron->clear_addresses();
468 $new_patron->clear_card();
469 $new_patron->clear_cards();
470 $new_patron->clear_id();
471 $new_patron->clear_isnew();
472 $new_patron->clear_ischanged();
473 $new_patron->clear_isdeleted();
474 $new_patron->clear_stat_cat_entries();
475 $new_patron->clear_permissions();
476 $new_patron->clear_standing_penalties();
486 my $user_obj = shift;
488 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
489 return (undef, $evt) if $evt;
491 my $ex = $session->request(
492 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
494 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
497 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
499 my $id = $session->request(
500 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
501 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
503 $logger->info("Successfully created new user [$id] in DB");
505 return ( $session->request(
506 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
510 sub check_group_perm {
511 my( $session, $requestor, $patron ) = @_;
514 # first let's see if the requestor has
515 # priveleges to update this user in any way
516 if( ! $patron->isnew ) {
517 my $p = $session->request(
518 'open-ils.storage.direct.actor.user.retrieve', $patron->id )->gather(1);
520 # If we are the requestor (trying to update our own account)
521 # and we are not trying to change our profile, we're good
522 if( $p->id == $requestor->id and
523 $p->profile == $patron->profile ) {
528 $evt = group_perm_failed($session, $requestor, $p);
532 # They are allowed to edit this patron.. can they put the
533 # patron into the group requested?
534 $evt = group_perm_failed($session, $requestor, $patron);
540 sub group_perm_failed {
541 my( $session, $requestor, $patron ) = @_;
545 my $grpid = $patron->profile;
549 $logger->debug("user update looking for group perm for group $grpid");
550 $grp = $session->request(
551 'open-ils.storage.direct.permission.grp_tree.retrieve', $grpid )->gather(1);
552 return OpenILS::Event->new('PERMISSION_GRP_TREE_NOT_FOUND') unless $grp;
554 } while( !($perm = $grp->application_perm) and ($grpid = $grp->parent) );
556 $logger->info("user update checking perm $perm on user ".
557 $requestor->id." for update/create on user username=".$patron->usrname);
559 my $evt = $U->check_perms($requestor->id, $patron->home_ou, $perm);
567 my( $session, $patron, $user_obj, $noperm) = @_;
569 $logger->info("Updating patron ".$patron->id." in DB");
574 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
575 return (undef, $evt) if $evt;
578 # update the password by itself to avoid the password protection magic
579 if( $patron->passwd ) {
580 my $s = $session->request(
581 'open-ils.storage.direct.actor.user.remote_update',
582 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
583 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
584 $patron->clear_passwd;
587 if(!$patron->ident_type) {
588 $patron->clear_ident_type;
589 $patron->clear_ident_value;
592 $evt = verify_last_xact($session, $patron);
593 return (undef, $evt) if $evt;
595 my $stat = $session->request(
596 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
597 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
602 sub verify_last_xact {
603 my( $session, $patron ) = @_;
604 return undef unless $patron->id and $patron->id > 0;
605 my $p = $session->request(
606 'open-ils.storage.direct.actor.user.retrieve', $patron->id)->gather(1);
607 my $xact = $p->last_xact_id;
608 return undef unless $xact;
609 $logger->info("user xact = $xact, saving with xact " . $patron->last_xact_id);
610 return OpenILS::Event->new('XACT_COLLISION')
611 if $xact != $patron->last_xact_id;
616 sub _check_dup_ident {
617 my( $session, $patron ) = @_;
619 return undef unless $patron->ident_value;
622 ident_type => $patron->ident_type,
623 ident_value => $patron->ident_value,
626 $logger->debug("patron update searching for dup ident values: " .
627 $patron->ident_type . ':' . $patron->ident_value);
629 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
631 my $dups = $session->request(
632 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
635 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
642 sub _add_update_addresses {
646 my $new_patron = shift;
650 my $current_id; # id of the address before creation
652 for my $address (@{$patron->addresses()}) {
654 next unless ref $address;
655 $current_id = $address->id();
657 if( $patron->billing_address() and
658 $patron->billing_address() == $current_id ) {
659 $logger->info("setting billing addr to $current_id");
660 $new_patron->billing_address($address->id());
661 $new_patron->ischanged(1);
664 if( $patron->mailing_address() and
665 $patron->mailing_address() == $current_id ) {
666 $new_patron->mailing_address($address->id());
667 $logger->info("setting mailing addr to $current_id");
668 $new_patron->ischanged(1);
672 if($address->isnew()) {
674 $address->usr($new_patron->id());
676 ($address, $evt) = _add_address($session,$address);
677 return (undef, $evt) if $evt;
679 # we need to get the new id
680 if( $patron->billing_address() and
681 $patron->billing_address() == $current_id ) {
682 $new_patron->billing_address($address->id());
683 $logger->info("setting billing addr to $current_id");
684 $new_patron->ischanged(1);
687 if( $patron->mailing_address() and
688 $patron->mailing_address() == $current_id ) {
689 $new_patron->mailing_address($address->id());
690 $logger->info("setting mailing addr to $current_id");
691 $new_patron->ischanged(1);
694 } elsif($address->ischanged() ) {
696 ($address, $evt) = _update_address($session, $address);
697 return (undef, $evt) if $evt;
699 } elsif($address->isdeleted() ) {
701 if( $address->id() == $new_patron->mailing_address() ) {
702 $new_patron->clear_mailing_address();
703 ($new_patron, $evt) = _update_patron($session, $new_patron);
704 return (undef, $evt) if $evt;
707 if( $address->id() == $new_patron->billing_address() ) {
708 $new_patron->clear_billing_address();
709 ($new_patron, $evt) = _update_patron($session, $new_patron);
710 return (undef, $evt) if $evt;
713 $evt = _delete_address($session, $address);
714 return (undef, $evt) if $evt;
718 return ( $new_patron, undef );
722 # adds an address to the db and returns the address with new id
724 my($session, $address) = @_;
725 $address->clear_id();
727 $logger->info("Creating new address at street ".$address->street1);
729 # put the address into the database
730 my $id = $session->request(
731 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
732 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
735 return ($address, undef);
739 sub _update_address {
740 my( $session, $address ) = @_;
742 $logger->info("Updating address ".$address->id." in the DB");
744 my $stat = $session->request(
745 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
747 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
748 return ($address, undef);
753 sub _add_update_cards {
757 my $new_patron = shift;
761 my $virtual_id; #id of the card before creation
762 for my $card (@{$patron->cards()}) {
764 $card->usr($new_patron->id());
766 if(ref($card) and $card->isnew()) {
768 $virtual_id = $card->id();
769 ( $card, $evt ) = _add_card($session,$card);
770 return (undef, $evt) if $evt;
772 #if(ref($patron->card)) { $patron->card($patron->card->id); }
773 if($patron->card() == $virtual_id) {
774 $new_patron->card($card->id());
775 $new_patron->ischanged(1);
778 } elsif( ref($card) and $card->ischanged() ) {
779 $evt = _update_card($session, $card);
780 return (undef, $evt) if $evt;
784 return ( $new_patron, undef );
788 # adds an card to the db and returns the card with new id
790 my( $session, $card ) = @_;
793 $logger->info("Adding new patron card ".$card->barcode);
795 my $id = $session->request(
796 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
797 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
798 $logger->info("Successfully created patron card $id");
801 return ( $card, undef );
805 # returns event on error. returns undef otherwise
807 my( $session, $card ) = @_;
808 $logger->info("Updating patron card ".$card->id);
810 my $stat = $session->request(
811 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
812 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
819 # returns event on error. returns undef otherwise
820 sub _delete_address {
821 my( $session, $address ) = @_;
823 $logger->info("Deleting address ".$address->id." from DB");
825 my $stat = $session->request(
826 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
828 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
834 sub _add_survey_responses {
835 my ($session, $patron, $new_patron) = @_;
837 $logger->info( "Updating survey responses for patron ".$new_patron->id );
839 my $responses = $patron->survey_responses;
843 $_->usr($new_patron->id) for (@$responses);
845 my $evt = $U->simplereq( "open-ils.circ",
846 "open-ils.circ.survey.submit.user_id", $responses );
848 return (undef, $evt) if defined($U->event_code($evt));
852 return ( $new_patron, undef );
856 sub _create_stat_maps {
858 my($session, $user_session, $patron, $new_patron) = @_;
860 my $maps = $patron->stat_cat_entries();
862 for my $map (@$maps) {
864 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
866 if ($map->isdeleted()) {
867 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
869 } elsif ($map->isnew()) {
870 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
875 $map->target_usr($new_patron->id);
878 $logger->info("Updating stat entry with method $method and map $map");
880 my $stat = $session->request($method, $map)->gather(1);
881 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
885 return ($new_patron, undef);
888 sub _create_perm_maps {
890 my($session, $user_session, $patron, $new_patron) = @_;
892 my $maps = $patron->permissions;
894 for my $map (@$maps) {
896 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
897 if ($map->isdeleted()) {
898 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
899 } elsif ($map->isnew()) {
900 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
905 $map->usr($new_patron->id);
907 #warn( "Updating permissions with method $method and session $user_session and map $map" );
908 $logger->info( "Updating permissions with method $method and map $map" );
910 my $stat = $session->request($method, $map)->gather(1);
911 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
915 return ($new_patron, undef);
919 __PACKAGE__->register_method(
920 method => "set_user_work_ous",
921 api_name => "open-ils.actor.user.work_ous.update",
924 sub set_user_work_ous {
930 my( $requestor, $evt ) = $apputils->checksesperm( $ses, 'ASSIGN_WORK_ORG_UNIT' );
933 my $session = $apputils->start_db_session();
935 for my $map (@$maps) {
937 my $method = "open-ils.storage.direct.permission.usr_work_ou_map.update";
938 if ($map->isdeleted()) {
939 $method = "open-ils.storage.direct.permission.usr_work_ou_map.delete";
940 } elsif ($map->isnew()) {
941 $method = "open-ils.storage.direct.permission.usr_work_ou_map.create";
945 #warn( "Updating permissions with method $method and session $ses and map $map" );
946 $logger->info( "Updating work_ou map with method $method and map $map" );
948 my $stat = $session->request($method, $map)->gather(1);
949 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
953 $apputils->commit_db_session($session);
955 return scalar(@$maps);
959 __PACKAGE__->register_method(
960 method => "set_user_perms",
961 api_name => "open-ils.actor.user.permissions.update",
970 my $session = $apputils->start_db_session();
972 my( $user_obj, $evt ) = $U->checkses($ses);
975 my $perms = $session->request('open-ils.storage.permission.user_perms.atomic', $user_obj->id)->gather(1);
978 $all = 1 if ($U->is_true($user_obj->super_user()));
979 $all = 1 unless ($U->check_perms($user_obj->id, $user_obj->home_ou, 'EVERYTHING'));
981 for my $map (@$maps) {
983 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
984 if ($map->isdeleted()) {
985 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
986 } elsif ($map->isnew()) {
987 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
991 next if (!$all and !grep { $_->perm eq $map->perm and $U->is_true($_->grantable) and $_->depth <= $map->depth } @$perms);
992 #warn( "Updating permissions with method $method and session $ses and map $map" );
993 $logger->info( "Updating permissions with method $method and map $map" );
995 my $stat = $session->request($method, $map)->gather(1);
996 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
1000 $apputils->commit_db_session($session);
1002 return scalar(@$maps);
1006 __PACKAGE__->register_method(
1007 method => "user_retrieve_by_barcode",
1009 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
1011 sub user_retrieve_by_barcode {
1012 my($self, $client, $user_session, $barcode) = @_;
1014 $logger->debug("Searching for user with barcode $barcode");
1015 my ($user_obj, $evt) = $apputils->checkses($user_session);
1016 return $evt if $evt;
1018 my $card = OpenILS::Application::AppUtils->simple_scalar_request(
1020 "open-ils.cstore.direct.actor.card.search.atomic",
1021 { barcode => $barcode }
1024 if(!$card || !$card->[0]) {
1025 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
1029 my $user = flesh_user($card->usr(), new_editor(requestor => $user_obj));
1031 $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
1032 return $evt if $evt;
1034 if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
1041 __PACKAGE__->register_method(
1042 method => "get_user_by_id",
1044 api_name => "open-ils.actor.user.retrieve",);
1046 sub get_user_by_id {
1047 my ($self, $client, $auth, $id) = @_;
1048 my $e = new_editor(authtoken=>$auth);
1049 return $e->event unless $e->checkauth;
1050 my $user = $e->retrieve_actor_user($id)
1051 or return $e->event;
1052 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
1058 __PACKAGE__->register_method(
1059 method => "get_org_types",
1060 api_name => "open-ils.actor.org_types.retrieve",);
1063 return $U->get_org_types();
1068 __PACKAGE__->register_method(
1069 method => "get_user_ident_types",
1070 api_name => "open-ils.actor.user.ident_types.retrieve",
1073 sub get_user_ident_types {
1074 return $ident_types if $ident_types;
1075 return $ident_types =
1076 new_editor()->retrieve_all_config_identification_type();
1082 __PACKAGE__->register_method(
1083 method => "get_org_unit",
1084 api_name => "open-ils.actor.org_unit.retrieve",
1088 my( $self, $client, $user_session, $org_id ) = @_;
1089 my $e = new_editor(authtoken => $user_session);
1091 return $e->event unless $e->checkauth;
1092 $org_id = $e->requestor->ws_ou;
1094 my $o = $e->retrieve_actor_org_unit($org_id)
1095 or return $e->event;
1099 __PACKAGE__->register_method(
1100 method => "search_org_unit",
1101 api_name => "open-ils.actor.org_unit_list.search",
1104 sub search_org_unit {
1106 my( $self, $client, $field, $value ) = @_;
1108 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
1110 "open-ils.cstore.direct.actor.org_unit.search.atomic",
1111 { $field => $value } );
1117 # build the org tree
1119 __PACKAGE__->register_method(
1120 method => "get_org_tree",
1121 api_name => "open-ils.actor.org_tree.retrieve",
1123 note => "Returns the entire org tree structure",
1129 return $U->get_org_tree($client->session->session_locale);
1133 __PACKAGE__->register_method(
1134 method => "get_org_descendants",
1135 api_name => "open-ils.actor.org_tree.descendants.retrieve"
1138 # depth is optional. org_unit is the id
1139 sub get_org_descendants {
1140 my( $self, $client, $org_unit, $depth ) = @_;
1142 if(ref $org_unit eq 'ARRAY') {
1145 for my $i (0..scalar(@$org_unit)-1) {
1146 my $list = $U->simple_scalar_request(
1148 "open-ils.storage.actor.org_unit.descendants.atomic",
1149 $org_unit->[$i], $depth->[$i] );
1150 push(@trees, $U->build_org_tree($list));
1155 my $orglist = $apputils->simple_scalar_request(
1157 "open-ils.storage.actor.org_unit.descendants.atomic",
1158 $org_unit, $depth );
1159 return $U->build_org_tree($orglist);
1164 __PACKAGE__->register_method(
1165 method => "get_org_ancestors",
1166 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
1169 # depth is optional. org_unit is the id
1170 sub get_org_ancestors {
1171 my( $self, $client, $org_unit, $depth ) = @_;
1172 my $orglist = $apputils->simple_scalar_request(
1174 "open-ils.storage.actor.org_unit.ancestors.atomic",
1175 $org_unit, $depth );
1176 return $U->build_org_tree($orglist);
1180 __PACKAGE__->register_method(
1181 method => "get_standings",
1182 api_name => "open-ils.actor.standings.retrieve"
1187 return $user_standings if $user_standings;
1188 return $user_standings =
1189 $apputils->simple_scalar_request(
1191 "open-ils.cstore.direct.config.standing.search.atomic",
1192 { id => { "!=" => undef } }
1198 __PACKAGE__->register_method(
1199 method => "get_my_org_path",
1200 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1203 sub get_my_org_path {
1204 my( $self, $client, $auth, $org_id ) = @_;
1205 my $e = new_editor(authtoken=>$auth);
1206 return $e->event unless $e->checkauth;
1207 $org_id = $e->requestor->ws_ou unless defined $org_id;
1209 return $apputils->simple_scalar_request(
1211 "open-ils.storage.actor.org_unit.full_path.atomic",
1216 __PACKAGE__->register_method(
1217 method => "patron_adv_search",
1218 api_name => "open-ils.actor.patron.search.advanced" );
1219 sub patron_adv_search {
1220 my( $self, $client, $auth, $search_hash,
1221 $search_limit, $search_sort, $include_inactive, $search_depth ) = @_;
1223 my $e = new_editor(authtoken=>$auth);
1224 return $e->event unless $e->checkauth;
1225 return $e->event unless $e->allowed('VIEW_USER');
1226 return $U->storagereq(
1227 "open-ils.storage.actor.user.crazy_search", $search_hash,
1228 $search_limit, $search_sort, $include_inactive, $e->requestor->ws_ou, $search_depth);
1232 __PACKAGE__->register_method(
1233 method => "update_passwd",
1235 api_name => "open-ils.actor.user.password.update");
1237 __PACKAGE__->register_method(
1238 method => "update_passwd",
1239 api_name => "open-ils.actor.user.username.update");
1241 __PACKAGE__->register_method(
1242 method => "update_passwd",
1243 api_name => "open-ils.actor.user.email.update");
1246 my( $self, $conn, $auth, $new_val, $orig_pw ) = @_;
1247 my $e = new_editor(xact=>1, authtoken=>$auth);
1248 return $e->die_event unless $e->checkauth;
1250 my $db_user = $e->retrieve_actor_user($e->requestor->id)
1251 or return $e->die_event;
1252 my $api = $self->api_name;
1254 if( $api =~ /password/o ) {
1256 # make sure the original password matches the in-database password
1257 return OpenILS::Event->new('INCORRECT_PASSWORD')
1258 if md5_hex($orig_pw) ne $db_user->passwd;
1259 $db_user->passwd($new_val);
1263 # if we don't clear the password, the user will be updated with
1264 # a hashed version of the hashed version of their password
1265 $db_user->clear_passwd;
1267 if( $api =~ /username/o ) {
1269 # make sure no one else has this username
1270 my $exist = $e->search_actor_user({usrname=>$new_val},{idlist=>1});
1271 return OpenILS::Event->new('USERNAME_EXISTS') if @$exist;
1272 $db_user->usrname($new_val);
1274 } elsif( $api =~ /email/o ) {
1275 $db_user->email($new_val);
1279 $e->update_actor_user($db_user) or return $e->die_event;
1287 __PACKAGE__->register_method(
1288 method => "check_user_perms",
1289 api_name => "open-ils.actor.user.perm.check",
1290 notes => <<" NOTES");
1291 Takes a login session, user id, an org id, and an array of perm type strings. For each
1292 perm type, if the user does *not* have the given permission it is added
1293 to a list which is returned from the method. If all permissions
1294 are allowed, an empty list is returned
1295 if the logged in user does not match 'user_id', then the logged in user must
1296 have VIEW_PERMISSION priveleges.
1299 sub check_user_perms {
1300 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1302 my( $staff, $evt ) = $apputils->checkses($login_session);
1303 return $evt if $evt;
1305 if($staff->id ne $user_id) {
1306 if( $evt = $apputils->check_perms(
1307 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1313 for my $perm (@$perm_types) {
1314 if($apputils->check_perms($user_id, $org_id, $perm)) {
1315 push @not_allowed, $perm;
1319 return \@not_allowed
1322 __PACKAGE__->register_method(
1323 method => "check_user_perms2",
1324 api_name => "open-ils.actor.user.perm.check.multi_org",
1326 Checks the permissions on a list of perms and orgs for a user
1327 @param authtoken The login session key
1328 @param user_id The id of the user to check
1329 @param orgs The array of org ids
1330 @param perms The array of permission names
1331 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1332 if the logged in user does not match 'user_id', then the logged in user must
1333 have VIEW_PERMISSION priveleges.
1336 sub check_user_perms2 {
1337 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1339 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1340 $authtoken, $user_id, 'VIEW_PERMISSION' );
1341 return $evt if $evt;
1344 for my $org (@$orgs) {
1345 for my $perm (@$perms) {
1346 if($apputils->check_perms($user_id, $org, $perm)) {
1347 push @not_allowed, [ $org, $perm ];
1352 return \@not_allowed
1356 __PACKAGE__->register_method(
1357 method => 'check_user_perms3',
1358 api_name => 'open-ils.actor.user.perm.highest_org',
1360 Returns the highest org unit id at which a user has a given permission
1361 If the requestor does not match the target user, the requestor must have
1362 'VIEW_PERMISSION' rights at the home org unit of the target user
1363 @param authtoken The login session key
1364 @param userid The id of the user in question
1365 @param perm The permission to check
1366 @return The org unit highest in the org tree within which the user has
1367 the requested permission
1370 sub check_user_perms3 {
1371 my($self, $client, $authtoken, $user_id, $perm) = @_;
1372 my $e = new_editor(authtoken=>$authtoken);
1373 return $e->event unless $e->checkauth;
1375 my $tree = $U->get_org_tree();
1377 unless($e->requestor->id == $user_id) {
1378 my $user = $e->retrieve_actor_user($user_id)
1379 or return $e->event;
1380 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1381 return $U->find_highest_perm_org($perm, $user_id, $user->home_ou, $tree );
1384 return $U->find_highest_perm_org($perm, $user_id, $e->requestor->ws_ou, $tree);
1387 __PACKAGE__->register_method(
1388 method => 'user_has_work_perm_at',
1389 api_name => 'open-ils.actor.user.has_work_perm_at',
1393 Returns a set of org unit IDs which represent the highest orgs in
1394 the org tree where the user has the requested permission. The
1395 purpose of this method is to return the smallest set of org units
1396 which represent the full expanse of the user's ability to perform
1397 the requested action. The user whose perms this method should
1398 check is implied by the authtoken. /,
1400 {desc => 'authtoken', type => 'string'},
1401 {desc => 'permission name', type => 'string'},
1402 {desc => q/user id, optional. If present, check perms for
1403 this user instead of the logged in user/, type => 'number'},
1405 return => {desc => 'An array of org IDs'}
1409 sub user_has_work_perm_at {
1410 my($self, $conn, $auth, $perm, $user_id) = @_;
1411 my $e = new_editor(authtoken=>$auth);
1412 return $e->event unless $e->checkauth;
1413 if(defined $user_id) {
1414 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1415 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1417 return $U->user_has_work_perm_at($e, $perm, undef, $user_id);
1420 __PACKAGE__->register_method(
1421 method => 'user_has_work_perm_at_batch',
1422 api_name => 'open-ils.actor.user.has_work_perm_at.batch',
1426 sub user_has_work_perm_at_batch {
1427 my($self, $conn, $auth, $perms, $user_id) = @_;
1428 my $e = new_editor(authtoken=>$auth);
1429 return $e->event unless $e->checkauth;
1430 if(defined $user_id) {
1431 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1432 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1435 $map->{$_} = $U->user_has_work_perm_at($e, $_) for @$perms;
1441 __PACKAGE__->register_method(
1442 method => 'check_user_perms4',
1443 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1445 Returns the highest org unit id at which a user has a given permission
1446 If the requestor does not match the target user, the requestor must have
1447 'VIEW_PERMISSION' rights at the home org unit of the target user
1448 @param authtoken The login session key
1449 @param userid The id of the user in question
1450 @param perms An array of perm names to check
1451 @return An array of orgId's representing the org unit
1452 highest in the org tree within which the user has the requested permission
1453 The arrah of orgId's has matches the order of the perms array
1456 sub check_user_perms4 {
1457 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1459 my( $staff, $target, $org, $evt );
1461 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1462 $authtoken, $userid, 'VIEW_PERMISSION' );
1463 return $evt if $evt;
1466 return [] unless ref($perms);
1467 my $tree = $U->get_org_tree();
1469 for my $p (@$perms) {
1470 push( @arr, $U->find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1478 __PACKAGE__->register_method(
1479 method => "user_fines_summary",
1480 api_name => "open-ils.actor.user.fines.summary",
1482 notes => <<" NOTES");
1483 Returns a short summary of the users total open fines, excluding voided fines
1484 Params are login_session, user_id
1485 Returns a 'mous' object.
1488 sub user_fines_summary {
1489 my( $self, $client, $auth, $user_id ) = @_;
1490 my $e = new_editor(authtoken=>$auth);
1491 return $e->event unless $e->checkauth;
1492 my $user = $e->retrieve_actor_user($user_id)
1493 or return $e->event;
1495 if( $user_id ne $e->requestor->id ) {
1496 return $e->event unless
1497 $e->allowed('VIEW_USER_FINES_SUMMARY', $user->home_ou);
1500 # run this inside a transaction to prevent replication delay errors
1501 my $ses = $U->start_db_session();
1502 my $s = $ses->request(
1503 'open-ils.storage.money.open_user_summary.search', $user_id )->gather(1);
1504 $U->rollback_db_session($ses);
1511 __PACKAGE__->register_method(
1512 method => "user_transactions",
1513 api_name => "open-ils.actor.user.transactions",
1514 notes => <<" NOTES");
1515 Returns a list of open user transactions (mbts objects);
1516 Params are login_session, user_id
1517 Optional third parameter is the transactions type. defaults to all
1520 __PACKAGE__->register_method(
1521 method => "user_transactions",
1522 api_name => "open-ils.actor.user.transactions.have_charge",
1523 notes => <<" NOTES");
1524 Returns a list of all open user transactions (mbts objects) that have an initial charge
1525 Params are login_session, user_id
1526 Optional third parameter is the transactions type. defaults to all
1529 __PACKAGE__->register_method(
1530 method => "user_transactions",
1531 api_name => "open-ils.actor.user.transactions.have_balance",
1533 notes => <<" NOTES");
1534 Returns a list of all open user transactions (mbts objects) that have a balance
1535 Params are login_session, user_id
1536 Optional third parameter is the transactions type. defaults to all
1539 __PACKAGE__->register_method(
1540 method => "user_transactions",
1541 api_name => "open-ils.actor.user.transactions.fleshed",
1542 notes => <<" NOTES");
1543 Returns an object/hash of transaction, circ, title where transaction = an open
1544 user transactions (mbts objects), circ is the attached circluation, and title
1545 is the title the circ points to
1546 Params are login_session, user_id
1547 Optional third parameter is the transactions type. defaults to all
1550 __PACKAGE__->register_method(
1551 method => "user_transactions",
1552 api_name => "open-ils.actor.user.transactions.have_charge.fleshed",
1553 notes => <<" NOTES");
1554 Returns an object/hash of transaction, circ, title where transaction = an open
1555 user transactions that has an initial charge (mbts objects), circ is the
1556 attached circluation, and title is the title the circ points to
1557 Params are login_session, user_id
1558 Optional third parameter is the transactions type. defaults to all
1561 __PACKAGE__->register_method(
1562 method => "user_transactions",
1563 api_name => "open-ils.actor.user.transactions.have_balance.fleshed",
1565 notes => <<" NOTES");
1566 Returns an object/hash of transaction, circ, title where transaction = an open
1567 user transaction that has a balance (mbts objects), circ is the attached
1568 circluation, and title is the title the circ points to
1569 Params are login_session, user_id
1570 Optional third parameter is the transaction type. defaults to all
1573 __PACKAGE__->register_method(
1574 method => "user_transactions",
1575 api_name => "open-ils.actor.user.transactions.count",
1576 notes => <<" NOTES");
1577 Returns an object/hash of transaction, circ, title where transaction = an open
1578 user transactions (mbts objects), circ is the attached circluation, and title
1579 is the title the circ points to
1580 Params are login_session, user_id
1581 Optional third parameter is the transactions type. defaults to all
1584 __PACKAGE__->register_method(
1585 method => "user_transactions",
1586 api_name => "open-ils.actor.user.transactions.have_charge.count",
1587 notes => <<" NOTES");
1588 Returns an object/hash of transaction, circ, title where transaction = an open
1589 user transactions that has an initial charge (mbts objects), circ is the
1590 attached circluation, and title is the title the circ points to
1591 Params are login_session, user_id
1592 Optional third parameter is the transactions type. defaults to all
1595 __PACKAGE__->register_method(
1596 method => "user_transactions",
1597 api_name => "open-ils.actor.user.transactions.have_balance.count",
1599 notes => <<" NOTES");
1600 Returns an object/hash of transaction, circ, title where transaction = an open
1601 user transaction that has a balance (mbts objects), circ is the attached
1602 circluation, and title is the title the circ points to
1603 Params are login_session, user_id
1604 Optional third parameter is the transaction type. defaults to all
1607 __PACKAGE__->register_method(
1608 method => "user_transactions",
1609 api_name => "open-ils.actor.user.transactions.have_balance.total",
1611 notes => <<" NOTES");
1612 Returns an object/hash of transaction, circ, title where transaction = an open
1613 user transaction that has a balance (mbts objects), circ is the attached
1614 circluation, and title is the title the circ points to
1615 Params are login_session, user_id
1616 Optional third parameter is the transaction type. defaults to all
1621 sub user_transactions {
1622 my( $self, $client, $login_session, $user_id, $type ) = @_;
1624 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1625 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1626 return $evt if $evt;
1628 my $api = $self->api_name();
1632 if(defined($type)) { @xact = (xact_type => $type);
1634 } else { @xact = (); }
1637 ->method_lookup('open-ils.actor.user.transactions.history.still_open')
1638 ->run($login_session => $user_id => $type);
1641 if($api =~ /have_charge/o) {
1643 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1645 } elsif($api =~ /have_balance/o) {
1647 $trans = [ grep { int($_->balance_owed * 100) != 0 } @$trans ];
1650 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1654 if($api =~ /total/o) {
1656 for my $t (@$trans) {
1657 $total += $t->balance_owed;
1660 $logger->debug("Total balance owed by user $user_id: $total");
1664 if($api =~ /count/o) { return scalar @$trans; }
1665 if($api !~ /fleshed/o) { return $trans; }
1668 for my $t (@$trans) {
1670 if( $t->xact_type ne 'circulation' ) {
1671 push @resp, {transaction => $t};
1675 my $circ = $apputils->simple_scalar_request(
1677 "open-ils.cstore.direct.action.circulation.retrieve",
1682 my $title = $apputils->simple_scalar_request(
1684 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1685 $circ->target_copy );
1689 my $u = OpenILS::Utils::ModsParser->new();
1690 $u->start_mods_batch($title->marc());
1691 my $mods = $u->finish_mods_batch();
1692 $mods->doc_id($title->id) if $mods;
1694 push @resp, {transaction => $t, circ => $circ, record => $mods };
1702 __PACKAGE__->register_method(
1703 method => "user_transaction_retrieve",
1704 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1706 notes => <<" NOTES");
1707 Returns a fleshed transaction record
1709 __PACKAGE__->register_method(
1710 method => "user_transaction_retrieve",
1711 api_name => "open-ils.actor.user.transaction.retrieve",
1713 notes => <<" NOTES");
1714 Returns a transaction record
1716 sub user_transaction_retrieve {
1717 my( $self, $client, $login_session, $bill_id ) = @_;
1719 # I think I'm deprecated... make sure. phasefx says, "No, I'll use you :)
1721 my $trans = $apputils->simple_scalar_request(
1723 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1727 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1728 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1729 return $evt if $evt;
1731 my $api = $self->api_name();
1732 if($api !~ /fleshed/o) { return $trans; }
1734 if( $trans->xact_type ne 'circulation' ) {
1735 $logger->debug("Returning non-circ transaction");
1736 return {transaction => $trans};
1739 my $circ = $apputils->simple_scalar_request(
1741 "open-ils.cstore.direct.action.circulation.retrieve",
1744 return {transaction => $trans} unless $circ;
1745 $logger->debug("Found the circ transaction");
1747 my $title = $apputils->simple_scalar_request(
1749 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1750 $circ->target_copy );
1752 return {transaction => $trans, circ => $circ } unless $title;
1753 $logger->debug("Found the circ title");
1756 my $copy = $apputils->simple_scalar_request(
1758 "open-ils.cstore.direct.asset.copy.retrieve",
1759 $circ->target_copy );
1762 my $u = OpenILS::Utils::ModsParser->new();
1763 $u->start_mods_batch($title->marc());
1764 $mods = $u->finish_mods_batch();
1766 if ($title->id == OILS_PRECAT_RECORD) {
1767 $mods = new Fieldmapper::metabib::virtual_record;
1768 $mods->doc_id(OILS_PRECAT_RECORD);
1769 $mods->title($copy->dummy_title);
1770 $mods->author($copy->dummy_author);
1774 $logger->debug("MODSized the circ title");
1776 return {transaction => $trans, circ => $circ, record => $mods, copy => $copy };
1780 __PACKAGE__->register_method(
1781 method => "hold_request_count",
1782 api_name => "open-ils.actor.user.hold_requests.count",
1785 notes => <<" NOTES");
1786 Returns hold ready/total counts
1788 sub hold_request_count {
1789 my( $self, $client, $login_session, $userid ) = @_;
1791 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1792 $login_session, $userid, 'VIEW_HOLD' );
1793 return $evt if $evt;
1796 my $holds = $apputils->simple_scalar_request(
1798 "open-ils.cstore.direct.action.hold_request.search.atomic",
1801 fulfillment_time => {"=" => undef },
1802 cancel_time => undef,
1807 for my $h (@$holds) {
1808 next unless $h->capture_time and $h->current_copy;
1810 my $copy = $apputils->simple_scalar_request(
1812 "open-ils.cstore.direct.asset.copy.retrieve",
1816 if ($copy and $copy->status == 8) {
1821 return { total => scalar(@$holds), ready => scalar(@ready) };
1825 __PACKAGE__->register_method(
1826 method => "checkedout_count",
1827 api_name => "open-ils.actor.user.checked_out.count__",
1829 notes => <<" NOTES");
1830 Returns a transaction record
1834 sub checkedout_count {
1835 my( $self, $client, $login_session, $userid ) = @_;
1837 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1838 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1839 return $evt if $evt;
1841 my $circs = $apputils->simple_scalar_request(
1843 "open-ils.cstore.direct.action.circulation.search.atomic",
1844 { usr => $userid, stop_fines => undef }
1845 #{ usr => $userid, checkin_time => {"=" => undef } }
1848 my $parser = DateTime::Format::ISO8601->new;
1851 for my $c (@$circs) {
1852 my $due_dt = $parser->parse_datetime( cleanse_ISO8601( $c->due_date ) );
1853 my $due = $due_dt->epoch;
1855 if ($due < DateTime->today->epoch) {
1860 return { total => scalar(@$circs), overdue => scalar(@overdue) };
1864 __PACKAGE__->register_method(
1865 method => "checked_out",
1866 api_name => "open-ils.actor.user.checked_out",
1870 Returns a structure of circulations objects sorted by
1871 out, overdue, lost, claims_returned, long_overdue.
1872 A list of IDs are returned of each type.
1873 lost, long_overdue, and claims_returned circ will not
1874 be "finished" (there is an outstanding balance or some
1875 other pending action on the circ).
1877 The .count method also includes a 'total' field which
1878 sums all "open" circs
1882 __PACKAGE__->register_method(
1883 method => "checked_out",
1884 api_name => "open-ils.actor.user.checked_out.count",
1887 signature => q/@see open-ils.actor.user.checked_out/
1891 my( $self, $conn, $auth, $userid ) = @_;
1893 my $e = new_editor(authtoken=>$auth);
1894 return $e->event unless $e->checkauth;
1896 if( $userid ne $e->requestor->id ) {
1897 my $user = $e->retrieve_actor_user($userid) or return $e->event;
1898 unless($e->allowed('VIEW_CIRCULATIONS', $user->home_ou)) {
1900 # see if there is a friend link allowing circ.view perms
1901 my $allowed = OpenILS::Application::Actor::Friends->friend_perm_allowed(
1902 $e, $userid, $e->requestor->id, 'circ.view');
1903 return $e->event unless $allowed;
1907 my $count = $self->api_name =~ /count/;
1908 return _checked_out( $count, $e, $userid );
1912 my( $iscount, $e, $userid ) = @_;
1913 my $meth = 'open-ils.storage.actor.user.checked_out';
1914 $meth = "$meth.count" if $iscount;
1915 return $U->storagereq($meth, $userid);
1919 sub _checked_out_WHAT {
1920 my( $iscount, $e, $userid ) = @_;
1922 my $circs = $e->search_action_circulation(
1923 { usr => $userid, stop_fines => undef });
1925 my $mcircs = $e->search_action_circulation(
1928 checkin_time => undef,
1929 xact_finish => undef,
1933 push( @$circs, @$mcircs );
1935 my $parser = DateTime::Format::ISO8601->new;
1937 # split the circs up into overdue and not-overdue circs
1939 for my $c (@$circs) {
1940 if( $c->due_date ) {
1941 my $due_dt = $parser->parse_datetime( cleanse_ISO8601( $c->due_date ) );
1942 my $due = $due_dt->epoch;
1943 if ($due < DateTime->today->epoch) {
1944 push @overdue, $c->id;
1953 # grab all of the lost, claims-returned, and longoverdue circs
1954 #my $open = $e->search_action_circulation(
1955 # {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1958 # these items have stop_fines, but no xact_finish, so money
1959 # is owed on them and they have not been checked in
1960 my $open = $e->search_action_circulation(
1963 stop_fines => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] },
1964 xact_finish => undef,
1965 checkin_time => undef,
1970 my( @lost, @cr, @lo );
1971 for my $c (@$open) {
1972 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1973 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1974 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1980 total => @$circs + @lost + @cr + @lo,
1981 out => scalar(@out),
1982 overdue => scalar(@overdue),
1983 lost => scalar(@lost),
1984 claims_returned => scalar(@cr),
1985 long_overdue => scalar(@lo)
1991 overdue => \@overdue,
1993 claims_returned => \@cr,
1994 long_overdue => \@lo
2000 __PACKAGE__->register_method(
2001 method => "checked_in_with_fines",
2002 api_name => "open-ils.actor.user.checked_in_with_fines",
2005 signature => q/@see open-ils.actor.user.checked_out/
2007 sub checked_in_with_fines {
2008 my( $self, $conn, $auth, $userid ) = @_;
2010 my $e = new_editor(authtoken=>$auth);
2011 return $e->event unless $e->checkauth;
2013 if( $userid ne $e->requestor->id ) {
2014 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
2017 # money is owed on these items and they are checked in
2018 my $open = $e->search_action_circulation(
2021 xact_finish => undef,
2022 checkin_time => { "!=" => undef },
2027 my( @lost, @cr, @lo );
2028 for my $c (@$open) {
2029 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
2030 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
2031 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
2036 claims_returned => \@cr,
2037 long_overdue => \@lo
2049 __PACKAGE__->register_method(
2050 method => "user_transaction_history",
2051 api_name => "open-ils.actor.user.transactions.history",
2053 notes => <<" NOTES");
2054 Returns a list of billable transactions for a user, optionally by type
2056 __PACKAGE__->register_method(
2057 method => "user_transaction_history",
2058 api_name => "open-ils.actor.user.transactions.history.have_charge",
2060 notes => <<" NOTES");
2061 Returns a list of billable transactions for a user that have an initial charge, optionally by type
2063 __PACKAGE__->register_method(
2064 method => "user_transaction_history",
2065 api_name => "open-ils.actor.user.transactions.history.have_balance",
2068 notes => <<" NOTES");
2069 Returns a list of billable transactions for a user that have a balance, optionally by type
2071 __PACKAGE__->register_method(
2072 method => "user_transaction_history",
2073 api_name => "open-ils.actor.user.transactions.history.still_open",
2075 notes => <<" NOTES");
2076 Returns a list of billable transactions for a user that are not finished
2078 __PACKAGE__->register_method(
2079 method => "user_transaction_history",
2080 api_name => "open-ils.actor.user.transactions.history.have_bill",
2083 notes => <<" NOTES");
2084 Returns a list of billable transactions for a user that has billings
2086 __PACKAGE__->register_method(
2087 method => "user_transaction_history",
2088 api_name => "open-ils.actor.user.transactions.history.ids",
2090 notes => <<" NOTES");
2091 Returns a list of billable transaction ids for a user, optionally by type
2093 __PACKAGE__->register_method(
2094 method => "user_transaction_history",
2095 api_name => "open-ils.actor.user.transactions.history.have_charge.ids",
2097 notes => <<" NOTES");
2098 Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
2100 __PACKAGE__->register_method(
2101 method => "user_transaction_history",
2102 api_name => "open-ils.actor.user.transactions.history.have_balance.ids",
2105 notes => <<" NOTES");
2106 Returns a list of billable transaction ids for a user that have a balance, optionally by type
2108 __PACKAGE__->register_method(
2109 method => "user_transaction_history",
2110 api_name => "open-ils.actor.user.transactions.history.still_open.ids",
2112 notes => <<" NOTES");
2113 Returns a list of billable transaction ids for a user that are not finished
2115 __PACKAGE__->register_method(
2116 method => "user_transaction_history",
2117 api_name => "open-ils.actor.user.transactions.history.have_bill.ids",
2120 notes => <<" NOTES");
2121 Returns a list of billable transaction ids for a user that has billings
2123 __PACKAGE__->register_method(
2124 method => "user_transaction_history",
2125 api_name => "open-ils.actor.user.transactions.history.have_bill_or_payment",
2128 notes => <<" NOTES");
2129 Returns a list of billable transactions for a user that has non-zero-sum billings or at least 1 payment
2131 __PACKAGE__->register_method(
2132 method => "user_transaction_history",
2133 api_name => "open-ils.actor.user.transactions.history.have_bill_or_payment.ids",
2136 notes => <<" NOTES");
2137 Returns a list of billable transaction ids for a user that has non-zero-sum billings or at least 1 payment
2142 sub user_transaction_history {
2143 my( $self, $conn, $auth, $userid, $type, $filter ) = @_;
2146 # run inside of a transaction to prevent replication delays
2147 my $e = new_editor(authtoken=>$auth);
2148 return $e->die_event unless $e->checkauth;
2150 if( $e->requestor->id ne $userid ) {
2151 return $e->die_event
2152 unless $e->allowed('VIEW_USER_TRANSACTIONS');
2155 my $api = $self->api_name;
2156 my @xact_finish = (xact_finish => undef ) if ($api =~ /history.still_open$/);
2158 my $mbts = $e->search_money_billable_transaction_summary(
2160 { usr => $userid, @xact_finish, %$filter },
2161 { order_by => { mbt => 'xact_start DESC' } }
2165 if(defined($type)) {
2166 @$mbts = grep { $_->xact_type eq $type } @$mbts;
2169 if($api =~ /have_bill_or_payment/o) {
2171 # transactions that have a non-zero sum across all billings or at least 1 payment
2173 int($_->balance_owed * 100) != 0 ||
2174 defined($_->last_payment_ts) } @$mbts;
2176 } elsif( $api =~ /have_balance/o) {
2178 # transactions that have a non-zero overall balance
2179 @$mbts = grep { int($_->balance_owed * 100) != 0 } @$mbts;
2181 } elsif( $api =~ /have_charge/o) {
2183 # transactions that have at least 1 billing, regardless of whether it was voided
2184 @$mbts = grep { defined($_->last_billing_ts) } @$mbts;
2186 } elsif( $api =~ /have_bill/o) {
2188 # transactions that have non-zero sum across all billings. This will exclude
2189 # xacts where all billings have been voided
2190 @$mbts = grep { int($_->total_owed * 100) != 0 } @$mbts;
2193 if ($api =~ /\.ids/) {
2194 return [map {$_->id} @$mbts];
2202 __PACKAGE__->register_method(
2203 method => "user_perms",
2204 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2206 notes => <<" NOTES");
2207 Returns a list of permissions
2210 my( $self, $client, $authtoken, $user ) = @_;
2212 my( $staff, $evt ) = $apputils->checkses($authtoken);
2213 return $evt if $evt;
2215 $user ||= $staff->id;
2217 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2221 return $apputils->simple_scalar_request(
2223 "open-ils.storage.permission.user_perms.atomic",
2227 __PACKAGE__->register_method(
2228 method => "retrieve_perms",
2229 api_name => "open-ils.actor.permissions.retrieve",
2230 notes => <<" NOTES");
2231 Returns a list of permissions
2233 sub retrieve_perms {
2234 my( $self, $client ) = @_;
2235 return $apputils->simple_scalar_request(
2237 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2238 { id => { '!=' => undef } }
2242 __PACKAGE__->register_method(
2243 method => "retrieve_groups",
2244 api_name => "open-ils.actor.groups.retrieve",
2245 notes => <<" NOTES");
2246 Returns a list of user groupss
2248 sub retrieve_groups {
2249 my( $self, $client ) = @_;
2250 return new_editor()->retrieve_all_permission_grp_tree();
2253 __PACKAGE__->register_method(
2254 method => "retrieve_org_address",
2255 api_name => "open-ils.actor.org_unit.address.retrieve",
2256 notes => <<' NOTES');
2257 Returns an org_unit address by ID
2258 @param An org_address ID
2260 sub retrieve_org_address {
2261 my( $self, $client, $id ) = @_;
2262 return $apputils->simple_scalar_request(
2264 "open-ils.cstore.direct.actor.org_address.retrieve",
2269 __PACKAGE__->register_method(
2270 method => "retrieve_groups_tree",
2271 api_name => "open-ils.actor.groups.tree.retrieve",
2272 notes => <<" NOTES");
2273 Returns a list of user groups
2275 sub retrieve_groups_tree {
2276 my( $self, $client ) = @_;
2277 return new_editor()->search_permission_grp_tree(
2282 flesh_fields => { pgt => ["children"] },
2283 order_by => { pgt => 'name'}
2290 __PACKAGE__->register_method(
2291 method => "add_user_to_groups",
2292 api_name => "open-ils.actor.user.set_groups",
2293 notes => <<" NOTES");
2294 Adds a user to one or more permission groups
2297 sub add_user_to_groups {
2298 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2300 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2301 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2302 return $evt if $evt;
2304 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2305 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2306 return $evt if $evt;
2308 $apputils->simplereq(
2310 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2312 for my $group (@$groups) {
2313 my $link = Fieldmapper::permission::usr_grp_map->new;
2315 $link->usr($userid);
2317 my $id = $apputils->simplereq(
2319 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2325 __PACKAGE__->register_method(
2326 method => "get_user_perm_groups",
2327 api_name => "open-ils.actor.user.get_groups",
2328 notes => <<" NOTES");
2329 Retrieve a user's permission groups.
2333 sub get_user_perm_groups {
2334 my( $self, $client, $authtoken, $userid ) = @_;
2336 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2337 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2338 return $evt if $evt;
2340 return $apputils->simplereq(
2342 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2346 __PACKAGE__->register_method(
2347 method => "get_user_work_ous",
2348 api_name => "open-ils.actor.user.get_work_ous",
2349 notes => <<" NOTES");
2350 Retrieve a user's work org units.
2352 __PACKAGE__->register_method(
2353 method => "get_user_work_ous",
2354 api_name => "open-ils.actor.user.get_work_ous.ids",
2355 notes => <<" NOTES");
2356 Retrieve a user's work org units.
2360 sub get_user_work_ous {
2361 my( $self, $client, $auth, $userid ) = @_;
2362 my $e = new_editor(authtoken=>$auth);
2363 return $e->event unless $e->checkauth;
2364 $userid ||= $e->requestor->id;
2366 if($e->requestor->id != $userid) {
2367 my $user = $e->retrieve_actor_user($userid)
2368 or return $e->event;
2369 return $e->event unless $e->allowed('ASSIGN_WORK_ORG_UNIT', $user->home_ou);
2372 return $e->search_permission_usr_work_ou_map({usr => $userid})
2373 unless $self->api_name =~ /.ids$/;
2375 # client just wants a list of org IDs
2376 return $U->get_user_work_ou_ids($e, $userid);
2382 __PACKAGE__->register_method (
2383 method => 'register_workstation',
2384 api_name => 'open-ils.actor.workstation.register.override',
2385 signature => q/@see open-ils.actor.workstation.register/);
2387 __PACKAGE__->register_method (
2388 method => 'register_workstation',
2389 api_name => 'open-ils.actor.workstation.register',
2391 Registers a new workstion in the system
2392 @param authtoken The login session key
2393 @param name The name of the workstation id
2394 @param owner The org unit that owns this workstation
2395 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2396 if the name is already in use.
2399 sub register_workstation {
2400 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2402 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2403 return $e->die_event unless $e->checkauth;
2404 return $e->die_event unless $e->allowed('REGISTER_WORKSTATION', $owner);
2405 my $existing = $e->search_actor_workstation({name => $name})->[0];
2409 if( $self->api_name =~ /override/o ) {
2410 # workstation with the given name exists.
2412 if($owner ne $existing->owning_lib) {
2413 # if necessary, update the owning_lib of the workstation
2415 $logger->info("changing owning lib of workstation ".$existing->id.
2416 " from ".$existing->owning_lib." to $owner");
2417 return $e->die_event unless
2418 $e->allowed('UPDATE_WORKSTATION', $existing->owning_lib);
2420 return $e->die_event unless $e->allowed('UPDATE_WORKSTATION', $owner);
2422 $existing->owning_lib($owner);
2423 return $e->die_event unless $e->update_actor_workstation($existing);
2429 "attempt to register an existing workstation. returning existing ID");
2432 return $existing->id;
2435 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2439 my $ws = Fieldmapper::actor::workstation->new;
2440 $ws->owning_lib($owner);
2442 $e->create_actor_workstation($ws) or return $e->die_event;
2444 return $ws->id; # note: editor sets the id on the new object for us
2447 __PACKAGE__->register_method (
2448 method => 'workstation_list',
2449 api_name => 'open-ils.actor.workstation.list',
2451 Returns a list of workstations registered at the given location
2452 @param authtoken The login session key
2453 @param ids A list of org_unit.id's for the workstation owners
2456 sub workstation_list {
2457 my( $self, $conn, $authtoken, @orgs ) = @_;
2459 my $e = new_editor(authtoken=>$authtoken);
2460 return $e->event unless $e->checkauth;
2465 unless $e->allowed('REGISTER_WORKSTATION', $o);
2466 $results{$o} = $e->search_actor_workstation({owning_lib=>$o});
2477 __PACKAGE__->register_method (
2478 method => 'fetch_patron_note',
2479 api_name => 'open-ils.actor.note.retrieve.all',
2482 Returns a list of notes for a given user
2483 Requestor must have VIEW_USER permission if pub==false and
2484 @param authtoken The login session key
2485 @param args Hash of params including
2486 patronid : the patron's id
2487 pub : true if retrieving only public notes
2491 sub fetch_patron_note {
2492 my( $self, $conn, $authtoken, $args ) = @_;
2493 my $patronid = $$args{patronid};
2495 my($reqr, $evt) = $U->checkses($authtoken);
2496 return $evt if $evt;
2499 ($patron, $evt) = $U->fetch_user($patronid);
2500 return $evt if $evt;
2503 if( $patronid ne $reqr->id ) {
2504 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2505 return $evt if $evt;
2507 return $U->cstorereq(
2508 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2509 { usr => $patronid, pub => 't' } );
2512 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2513 return $evt if $evt;
2515 return $U->cstorereq(
2516 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2519 __PACKAGE__->register_method (
2520 method => 'create_user_note',
2521 api_name => 'open-ils.actor.note.create',
2523 Creates a new note for the given user
2524 @param authtoken The login session key
2525 @param note The note object
2528 sub create_user_note {
2529 my( $self, $conn, $authtoken, $note ) = @_;
2530 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2531 return $e->die_event unless $e->checkauth;
2533 my $user = $e->retrieve_actor_user($note->usr)
2534 or return $e->die_event;
2536 return $e->die_event unless
2537 $e->allowed('UPDATE_USER',$user->home_ou);
2539 $note->creator($e->requestor->id);
2540 $e->create_actor_usr_note($note) or return $e->die_event;
2546 __PACKAGE__->register_method (
2547 method => 'delete_user_note',
2548 api_name => 'open-ils.actor.note.delete',
2550 Deletes a note for the given user
2551 @param authtoken The login session key
2552 @param noteid The note id
2555 sub delete_user_note {
2556 my( $self, $conn, $authtoken, $noteid ) = @_;
2558 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2559 return $e->die_event unless $e->checkauth;
2560 my $note = $e->retrieve_actor_usr_note($noteid)
2561 or return $e->die_event;
2562 my $user = $e->retrieve_actor_user($note->usr)
2563 or return $e->die_event;
2564 return $e->die_event unless
2565 $e->allowed('UPDATE_USER', $user->home_ou);
2567 $e->delete_actor_usr_note($note) or return $e->die_event;
2573 __PACKAGE__->register_method (
2574 method => 'update_user_note',
2575 api_name => 'open-ils.actor.note.update',
2577 @param authtoken The login session key
2578 @param note The note
2582 sub update_user_note {
2583 my( $self, $conn, $auth, $note ) = @_;
2584 my $e = new_editor(authtoken=>$auth, xact=>1);
2585 return $e->event unless $e->checkauth;
2586 my $patron = $e->retrieve_actor_user($note->usr)
2587 or return $e->event;
2588 return $e->event unless
2589 $e->allowed('UPDATE_USER', $patron->home_ou);
2590 $e->update_actor_user_note($note)
2591 or return $e->event;
2599 __PACKAGE__->register_method (
2600 method => 'create_closed_date',
2601 api_name => 'open-ils.actor.org_unit.closed_date.create',
2603 Creates a new closing entry for the given org_unit
2604 @param authtoken The login session key
2605 @param note The closed_date object
2608 sub create_closed_date {
2609 my( $self, $conn, $authtoken, $cd ) = @_;
2611 my( $user, $evt ) = $U->checkses($authtoken);
2612 return $evt if $evt;
2614 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2615 return $evt if $evt;
2617 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2619 my $id = $U->storagereq(
2620 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2621 return $U->DB_UPDATE_FAILED($cd) unless $id;
2626 __PACKAGE__->register_method (
2627 method => 'delete_closed_date',
2628 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2630 Deletes a closing entry for the given org_unit
2631 @param authtoken The login session key
2632 @param noteid The close_date id
2635 sub delete_closed_date {
2636 my( $self, $conn, $authtoken, $cd ) = @_;
2638 my( $user, $evt ) = $U->checkses($authtoken);
2639 return $evt if $evt;
2642 ($cd_obj, $evt) = fetch_closed_date($cd);
2643 return $evt if $evt;
2645 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2646 return $evt if $evt;
2648 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2650 my $stat = $U->storagereq(
2651 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2652 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2657 __PACKAGE__->register_method(
2658 method => 'usrname_exists',
2659 api_name => 'open-ils.actor.username.exists',
2661 Returns 1 if the requested username exists, returns 0 otherwise
2665 sub usrname_exists {
2666 my( $self, $conn, $auth, $usrname ) = @_;
2667 my $e = new_editor(authtoken=>$auth);
2668 return $e->event unless $e->checkauth;
2669 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2670 return $$a[0] if $a and @$a;
2674 __PACKAGE__->register_method(
2675 method => 'barcode_exists',
2676 api_name => 'open-ils.actor.barcode.exists',
2679 Returns 1 if the requested barcode exists, returns 0 otherwise
2683 sub barcode_exists {
2684 my( $self, $conn, $auth, $barcode ) = @_;
2685 my $e = new_editor(authtoken=>$auth);
2686 return $e->event unless $e->checkauth;
2687 my $card = $e->search_actor_card({barcode => $barcode});
2693 #return undef unless @$card;
2694 #return $card->[0]->usr;
2698 __PACKAGE__->register_method(
2699 method => 'retrieve_net_levels',
2700 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2703 sub retrieve_net_levels {
2704 my( $self, $conn, $auth ) = @_;
2705 my $e = new_editor(authtoken=>$auth);
2706 return $e->event unless $e->checkauth;
2707 return $e->retrieve_all_config_net_access_level();
2710 # Retain the old typo API name just in case
2711 __PACKAGE__->register_method(
2712 method => 'fetch_org_by_shortname',
2713 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2715 __PACKAGE__->register_method(
2716 method => 'fetch_org_by_shortname',
2717 api_name => 'open-ils.actor.org_unit.retrieve_by_shortname',
2719 sub fetch_org_by_shortname {
2720 my( $self, $conn, $sname ) = @_;
2721 my $e = new_editor();
2722 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2723 return $e->event unless $org;
2728 __PACKAGE__->register_method(
2729 method => 'session_home_lib',
2730 api_name => 'open-ils.actor.session.home_lib',
2733 sub session_home_lib {
2734 my( $self, $conn, $auth ) = @_;
2735 my $e = new_editor(authtoken=>$auth);
2736 return undef unless $e->checkauth;
2737 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2738 return $org->shortname;
2741 __PACKAGE__->register_method(
2742 method => 'session_safe_token',
2743 api_name => 'open-ils.actor.session.safe_token',
2745 Returns a hashed session ID that is safe for export to the world.
2746 This safe token will expire after 1 hour of non-use.
2747 @param auth Active authentication token
2751 sub session_safe_token {
2752 my( $self, $conn, $auth ) = @_;
2753 my $e = new_editor(authtoken=>$auth);
2754 return undef unless $e->checkauth;
2756 my $safe_token = md5_hex($auth);
2758 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2760 # Add more like the following if needed...
2762 "safe-token-home_lib-shortname-$safe_token",
2763 $e->retrieve_actor_org_unit(
2764 $e->requestor->home_ou
2773 __PACKAGE__->register_method(
2774 method => 'safe_token_home_lib',
2775 api_name => 'open-ils.actor.safe_token.home_lib.shortname',
2777 Returns the home library shortname from the session
2778 asscociated with a safe token from generated by
2779 open-ils.actor.session.safe_token.
2780 @param safe_token Active safe token
2784 sub safe_token_home_lib {
2785 my( $self, $conn, $safe_token ) = @_;
2787 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2788 return $cache->get_cache( 'safe-token-home_lib-shortname-'. $safe_token );
2793 __PACKAGE__->register_method(
2794 method => 'slim_tree',
2795 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2798 my $tree = new_editor()->search_actor_org_unit(
2800 {"parent_ou" => undef },
2803 flesh_fields => { aou => ['children'] },
2804 order_by => { aou => 'name'},
2805 select => { aou => ["id","shortname", "name"]},
2810 return trim_tree($tree);
2816 return undef unless $tree;
2818 code => $tree->shortname,
2819 name => $tree->name,
2821 if( $tree->children and @{$tree->children} ) {
2822 $htree->{children} = [];
2823 for my $c (@{$tree->children}) {
2824 push( @{$htree->{children}}, trim_tree($c) );
2832 __PACKAGE__->register_method(
2833 method => "update_penalties",
2834 api_name => "open-ils.actor.user.penalties.update");
2836 sub update_penalties {
2837 my($self, $conn, $auth, $user_id) = @_;
2838 my $e = new_editor(authtoken=>$auth, xact => 1);
2839 return $e->die_event unless $e->checkauth;
2840 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
2841 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2842 my $evt = OpenILS::Utils::Penalty->calculate_penalties($e, $user_id, $e->requestor->ws_ou);
2843 return $evt if $evt;
2849 __PACKAGE__->register_method(
2850 method => "apply_penalty",
2851 api_name => "open-ils.actor.user.penalty.apply");
2854 my($self, $conn, $auth, $penalty) = @_;
2856 my $e = new_editor(authtoken=>$auth, xact => 1);
2857 return $e->die_event unless $e->checkauth;
2859 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2860 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2862 my $ptype = $e->retrieve_config_standing_penalty($penalty->standing_penalty) or return $e->die_event;
2865 (defined $ptype->org_depth) ?
2866 $U->org_unit_ancestor_at_depth($penalty->org_unit, $ptype->org_depth) :
2869 $penalty->org_unit($ctx_org);
2870 $penalty->staff($e->requestor->id);
2871 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
2874 return $penalty->id;
2877 __PACKAGE__->register_method(
2878 method => "remove_penalty",
2879 api_name => "open-ils.actor.user.penalty.remove");
2881 sub remove_penalty {
2882 my($self, $conn, $auth, $penalty) = @_;
2883 my $e = new_editor(authtoken=>$auth, xact => 1);
2884 return $e->die_event unless $e->checkauth;
2885 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2886 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2888 $e->delete_actor_user_standing_penalty($penalty) or return $e->die_event;
2893 __PACKAGE__->register_method(
2894 method => "update_penalty_note",
2895 api_name => "open-ils.actor.user.penalty.note.update");
2897 sub update_penalty_note {
2898 my($self, $conn, $auth, $penalty_ids, $note) = @_;
2899 my $e = new_editor(authtoken=>$auth, xact => 1);
2900 return $e->die_event unless $e->checkauth;
2901 for my $penalty_id (@$penalty_ids) {
2902 my $penalty = $e->search_actor_user_standing_penalty( { id => $penalty_id } )->[0];
2903 if (! $penalty ) { return $e->die_event; }
2904 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2905 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2907 $penalty->note( $note ); $penalty->ischanged( 1 );
2909 $e->update_actor_user_standing_penalty($penalty) or return $e->die_event;
2915 __PACKAGE__->register_method(
2916 method => "ranged_penalty_thresholds",
2917 api_name => "open-ils.actor.grp_penalty_threshold.ranged.retrieve",
2921 sub ranged_penalty_thresholds {
2922 my($self, $conn, $auth, $context_org) = @_;
2923 my $e = new_editor(authtoken=>$auth);
2924 return $e->event unless $e->checkauth;
2925 return $e->event unless $e->allowed('VIEW_GROUP_PENALTY_THRESHOLD', $context_org);
2926 my $list = $e->search_permission_grp_penalty_threshold([
2927 {org_unit => $U->get_org_ancestors($context_org)},
2928 {order_by => {pgpt => 'id'}}
2930 $conn->respond($_) for @$list;
2936 __PACKAGE__->register_method(
2937 method => "user_retrieve_fleshed_by_id",
2939 api_name => "open-ils.actor.user.fleshed.retrieve",);
2941 sub user_retrieve_fleshed_by_id {
2942 my( $self, $client, $auth, $user_id, $fields ) = @_;
2943 my $e = new_editor(authtoken => $auth);
2944 return $e->event unless $e->checkauth;
2946 if( $e->requestor->id != $user_id ) {
2947 return $e->event unless $e->allowed('VIEW_USER');
2953 "standing_penalties",
2957 "stat_cat_entries" ];
2958 return new_flesh_user($user_id, $fields, $e);
2962 sub new_flesh_user {
2965 my $fields = shift || [];
2968 my $fetch_penalties = 0;
2969 if(grep {$_ eq 'standing_penalties'} @$fields) {
2970 $fields = [grep {$_ ne 'standing_penalties'} @$fields];
2971 $fetch_penalties = 1;
2974 my $user = $e->retrieve_actor_user(
2979 "flesh_fields" => { "au" => $fields }
2982 ) or return $e->event;
2985 if( grep { $_ eq 'addresses' } @$fields ) {
2987 $user->addresses([]) unless @{$user->addresses};
2988 # don't expose "replaced" addresses by default
2989 $user->addresses([grep {$_->id >= 0} @{$user->addresses}]);
2991 if( ref $user->billing_address ) {
2992 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2993 push( @{$user->addresses}, $user->billing_address );
2997 if( ref $user->mailing_address ) {
2998 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2999 push( @{$user->addresses}, $user->mailing_address );
3004 if($fetch_penalties) {
3005 # grab the user penalties ranged for this location
3006 $user->standing_penalties(
3007 $e->search_actor_user_standing_penalty([
3010 {stop_date => undef},
3011 {stop_date => {'>' => 'now'}}
3013 org_unit => $U->get_org_ancestors($e->requestor->ws_ou)
3016 flesh_fields => {ausp => ['standing_penalty']}
3023 $user->clear_passwd();
3030 __PACKAGE__->register_method(
3031 method => "user_retrieve_parts",
3032 api_name => "open-ils.actor.user.retrieve.parts",);
3034 sub user_retrieve_parts {
3035 my( $self, $client, $auth, $user_id, $fields ) = @_;
3036 my $e = new_editor(authtoken => $auth);
3037 return $e->event unless $e->checkauth;
3038 if( $e->requestor->id != $user_id ) {
3039 return $e->event unless $e->allowed('VIEW_USER');
3042 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3043 push(@resp, $user->$_()) for(@$fields);
3049 __PACKAGE__->register_method(
3050 method => 'user_opt_in_enabled',
3051 api_name => 'open-ils.actor.user.org_unit_opt_in.enabled',
3053 @return 1 if user opt-in is globally enabled, 0 otherwise.
3056 sub user_opt_in_enabled {
3057 my($self, $conn) = @_;
3058 my $sc = OpenSRF::Utils::SettingsClient->new;
3059 return 1 if lc($sc->config_value(share => user => 'opt_in')) eq 'true';
3064 __PACKAGE__->register_method(
3065 method => 'user_opt_in_at_org',
3066 api_name => 'open-ils.actor.user.org_unit_opt_in.check',
3068 @param $auth The auth token
3069 @param user_id The ID of the user to test
3070 @return 1 if the user has opted in at the specified org,
3071 event on error, and 0 otherwise. /);
3072 sub user_opt_in_at_org {
3073 my($self, $conn, $auth, $user_id) = @_;
3075 # see if we even need to enforce the opt-in value
3076 return 1 unless user_opt_in_enabled($self);
3078 my $e = new_editor(authtoken => $auth);
3079 return $e->event unless $e->checkauth;
3080 my $org_id = $e->requestor->ws_ou;
3082 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3083 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3085 # user is automatically opted-in at the home org
3086 return 1 if $user->home_ou eq $org_id;
3088 my $vals = $e->search_actor_usr_org_unit_opt_in(
3089 {org_unit=>$org_id, usr=>$user_id},{idlist=>1});
3095 __PACKAGE__->register_method(
3096 method => 'create_user_opt_in_at_org',
3097 api_name => 'open-ils.actor.user.org_unit_opt_in.create',
3099 @param $auth The auth token
3100 @param user_id The ID of the user to test
3101 @return The ID of the newly created object, event on error./);
3103 sub create_user_opt_in_at_org {
3104 my($self, $conn, $auth, $user_id) = @_;
3106 my $e = new_editor(authtoken => $auth, xact=>1);
3107 return $e->die_event unless $e->checkauth;
3108 my $org_id = $e->requestor->ws_ou;
3110 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3111 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3113 my $opt_in = Fieldmapper::actor::usr_org_unit_opt_in->new;
3115 $opt_in->org_unit($org_id);
3116 $opt_in->usr($user_id);
3117 $opt_in->staff($e->requestor->id);
3118 $opt_in->opt_in_ts('now');
3119 $opt_in->opt_in_ws($e->requestor->wsid);
3121 $opt_in = $e->create_actor_usr_org_unit_opt_in($opt_in)
3122 or return $e->die_event;
3130 __PACKAGE__->register_method (
3131 method => 'retrieve_org_hours',
3132 api_name => 'open-ils.actor.org_unit.hours_of_operation.retrieve',
3134 Returns the hours of operation for a specified org unit
3135 @param authtoken The login session key
3136 @param org_id The org_unit ID
3140 sub retrieve_org_hours {
3141 my($self, $conn, $auth, $org_id) = @_;
3142 my $e = new_editor(authtoken => $auth);
3143 return $e->die_event unless $e->checkauth;
3144 $org_id ||= $e->requestor->ws_ou;
3145 return $e->retrieve_actor_org_unit_hours_of_operation($org_id);
3149 __PACKAGE__->register_method (
3150 method => 'verify_user_password',
3151 api_name => 'open-ils.actor.verify_user_password',
3153 Given a barcode or username and the MD5 encoded password,
3154 returns 1 if the password is correct. Returns 0 otherwise.
3158 sub verify_user_password {
3159 my($self, $conn, $auth, $barcode, $username, $password) = @_;
3160 my $e = new_editor(authtoken => $auth);
3161 return $e->die_event unless $e->checkauth;
3163 my $user_by_barcode;
3164 my $user_by_username;
3166 my $card = $e->search_actor_card([
3167 {barcode => $barcode},
3168 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0] or return 0;
3169 $user_by_barcode = $card->usr;
3170 $user = $user_by_barcode;
3173 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return 0;
3174 $user = $user_by_username;
3176 return 0 if (!$user);
3177 return 0 if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3178 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3179 return 1 if $user->passwd eq $password;
3183 __PACKAGE__->register_method (
3184 method => 'retrieve_usr_id_via_barcode_or_usrname',
3185 api_name => "open-ils.actor.user.retrieve_id_by_barcode_or_username",
3187 Given a barcode or username returns the id for the user or
3192 sub retrieve_usr_id_via_barcode_or_usrname {
3193 my($self, $conn, $auth, $barcode, $username) = @_;
3194 my $e = new_editor(authtoken => $auth);
3195 return $e->die_event unless $e->checkauth;
3196 my $id_as_barcode= OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.actor' => app_settings => 'id_as_barcode');
3198 my $user_by_barcode;
3199 my $user_by_username;
3200 $logger->info("$id_as_barcode is the ID as BARCODE");
3202 my $card = $e->search_actor_card([
3203 {barcode => $barcode},
3204 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0];
3205 if ($id_as_barcode =~ /^t/i) {
3207 $user = $e->retrieve_actor_user($barcode);
3208 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$user);
3210 $user_by_barcode = $card->usr;
3211 $user = $user_by_barcode;
3214 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$card);
3215 $user_by_barcode = $card->usr;
3216 $user = $user_by_barcode;
3221 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return OpenILS::Event->new( 'ACTOR_USR_NOT_FOUND' );
3223 $user = $user_by_username;
3225 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if (!$user);
3226 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3227 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3232 __PACKAGE__->register_method (
3233 method => 'merge_users',
3234 api_name => 'open-ils.actor.user.merge',
3237 Given a list of source users and destination user, transfer all data from the source
3238 to the dest user and delete the source user. All user related data is
3239 transferred, including circulations, holds, bookbags, etc.
3245 my($self, $conn, $auth, $master_id, $user_ids, $options) = @_;
3246 my $e = new_editor(xact => 1, authtoken => $auth);
3247 return $e->die_event unless $e->checkauth;
3249 # disallow the merge if any subordinate accounts are in collections
3250 my $colls = $e->search_money_collections_tracker({usr => $user_ids}, {idlist => 1});
3251 return OpenILS::Event->new('MERGED_USER_IN_COLLECTIONS', payload => $user_ids) if @$colls;
3253 my $master_user = $e->retrieve_actor_user($master_id) or return $e->die_event;
3254 my $del_addrs = ($U->ou_ancestor_setting_value(
3255 $master_user->home_ou, 'circ.user_merge.delete_addresses', $e)) ? 't' : 'f';
3256 my $del_cards = ($U->ou_ancestor_setting_value(
3257 $master_user->home_ou, 'circ.user_merge.delete_cards', $e)) ? 't' : 'f';
3258 my $deactivate_cards = ($U->ou_ancestor_setting_value(
3259 $master_user->home_ou, 'circ.user_merge.deactivate_cards', $e)) ? 't' : 'f';
3261 for my $src_id (@$user_ids) {
3262 my $src_user = $e->retrieve_actor_user($src_id) or return $e->die_event;
3264 return $e->die_event unless $e->allowed('MERGE_USERS', $src_user->home_ou);
3265 if($src_user->home_ou ne $master_user->home_ou) {
3266 return $e->die_event unless $e->allowed('MERGE_USERS', $master_user->home_ou);
3269 return $e->die_event unless
3270 $e->json_query({from => [
3285 __PACKAGE__->register_method (
3286 method => 'approve_user_address',
3287 api_name => 'open-ils.actor.user.pending_address.approve',
3294 sub approve_user_address {
3295 my($self, $conn, $auth, $addr) = @_;
3296 my $e = new_editor(xact => 1, authtoken => $auth);
3297 return $e->die_event unless $e->checkauth;
3299 # if the caller passes an address object, assume they want to
3300 # update it first before approving it
3301 $e->update_actor_user_address($addr) or return $e->die_event;
3303 $addr = $e->retrieve_actor_user_address($addr) or return $e->die_event;
3305 my $user = $e->retrieve_actor_user($addr->usr);
3306 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3307 my $result = $e->json_query({from => ['actor.approve_pending_address', $addr->id]})->[0]
3308 or return $e->die_event;
3310 return [values %$result]->[0];
3314 __PACKAGE__->register_method (
3315 method => 'retrieve_friends',
3316 api_name => 'open-ils.actor.friends.retrieve',
3319 returns { confirmed: [], pending_out: [], pending_in: []}
3320 pending_out are users I'm requesting friendship with
3321 pending_in are users requesting friendship with me
3326 sub retrieve_friends {
3327 my($self, $conn, $auth, $user_id, $options) = @_;
3328 my $e = new_editor(authtoken => $auth);
3329 return $e->event unless $e->checkauth;
3330 $user_id ||= $e->requestor->id;
3332 if($user_id != $e->requestor->id) {
3333 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3334 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3337 return OpenILS::Application::Actor::Friends->retrieve_friends(
3338 $e, $user_id, $options);
3343 __PACKAGE__->register_method (
3344 method => 'apply_friend_perms',
3345 api_name => 'open-ils.actor.friends.perms.apply',
3351 sub apply_friend_perms {
3352 my($self, $conn, $auth, $user_id, $delegate_id, @perms) = @_;
3353 my $e = new_editor(authtoken => $auth, xact => 1);
3354 return $e->event unless $e->checkauth;
3356 if($user_id != $e->requestor->id) {
3357 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3358 return $e->die_event unless $e->allowed('VIEW_USER', $user->home_ou);
3361 for my $perm (@perms) {
3363 OpenILS::Application::Actor::Friends->apply_friend_perm(
3364 $e, $user_id, $delegate_id, $perm);
3365 return $evt if $evt;
3373 __PACKAGE__->register_method (
3374 method => 'update_user_pending_address',
3375 api_name => 'open-ils.actor.user.address.pending.cud'
3378 sub update_user_pending_address {
3379 my($self, $conn, $auth, $addr) = @_;
3380 my $e = new_editor(authtoken => $auth, xact => 1);
3381 return $e->event unless $e->checkauth;
3383 if($addr->usr != $e->requestor->id) {
3384 my $user = $e->retrieve_actor_user($addr->usr) or return $e->die_event;
3385 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3389 $e->create_actor_user_address($addr) or return $e->die_event;
3390 } elsif($addr->isdeleted) {
3391 $e->delete_actor_user_address($addr) or return $e->die_event;
3393 $e->update_actor_user_address($addr) or return $e->die_event;
3401 __PACKAGE__->register_method (
3402 method => 'user_events',
3403 api_name => 'open-ils.actor.user.events.circ',
3406 __PACKAGE__->register_method (
3407 method => 'user_events',
3408 api_name => 'open-ils.actor.user.events.ahr',
3413 my($self, $conn, $auth, $user_id, $filters) = @_;
3414 my $e = new_editor(authtoken => $auth);
3415 return $e->event unless $e->checkauth;
3417 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3418 my $user_field = 'usr';
3421 $filters->{target} = {
3422 select => { $obj_type => ['id'] },
3424 where => {usr => $user_id}
3427 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3428 if($e->requestor->id != $user_id) {
3429 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3432 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3433 my $req = $ses->request('open-ils.trigger.events_by_target',
3434 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3436 while(my $resp = $req->recv) {
3437 my $val = $resp->content;
3438 my $tgt = $val->target;
3440 if($obj_type eq 'circ') {
3441 $tgt->target_copy($e->retrieve_asset_copy($tgt->target_copy));
3443 } elsif($obj_type eq 'ahr') {
3444 $tgt->current_copy($e->retrieve_asset_copy($tgt->current_copy))
3445 if $tgt->current_copy;
3448 $conn->respond($val) if $val;
3454 __PACKAGE__->register_method (
3455 method => 'copy_events',
3456 api_name => 'open-ils.actor.copy.events.circ',
3459 __PACKAGE__->register_method (
3460 method => 'copy_events',
3461 api_name => 'open-ils.actor.copy.events.ahr',
3466 my($self, $conn, $auth, $copy_id, $filters) = @_;
3467 my $e = new_editor(authtoken => $auth);
3468 return $e->event unless $e->checkauth;
3470 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3472 my $copy = $e->retrieve_asset_copy($copy_id) or return $e->event;
3474 my $copy_field = 'target_copy';
3475 $copy_field = 'current_copy' if $obj_type eq 'ahr';
3478 $filters->{target} = {
3479 select => { $obj_type => ['id'] },
3481 where => {$copy_field => $copy_id}
3485 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3486 my $req = $ses->request('open-ils.trigger.events_by_target',
3487 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3489 while(my $resp = $req->recv) {
3490 my $val = $resp->content;
3491 my $tgt = $val->target;
3493 my $user = $e->retrieve_actor_user($tgt->usr);
3494 if($e->requestor->id != $user->id) {
3495 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3498 $tgt->$copy_field($copy);
3501 $conn->respond($val) if $val;
3510 __PACKAGE__->register_method (
3511 method => 'update_events',
3512 api_name => 'open-ils.actor.user.event.cancel.batch',
3515 __PACKAGE__->register_method (
3516 method => 'update_events',
3517 api_name => 'open-ils.actor.user.event.reset.batch',
3522 my($self, $conn, $auth, $event_ids) = @_;
3523 my $e = new_editor(xact => 1, authtoken => $auth);
3524 return $e->die_event unless $e->checkauth;
3527 for my $id (@$event_ids) {
3529 # do a little dance to determine what user we are ultimately affecting
3530 my $event = $e->retrieve_action_trigger_event([
3533 flesh_fields => {atev => ['event_def'], atevdef => ['hook']}
3535 ]) or return $e->die_event;
3538 if($event->event_def->hook->core_type eq 'circ') {
3539 $user_id = $e->retrieve_action_circulation($event->target)->usr;
3540 } elsif($event->event_def->hook->core_type eq 'ahr') {
3541 $user_id = $e->retrieve_action_hold_request($event->target)->usr;
3546 my $user = $e->retrieve_actor_user($user_id);
3547 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3549 if($self->api_name =~ /cancel/) {
3550 $event->state('invalid');
3551 } elsif($self->api_name =~ /reset/) {
3552 $event->clear_start_time;
3553 $event->clear_update_time;
3554 $event->state('pending');
3557 $e->update_action_trigger_event($event) or return $e->die_event;
3558 $conn->respond({maximum => scalar(@$event_ids), progress => $x++});
3562 return {complete => 1};
3566 __PACKAGE__->register_method (
3567 method => 'really_delete_user',
3568 api_name => 'open-ils.actor.user.delete',
3570 It anonymizes all personally identifiable information in actor.usr. By calling actor.usr_purge_data()
3571 it also purges related data from other tables, sometimes by transferring it to a designated destination user.
3572 The usrname field (along with first_given_name and family_name) is updated to id '-PURGED-' now().
3573 dest_usr_id is only required when deleting a user that performs staff functions.
3577 sub really_delete_user {
3578 my($self, $conn, $auth, $user_id, $dest_user_id) = @_;
3579 my $e = new_editor(authtoken => $auth, xact => 1);
3580 return $e->die_event unless $e->checkauth;
3581 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3582 return $e->die_event unless $e->allowed('DELETE_USER', $user->home_ou);
3583 my $stat = $e->json_query(
3584 {from => ['actor.usr_delete', $user_id, $dest_user_id]})->[0]
3585 or return $e->die_event;
3592 __PACKAGE__->register_method (
3593 method => 'user_payments',
3594 api_name => 'open-ils.actor.user.payments.retrieve',
3597 Returns all payments for a given user. Default order is newest payments first.
3598 @param auth Authentication token
3599 @param user_id The user ID
3600 @param filters An optional hash of filters, including limit, offset, and order_by definitions
3605 my($self, $conn, $auth, $user_id, $filters) = @_;
3608 my $e = new_editor(authtoken => $auth);
3609 return $e->die_event unless $e->checkauth;
3611 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3612 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS', $user->home_ou);
3614 # Find all payments for all transactions for user $user_id
3616 select => {mp => ['id']},
3621 select => {mbt => ['id']},
3623 where => {usr => $user_id}
3627 order_by => [{ # by default, order newest payments first
3629 field => 'payment_ts',
3634 for (qw/order_by limit offset/) {
3635 $query->{$_} = $filters->{$_} if defined $filters->{$_};
3638 if(defined $filters->{where}) {
3639 foreach (keys %{$filters->{where}}) {
3640 # don't allow the caller to expand the result set to other users
3641 $query->{where}->{$_} = $filters->{where}->{$_} unless $_ eq 'xact';
3645 my $payment_ids = $e->json_query($query);
3646 for my $pid (@$payment_ids) {
3647 my $pay = $e->retrieve_money_payment([
3652 mbt => ['summary', 'circulation', 'grocery'],
3653 circ => ['target_copy'],
3654 acp => ['call_number'],
3662 xact_type => $pay->xact->summary->xact_type,
3663 last_billing_type => $pay->xact->summary->last_billing_type,
3666 if($pay->xact->summary->xact_type eq 'circulation') {
3667 $resp->{barcode} = $pay->xact->circulation->target_copy->barcode;
3668 $resp->{title} = $U->record_to_mvr($pay->xact->circulation->target_copy->call_number->record)->title;
3671 $pay->xact($pay->xact->id); # de-flesh
3672 $conn->respond($resp);
3680 __PACKAGE__->register_method (
3681 method => 'negative_balance_users',
3682 api_name => 'open-ils.actor.users.negative_balance',
3685 Returns all users that have an overall negative balance
3686 @param auth Authentication token
3687 @param org_id The context org unit as an ID or list of IDs. This will be the home
3688 library of the user. If no org_unit is specified, no org unit filter is applied
3692 sub negative_balance_users {
3693 my($self, $conn, $auth, $org_id) = @_;
3695 my $e = new_editor(authtoken => $auth);
3696 return $e->die_event unless $e->checkauth;
3697 return $e->die_event unless $e->allowed('VIEW_USER', $org_id);
3701 mous => ['usr', 'balance_owed'],
3704 {column => 'last_billing_ts', transform => 'max', aggregate => 1},
3705 {column => 'last_payment_ts', transform => 'max', aggregate => 1},
3722 where => {'+mous' => {balance_owed => {'<' => 0}}}
3725 $query->{from}->{mous}->{au}->{filter}->{home_ou} = $org_id if $org_id;
3727 my $list = $e->json_query($query, {timeout => 600});
3729 for my $data (@$list) {
3731 usr => $e->retrieve_actor_user([$data->{usr}, {flesh => 1, flesh_fields => {au => ['card']}}]),
3732 balance_owed => $data->{balance_owed},
3733 last_billing_activity => max($data->{last_billing_ts}, $data->{last_payment_ts})