]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Actor.pm
updating org settings now requires an explicit permission per setting
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Actor.pm
1 package OpenILS::Application::Actor;
2 use OpenILS::Application;
3 use base qw/OpenILS::Application/;
4 use strict; use warnings;
5 use Data::Dumper;
6 $Data::Dumper::Indent = 0;
7 use OpenILS::Event;
8
9 use Digest::MD5 qw(md5_hex);
10
11 use OpenSRF::EX qw(:try);
12 use OpenILS::Perm;
13
14 use OpenILS::Application::AppUtils;
15
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;
21
22 use OpenSRF::Utils::Cache;
23
24 use OpenSRF::Utils::JSON;
25 use DateTime;
26 use DateTime::Format::ISO8601;
27 use OpenILS::Const qw/:const/;
28
29 use OpenILS::Application::Actor::Container;
30 use OpenILS::Application::Actor::ClosedDates;
31
32 use OpenILS::Utils::CStoreEditor qw/:funcs/;
33
34 use OpenILS::Application::Actor::UserGroups;
35 sub initialize {
36         OpenILS::Application::Actor::Container->initialize();
37         OpenILS::Application::Actor::UserGroups->initialize();
38         OpenILS::Application::Actor::ClosedDates->initialize();
39 }
40
41 my $apputils = "OpenILS::Application::AppUtils";
42 my $U = $apputils;
43
44 sub _d { warn "Patron:\n" . Dumper(shift()); }
45
46 my $cache;
47
48
49 my $set_user_settings;
50 my $set_ou_settings;
51
52 __PACKAGE__->register_method(
53         method  => "set_user_settings",
54         api_name        => "open-ils.actor.patron.settings.update",
55 );
56 sub set_user_settings {
57         my( $self, $client, $user_session, $uid, $settings ) = @_;
58         
59         $logger->debug("Setting user settings: $user_session, $uid, " . Dumper($settings));
60
61         my( $staff, $user, $evt ) = 
62                 $apputils->checkses_requestor( $user_session, $uid, 'UPDATE_USER' );    
63         return $evt if $evt;
64         
65         my @params = map { 
66                 [{ usr => $user->id, name => $_}, {value => $$settings{$_}}] } keys %$settings;
67                 
68         $_->[1]->{value} = OpenSRF::Utils::JSON->perl2JSON($_->[1]->{value}) for @params;
69
70         $logger->activity("User " . $staff->id . " updating user $uid settings with: " . Dumper(\@params));
71
72         my $ses = $U->start_db_session();
73         my $stat = $ses->request(
74                 'open-ils.storage.direct.actor.user_setting.batch.merge', @params )->gather(1);
75         $U->commit_db_session($ses);
76
77         return $stat;
78 }
79
80
81 __PACKAGE__->register_method(
82         method  => "set_ou_settings",
83         api_name        => "open-ils.actor.org_unit.settings.update",
84 );
85 sub set_ou_settings {
86         my( $self, $client, $auth, $org_id, $settings ) = @_;
87
88     my $e = new_editor(authtoken => $auth, xact => 1);
89     return $e->die_event unless $e->checkauth;
90
91         for my $name (keys %$settings) {
92         my $val = $$settings{$name};
93         my $set = $e->search_actor_org_unit_setting({org_unit => $org_id, name => $name})->[0];
94
95         return $e->die_event unless $e->allowed("UPDATE_ORG_UNIT_SETTING.$name", $org_id);
96
97         if(defined $val) {
98             $val = OpenSRF::Utils::JSON->perl2JSON($val);
99             if($set) {
100                 $set->value($val);
101                 $e->update_actor_org_unit_setting($set) or return $e->die_event;
102             } else {
103                 $set = Fieldmapper::actor::org_unit_setting->new;
104                 $set->org_unit($org_id);
105                 $set->name($name);
106                 $set->value($val);
107                 $e->create_actor_org_unit_setting($set) or return $e->die_event;
108             }
109         } elsif($set) {
110             $e->delete_actor_org_unit_setting($set) or return $e->die_event;
111         }
112     }
113
114     $e->commit;
115     return 1;
116 }
117
118 my $fetch_user_settings;
119 my $fetch_ou_settings;
120
121 __PACKAGE__->register_method(
122         method  => "user_settings",
123         api_name        => "open-ils.actor.patron.settings.retrieve",
124 );
125 sub user_settings {
126         my( $self, $client, $auth, $user_id, $setting ) = @_;
127
128     my $e = new_editor(authtoken => $auth);
129     return $e->event unless $e->checkauth;
130
131     my $patron = $e->retrieve_actor_user($user_id) or return $e->event;
132     if($e->requestor->id != $user_id) {
133         return $e->event unless $e->allowed('VIEW_USER', $patron->home_ou);
134     }
135
136     my $s = $e->search_actor_user_setting({usr => $user_id});
137         my $settings =  { map { ( $_->name => OpenSRF::Utils::JSON->JSON2perl($_->value) ) } @$s };
138
139     return $$settings{$setting} if $setting;
140     return $settings;
141 }
142
143
144
145 __PACKAGE__->register_method(
146         method  => "ou_settings",
147         api_name        => "open-ils.actor.org_unit.settings.retrieve",
148 );
149 sub ou_settings {
150         my( $self, $client, $ouid ) = @_;
151         
152         $logger->info("Fetching org unit settings for org $ouid");
153
154         my $s = $apputils->simplereq(
155                 'open-ils.cstore',
156                 'open-ils.cstore.direct.actor.org_unit_setting.search.atomic', {org_unit => $ouid});
157
158         return { map { ( $_->name => OpenSRF::Utils::JSON->JSON2perl($_->value) ) } @$s };
159 }
160
161
162
163 __PACKAGE__->register_method(
164     api_name => 'open-ils.actor.ou_setting.ancestor_default',
165     method => 'ou_ancestor_setting',
166 );
167
168 # ------------------------------------------------------------------
169 # Attempts to find the org setting value for a given org.  if not 
170 # found at the requested org, searches up the org tree until it 
171 # finds a parent that has the requested setting.
172 # when found, returns { org => $id, value => $value }
173 # otherwise, returns NULL
174 # ------------------------------------------------------------------
175 sub ou_ancestor_setting {
176     my( $self, $client, $orgid, $name ) = @_;
177     return $U->ou_ancestor_setting($orgid, $name);
178 }
179
180 __PACKAGE__->register_method(
181     api_name => 'open-ils.actor.ou_setting.ancestor_default.batch',
182     method => 'ou_ancestor_setting_batch',
183 );
184 sub ou_ancestor_setting_batch {
185     my( $self, $client, $orgid, $name_list ) = @_;
186     my %values;
187     $values{$_} = $U->ou_ancestor_setting($orgid, $_) for @$name_list;
188     return \%values;
189 }
190
191
192
193
194 __PACKAGE__->register_method (
195         method          => "ou_setting_delete",
196         api_name                => 'open-ils.actor.org_setting.delete',
197         signature       => q/
198                 Deletes a specific org unit setting for a specific location
199                 @param authtoken The login session key
200                 @param orgid The org unit whose setting we're changing
201                 @param setting The name of the setting to delete
202                 @return True value on success.
203         /
204 );
205
206 sub ou_setting_delete {
207         my( $self, $conn, $authtoken, $orgid, $setting ) = @_;
208         my( $reqr, $evt) = $U->checkses($authtoken);
209         return $evt if $evt;
210         $evt = $U->check_perms($reqr->id, $orgid, 'UPDATE_ORG_SETTING');
211         return $evt if $evt;
212
213         my $id = $U->cstorereq(
214                 'open-ils.cstore.direct.actor.org_unit_setting.id_list', 
215                 { name => $setting, org_unit => $orgid } );
216
217         $logger->debug("Retrieved setting $id in org unit setting delete");
218
219         my $s = $U->cstorereq(
220                 'open-ils.cstore.direct.actor.org_unit_setting.delete', $id );
221
222         $logger->activity("User ".$reqr->id." deleted org unit setting $id") if $s;
223         return $s;
224 }
225
226
227
228
229
230
231
232
233
234
235
236 __PACKAGE__->register_method(
237         method  => "update_patron",
238         api_name        => "open-ils.actor.patron.update",);
239
240 sub update_patron {
241         my( $self, $client, $user_session, $patron ) = @_;
242
243         my $session = $apputils->start_db_session();
244         my $err = undef;
245
246
247         $logger->info("Creating new patron...") if $patron->isnew; 
248         $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
249
250         my( $user_obj, $evt ) = $U->checkses($user_session);
251         return $evt if $evt;
252
253         $evt = check_group_perm($session, $user_obj, $patron);
254         return $evt if $evt;
255
256
257         # $new_patron is the patron in progress.  $patron is the original patron
258         # passed in with the method.  new_patron will change as the components
259         # of patron are added/updated.
260
261         my $new_patron;
262
263         # unflesh the real items on the patron
264         $patron->card( $patron->card->id ) if(ref($patron->card));
265         $patron->billing_address( $patron->billing_address->id ) 
266                 if(ref($patron->billing_address));
267         $patron->mailing_address( $patron->mailing_address->id ) 
268                 if(ref($patron->mailing_address));
269
270         # create/update the patron first so we can use his id
271         if($patron->isnew()) {
272                 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
273                 return $evt if $evt;
274         } else { $new_patron = $patron; }
275
276         ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
277         return $evt if $evt;
278
279         ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
280         return $evt if $evt;
281
282         ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
283         return $evt if $evt;
284
285         # re-update the patron if anything has happened to him during this process
286         if($new_patron->ischanged()) {
287                 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
288                 return $evt if $evt;
289         }
290
291         ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
292         return $evt if $evt;
293
294         ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
295         return $evt if $evt;
296
297         ($new_patron, $evt) = _create_standing_penalties($session, $user_session, $patron, $new_patron, $user_obj);
298         return $evt if $evt;
299
300         $logger->activity("user ".$user_obj->id." updating/creating  user ".$new_patron->id);
301
302         my $opatron;
303         if(!$patron->isnew) {
304                 $opatron = new_editor()->retrieve_actor_user($new_patron->id);
305         }
306
307         $apputils->commit_db_session($session);
308         my $fuser =  flesh_user($new_patron->id());
309
310         if( $opatron ) {
311                 # Log the new and old patron for investigation
312                 $logger->info("$user_session updating patron object. orig patron object = ".
313                         OpenSRF::Utils::JSON->perl2JSON($opatron). " |||| new patron = ".OpenSRF::Utils::JSON->perl2JSON($fuser));
314         }
315
316
317         return $fuser;
318 }
319
320
321 sub flesh_user {
322         my $id = shift;
323         return new_flesh_user($id, [
324                 "cards",
325                 "card",
326                 "standing_penalties",
327                 "addresses",
328                 "billing_address",
329                 "mailing_address",
330                 "stat_cat_entries" ] );
331 }
332
333
334
335
336
337
338 # clone and clear stuff that would break the database
339 sub _clone_patron {
340         my $patron = shift;
341
342         my $new_patron = $patron->clone;
343         # clear these
344         $new_patron->clear_billing_address();
345         $new_patron->clear_mailing_address();
346         $new_patron->clear_addresses();
347         $new_patron->clear_card();
348         $new_patron->clear_cards();
349         $new_patron->clear_id();
350         $new_patron->clear_isnew();
351         $new_patron->clear_ischanged();
352         $new_patron->clear_isdeleted();
353         $new_patron->clear_stat_cat_entries();
354         $new_patron->clear_permissions();
355         $new_patron->clear_standing_penalties();
356
357         return $new_patron;
358 }
359
360
361 sub _add_patron {
362
363         my $session             = shift;
364         my $patron              = shift;
365         my $user_obj    = shift;
366
367         my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
368         return (undef, $evt) if $evt;
369
370         my $ex = $session->request(
371                 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
372         if( $ex and @$ex ) {
373                 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
374         }
375
376         $logger->info("Creating new user in the DB with username: ".$patron->usrname());
377
378         my $id = $session->request(
379                 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
380         return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
381
382         $logger->info("Successfully created new user [$id] in DB");
383
384         return ( $session->request( 
385                 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
386 }
387
388
389 sub check_group_perm {
390         my( $session, $requestor, $patron ) = @_;
391         my $evt;
392
393         # first let's see if the requestor has 
394         # priveleges to update this user in any way
395         if( ! $patron->isnew ) {
396                 my $p = $session->request(
397                         'open-ils.storage.direct.actor.user.retrieve', $patron->id )->gather(1);
398
399                 # If we are the requestor (trying to update our own account)
400                 # and we are not trying to change our profile, we're good
401                 if( $p->id == $requestor->id and 
402                                 $p->profile == $patron->profile ) {
403                         return undef;
404                 }
405
406
407                 $evt = group_perm_failed($session, $requestor, $p);
408                 return $evt if $evt;
409         }
410
411         # They are allowed to edit this patron.. can they put the 
412         # patron into the group requested?
413         $evt = group_perm_failed($session, $requestor, $patron);
414         return $evt if $evt;
415         return undef;
416 }
417
418
419 sub group_perm_failed {
420         my( $session, $requestor, $patron ) = @_;
421
422         my $perm;
423         my $grp;
424         my $grpid = $patron->profile;
425
426         do {
427
428                 $logger->debug("user update looking for group perm for group $grpid");
429                 $grp = $session->request(
430                         'open-ils.storage.direct.permission.grp_tree.retrieve', $grpid )->gather(1);
431                 return OpenILS::Event->new('PERMISSION_GRP_TREE_NOT_FOUND') unless $grp;
432
433         } while( !($perm = $grp->application_perm) and ($grpid = $grp->parent) );
434
435         $logger->info("user update checking perm $perm on user ".
436                 $requestor->id." for update/create on user username=".$patron->usrname);
437
438         my $evt = $U->check_perms($requestor->id, $patron->home_ou, $perm);
439         return $evt if $evt;
440         return undef;
441 }
442
443
444
445 sub _update_patron {
446         my( $session, $patron, $user_obj, $noperm) = @_;
447
448         $logger->info("Updating patron ".$patron->id." in DB");
449
450         my $evt;
451
452         if(!$noperm) {
453                 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
454                 return (undef, $evt) if $evt;
455         }
456
457         # update the password by itself to avoid the password protection magic
458         if( $patron->passwd ) {
459                 my $s = $session->request(
460                         'open-ils.storage.direct.actor.user.remote_update',
461                         {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
462                 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
463                 $patron->clear_passwd;
464         }
465
466         if(!$patron->ident_type) {
467                 $patron->clear_ident_type;
468                 $patron->clear_ident_value;
469         }
470
471     $evt = verify_last_xact($session, $patron);
472     return (undef, $evt) if $evt;
473
474         my $stat = $session->request(
475                 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
476         return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
477
478         return ($patron);
479 }
480
481 sub verify_last_xact {
482     my( $session, $patron ) = @_;
483     return undef unless $patron->id and $patron->id > 0;
484     my $p = $session->request(
485         'open-ils.storage.direct.actor.user.retrieve', $patron->id)->gather(1);
486     my $xact = $p->last_xact_id;
487     return undef unless $xact;
488     $logger->info("user xact = $xact, saving with xact " . $patron->last_xact_id);
489     return OpenILS::Event->new('XACT_COLLISION')
490         if $xact != $patron->last_xact_id;
491     return undef;
492 }
493
494
495 sub _check_dup_ident {
496         my( $session, $patron ) = @_;
497
498         return undef unless $patron->ident_value;
499
500         my $search = {
501                 ident_type      => $patron->ident_type, 
502                 ident_value => $patron->ident_value,
503         };
504
505         $logger->debug("patron update searching for dup ident values: " . 
506                 $patron->ident_type . ':' . $patron->ident_value);
507
508         $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
509
510         my $dups = $session->request(
511                 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
512
513
514         return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
515                 if $dups and @$dups;
516
517         return undef;
518 }
519
520
521 sub _add_update_addresses {
522
523         my $session = shift;
524         my $patron = shift;
525         my $new_patron = shift;
526
527         my $evt;
528
529         my $current_id; # id of the address before creation
530
531         for my $address (@{$patron->addresses()}) {
532
533                 next unless ref $address;
534                 $current_id = $address->id();
535
536                 if( $patron->billing_address() and
537                         $patron->billing_address() == $current_id ) {
538                         $logger->info("setting billing addr to $current_id");
539                         $new_patron->billing_address($address->id());
540                         $new_patron->ischanged(1);
541                 }
542         
543                 if( $patron->mailing_address() and
544                         $patron->mailing_address() == $current_id ) {
545                         $new_patron->mailing_address($address->id());
546                         $logger->info("setting mailing addr to $current_id");
547                         $new_patron->ischanged(1);
548                 }
549
550
551                 if($address->isnew()) {
552
553                         $address->usr($new_patron->id());
554
555                         ($address, $evt) = _add_address($session,$address);
556                         return (undef, $evt) if $evt;
557
558                         # we need to get the new id
559                         if( $patron->billing_address() and 
560                                         $patron->billing_address() == $current_id ) {
561                                 $new_patron->billing_address($address->id());
562                                 $logger->info("setting billing addr to $current_id");
563                                 $new_patron->ischanged(1);
564                         }
565
566                         if( $patron->mailing_address() and
567                                         $patron->mailing_address() == $current_id ) {
568                                 $new_patron->mailing_address($address->id());
569                                 $logger->info("setting mailing addr to $current_id");
570                                 $new_patron->ischanged(1);
571                         }
572
573                 } elsif($address->ischanged() ) {
574
575                         ($address, $evt) = _update_address($session, $address);
576                         return (undef, $evt) if $evt;
577
578                 } elsif($address->isdeleted() ) {
579
580                         if( $address->id() == $new_patron->mailing_address() ) {
581                                 $new_patron->clear_mailing_address();
582                                 ($new_patron, $evt) = _update_patron($session, $new_patron);
583                                 return (undef, $evt) if $evt;
584                         }
585
586                         if( $address->id() == $new_patron->billing_address() ) {
587                                 $new_patron->clear_billing_address();
588                                 ($new_patron, $evt) = _update_patron($session, $new_patron);
589                                 return (undef, $evt) if $evt;
590                         }
591
592                         $evt = _delete_address($session, $address);
593                         return (undef, $evt) if $evt;
594                 } 
595         }
596
597         return ( $new_patron, undef );
598 }
599
600
601 # adds an address to the db and returns the address with new id
602 sub _add_address {
603         my($session, $address) = @_;
604         $address->clear_id();
605
606         $logger->info("Creating new address at street ".$address->street1);
607
608         # put the address into the database
609         my $id = $session->request(
610                 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
611         return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
612
613         $address->id( $id );
614         return ($address, undef);
615 }
616
617
618 sub _update_address {
619         my( $session, $address ) = @_;
620
621         $logger->info("Updating address ".$address->id." in the DB");
622
623         my $stat = $session->request(
624                 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
625
626         return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
627         return ($address, undef);
628 }
629
630
631
632 sub _add_update_cards {
633
634         my $session = shift;
635         my $patron = shift;
636         my $new_patron = shift;
637
638         my $evt;
639
640         my $virtual_id; #id of the card before creation
641         for my $card (@{$patron->cards()}) {
642
643                 $card->usr($new_patron->id());
644
645                 if(ref($card) and $card->isnew()) {
646
647                         $virtual_id = $card->id();
648                         ( $card, $evt ) = _add_card($session,$card);
649                         return (undef, $evt) if $evt;
650
651                         #if(ref($patron->card)) { $patron->card($patron->card->id); }
652                         if($patron->card() == $virtual_id) {
653                                 $new_patron->card($card->id());
654                                 $new_patron->ischanged(1);
655                         }
656
657                 } elsif( ref($card) and $card->ischanged() ) {
658                         $evt = _update_card($session, $card);
659                         return (undef, $evt) if $evt;
660                 }
661         }
662
663         return ( $new_patron, undef );
664 }
665
666
667 # adds an card to the db and returns the card with new id
668 sub _add_card {
669         my( $session, $card ) = @_;
670         $card->clear_id();
671
672         $logger->info("Adding new patron card ".$card->barcode);
673
674         my $id = $session->request(
675                 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
676         return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
677         $logger->info("Successfully created patron card $id");
678
679         $card->id($id);
680         return ( $card, undef );
681 }
682
683
684 # returns event on error.  returns undef otherwise
685 sub _update_card {
686         my( $session, $card ) = @_;
687         $logger->info("Updating patron card ".$card->id);
688
689         my $stat = $session->request(
690                 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
691         return $U->DB_UPDATE_FAILED($card) unless defined($stat);
692         return undef;
693 }
694
695
696
697
698 # returns event on error.  returns undef otherwise
699 sub _delete_address {
700         my( $session, $address ) = @_;
701
702         $logger->info("Deleting address ".$address->id." from DB");
703
704         my $stat = $session->request(
705                 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
706
707         return $U->DB_UPDATE_FAILED($address) unless defined($stat);
708         return undef;
709 }
710
711
712
713 sub _add_survey_responses {
714         my ($session, $patron, $new_patron) = @_;
715
716         $logger->info( "Updating survey responses for patron ".$new_patron->id );
717
718         my $responses = $patron->survey_responses;
719
720         if($responses) {
721
722                 $_->usr($new_patron->id) for (@$responses);
723
724                 my $evt = $U->simplereq( "open-ils.circ", 
725                         "open-ils.circ.survey.submit.user_id", $responses );
726
727                 return (undef, $evt) if defined($U->event_code($evt));
728
729         }
730
731         return ( $new_patron, undef );
732 }
733
734
735 sub _create_stat_maps {
736
737         my($session, $user_session, $patron, $new_patron) = @_;
738
739         my $maps = $patron->stat_cat_entries();
740
741         for my $map (@$maps) {
742
743                 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
744
745                 if ($map->isdeleted()) {
746                         $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
747
748                 } elsif ($map->isnew()) {
749                         $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
750                         $map->clear_id;
751                 }
752
753
754                 $map->target_usr($new_patron->id);
755
756                 #warn "
757                 $logger->info("Updating stat entry with method $method and map $map");
758
759                 my $stat = $session->request($method, $map)->gather(1);
760                 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
761
762         }
763
764         return ($new_patron, undef);
765 }
766
767 sub _create_perm_maps {
768
769         my($session, $user_session, $patron, $new_patron) = @_;
770
771         my $maps = $patron->permissions;
772
773         for my $map (@$maps) {
774
775                 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
776                 if ($map->isdeleted()) {
777                         $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
778                 } elsif ($map->isnew()) {
779                         $method = "open-ils.storage.direct.permission.usr_perm_map.create";
780                         $map->clear_id;
781                 }
782
783
784                 $map->usr($new_patron->id);
785
786                 #warn( "Updating permissions with method $method and session $user_session and map $map" );
787                 $logger->info( "Updating permissions with method $method and map $map" );
788
789                 my $stat = $session->request($method, $map)->gather(1);
790                 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
791
792         }
793
794         return ($new_patron, undef);
795 }
796
797
798 __PACKAGE__->register_method(
799         method  => "set_user_work_ous",
800         api_name        => "open-ils.actor.user.work_ous.update",
801 );
802
803 sub set_user_work_ous {
804         my $self = shift;
805         my $client = shift;
806         my $ses = shift;
807         my $maps = shift;
808
809         my( $requestor, $evt ) = $apputils->checksesperm( $ses, 'ASSIGN_WORK_ORG_UNIT' );
810         return $evt if $evt;
811
812         my $session = $apputils->start_db_session();
813
814         for my $map (@$maps) {
815
816                 my $method = "open-ils.storage.direct.permission.usr_work_ou_map.update";
817                 if ($map->isdeleted()) {
818                         $method = "open-ils.storage.direct.permission.usr_work_ou_map.delete";
819                 } elsif ($map->isnew()) {
820                         $method = "open-ils.storage.direct.permission.usr_work_ou_map.create";
821                         $map->clear_id;
822                 }
823
824                 #warn( "Updating permissions with method $method and session $ses and map $map" );
825                 $logger->info( "Updating work_ou map with method $method and map $map" );
826
827                 my $stat = $session->request($method, $map)->gather(1);
828                 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
829
830         }
831
832         $apputils->commit_db_session($session);
833
834         return scalar(@$maps);
835 }
836
837
838 __PACKAGE__->register_method(
839         method  => "set_user_perms",
840         api_name        => "open-ils.actor.user.permissions.update",
841 );
842
843 sub set_user_perms {
844         my $self = shift;
845         my $client = shift;
846         my $ses = shift;
847         my $maps = shift;
848
849         my $session = $apputils->start_db_session();
850
851         my( $user_obj, $evt ) = $U->checkses($ses);
852         return $evt if $evt;
853
854         my $perms = $session->request('open-ils.storage.permission.user_perms.atomic', $user_obj->id)->gather(1);
855
856         my $all = undef;
857         $all = 1 if ($U->is_true($user_obj->super_user()));
858     $all = 1 unless ($U->check_perms($user_obj->id, $user_obj->home_ou, 'EVERYTHING'));
859
860         for my $map (@$maps) {
861
862                 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
863                 if ($map->isdeleted()) {
864                         $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
865                 } elsif ($map->isnew()) {
866                         $method = "open-ils.storage.direct.permission.usr_perm_map.create";
867                         $map->clear_id;
868                 }
869
870                 next if (!$all and !grep { $_->perm eq $map->perm and $U->is_true($_->grantable) and $_->depth <= $map->depth } @$perms);
871                 #warn( "Updating permissions with method $method and session $ses and map $map" );
872                 $logger->info( "Updating permissions with method $method and map $map" );
873
874                 my $stat = $session->request($method, $map)->gather(1);
875                 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
876
877         }
878
879         $apputils->commit_db_session($session);
880
881         return scalar(@$maps);
882 }
883
884
885 sub _create_standing_penalties {
886
887         my($session, $user_session, $patron, $new_patron) = @_;
888
889         my $maps = $patron->standing_penalties;
890         my $method;
891
892         for my $map (@$maps) {
893
894                 if ($map->isdeleted()) {
895                         $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
896                 } elsif ($map->isnew()) {
897                         $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
898                         $map->clear_id;
899                 } else {
900                         next;
901                 }
902
903                 $map->usr($new_patron->id);
904
905                 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
906
907                 my $stat = $session->request($method, $map)->gather(1);
908                 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
909         }
910
911         return ($new_patron, undef);
912 }
913
914
915
916 __PACKAGE__->register_method(
917         method  => "search_username",
918         api_name        => "open-ils.actor.user.search.username",
919 );
920
921 sub search_username {
922         my($self, $client, $username) = @_;
923     return new_editor()->search_actor_user({usrname=>$username});
924 }
925
926
927
928
929 __PACKAGE__->register_method(
930         method  => "user_retrieve_by_barcode",
931     authoritative => 1,
932         api_name        => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
933
934 sub user_retrieve_by_barcode {
935         my($self, $client, $user_session, $barcode) = @_;
936
937         $logger->debug("Searching for user with barcode $barcode");
938         my ($user_obj, $evt) = $apputils->checkses($user_session);
939         return $evt if $evt;
940
941         my $card = OpenILS::Application::AppUtils->simple_scalar_request(
942                         "open-ils.cstore", 
943                         "open-ils.cstore.direct.actor.card.search.atomic",
944                         { barcode => $barcode }
945         );
946
947         if(!$card || !$card->[0]) {
948                 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
949         }
950
951         $card = $card->[0];
952         my $user = flesh_user($card->usr());
953
954         $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
955         return $evt if $evt;
956
957         if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
958         return $user;
959
960 }
961
962
963
964 __PACKAGE__->register_method(
965         method  => "get_user_by_id",
966         api_name        => "open-ils.actor.user.retrieve",);
967
968 sub get_user_by_id {
969         my ($self, $client, $auth, $id) = @_;
970         my $e = new_editor(authtoken=>$auth);
971         return $e->event unless $e->checkauth;
972         my $user = $e->retrieve_actor_user($id)
973                 or return $e->event;
974         return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);       
975         return $user;
976 }
977
978
979
980 __PACKAGE__->register_method(
981         method  => "get_org_types",
982         api_name        => "open-ils.actor.org_types.retrieve",);
983
984 sub get_org_types {
985     return $U->get_org_types();
986 }
987
988
989
990 __PACKAGE__->register_method(
991         method  => "get_user_ident_types",
992         api_name        => "open-ils.actor.user.ident_types.retrieve",
993 );
994 my $ident_types;
995 sub get_user_ident_types {
996         return $ident_types if $ident_types;
997         return $ident_types = 
998                 new_editor()->retrieve_all_config_identification_type();
999 }
1000
1001
1002
1003
1004 __PACKAGE__->register_method(
1005         method  => "get_org_unit",
1006         api_name        => "open-ils.actor.org_unit.retrieve",
1007 );
1008
1009 sub get_org_unit {
1010         my( $self, $client, $user_session, $org_id ) = @_;
1011         my $e = new_editor(authtoken => $user_session);
1012         if(!$org_id) {
1013                 return $e->event unless $e->checkauth;
1014                 $org_id = $e->requestor->ws_ou;
1015         }
1016         my $o = $e->retrieve_actor_org_unit($org_id)
1017                 or return $e->event;
1018         return $o;
1019 }
1020
1021 __PACKAGE__->register_method(
1022         method  => "search_org_unit",
1023         api_name        => "open-ils.actor.org_unit_list.search",
1024 );
1025
1026 sub search_org_unit {
1027
1028         my( $self, $client, $field, $value ) = @_;
1029
1030         my $list = OpenILS::Application::AppUtils->simple_scalar_request(
1031                 "open-ils.cstore",
1032                 "open-ils.cstore.direct.actor.org_unit.search.atomic", 
1033                 { $field => $value } );
1034
1035         return $list;
1036 }
1037
1038
1039 # build the org tree
1040
1041 __PACKAGE__->register_method(
1042         method  => "get_org_tree",
1043         api_name        => "open-ils.actor.org_tree.retrieve",
1044         argc            => 0, 
1045         note            => "Returns the entire org tree structure",
1046 );
1047
1048 sub get_org_tree {
1049         my $self = shift;
1050         my $client = shift;
1051     return $U->get_org_tree($client->session->session_locale);
1052 }
1053
1054
1055 __PACKAGE__->register_method(
1056         method  => "get_org_descendants",
1057         api_name        => "open-ils.actor.org_tree.descendants.retrieve"
1058 );
1059
1060 # depth is optional.  org_unit is the id
1061 sub get_org_descendants {
1062         my( $self, $client, $org_unit, $depth ) = @_;
1063
1064     if(ref $org_unit eq 'ARRAY') {
1065         $depth ||= [];
1066         my @trees;
1067         for my $i (0..scalar(@$org_unit)-1) {
1068             my $list = $U->simple_scalar_request(
1069                             "open-ils.storage", 
1070                             "open-ils.storage.actor.org_unit.descendants.atomic",
1071                             $org_unit->[$i], $depth->[$i] );
1072             push(@trees, $U->build_org_tree($list));
1073         }
1074         return \@trees;
1075
1076     } else {
1077             my $orglist = $apputils->simple_scalar_request(
1078                             "open-ils.storage", 
1079                             "open-ils.storage.actor.org_unit.descendants.atomic",
1080                             $org_unit, $depth );
1081             return $U->build_org_tree($orglist);
1082     }
1083 }
1084
1085
1086 __PACKAGE__->register_method(
1087         method  => "get_org_ancestors",
1088         api_name        => "open-ils.actor.org_tree.ancestors.retrieve"
1089 );
1090
1091 # depth is optional.  org_unit is the id
1092 sub get_org_ancestors {
1093         my( $self, $client, $org_unit, $depth ) = @_;
1094         my $orglist = $apputils->simple_scalar_request(
1095                         "open-ils.storage", 
1096                         "open-ils.storage.actor.org_unit.ancestors.atomic",
1097                         $org_unit, $depth );
1098         return $U->build_org_tree($orglist);
1099 }
1100
1101
1102 __PACKAGE__->register_method(
1103         method  => "get_standings",
1104         api_name        => "open-ils.actor.standings.retrieve"
1105 );
1106
1107 my $user_standings;
1108 sub get_standings {
1109         return $user_standings if $user_standings;
1110         return $user_standings = 
1111                 $apputils->simple_scalar_request(
1112                         "open-ils.cstore",
1113                         "open-ils.cstore.direct.config.standing.search.atomic",
1114                         { id => { "!=" => undef } }
1115                 );
1116 }
1117
1118
1119
1120 __PACKAGE__->register_method(
1121         method  => "get_my_org_path",
1122         api_name        => "open-ils.actor.org_unit.full_path.retrieve"
1123 );
1124
1125 sub get_my_org_path {
1126         my( $self, $client, $auth, $org_id ) = @_;
1127         my $e = new_editor(authtoken=>$auth);
1128         return $e->event unless $e->checkauth;
1129         $org_id = $e->requestor->ws_ou unless defined $org_id;
1130
1131         return $apputils->simple_scalar_request(
1132                 "open-ils.storage",
1133                 "open-ils.storage.actor.org_unit.full_path.atomic",
1134                 $org_id );
1135 }
1136
1137
1138 __PACKAGE__->register_method(
1139         method  => "patron_adv_search",
1140         api_name        => "open-ils.actor.patron.search.advanced" );
1141 sub patron_adv_search {
1142         my( $self, $client, $auth, $search_hash, 
1143         $search_limit, $search_sort, $include_inactive, $search_depth ) = @_;
1144
1145         my $e = new_editor(authtoken=>$auth);
1146         return $e->event unless $e->checkauth;
1147         return $e->event unless $e->allowed('VIEW_USER');
1148         return $U->storagereq(
1149                 "open-ils.storage.actor.user.crazy_search", $search_hash, 
1150             $search_limit, $search_sort, $include_inactive, $e->requestor->ws_ou, $search_depth);
1151 }
1152
1153
1154 __PACKAGE__->register_method(
1155         method  => "update_passwd",
1156     authoritative => 1,
1157         api_name        => "open-ils.actor.user.password.update");
1158
1159 __PACKAGE__->register_method(
1160         method  => "update_passwd",
1161         api_name        => "open-ils.actor.user.username.update");
1162
1163 __PACKAGE__->register_method(
1164         method  => "update_passwd",
1165         api_name        => "open-ils.actor.user.email.update");
1166
1167 sub update_passwd {
1168     my( $self, $conn, $auth, $new_val, $orig_pw ) = @_;
1169     my $e = new_editor(xact=>1, authtoken=>$auth);
1170     return $e->die_event unless $e->checkauth;
1171
1172     my $db_user = $e->retrieve_actor_user($e->requestor->id)
1173         or return $e->die_event;
1174     my $api = $self->api_name;
1175
1176     if( $api =~ /password/o ) {
1177
1178         # make sure the original password matches the in-database password
1179         return OpenILS::Event->new('INCORRECT_PASSWORD')
1180             if md5_hex($orig_pw) ne $db_user->passwd;
1181         $db_user->passwd($new_val);
1182
1183     } else {
1184
1185         # if we don't clear the password, the user will be updated with
1186         # a hashed version of the hashed version of their password
1187         $db_user->clear_passwd;
1188
1189         if( $api =~ /username/o ) {
1190
1191             # make sure no one else has this username
1192             my $exist = $e->search_actor_user({usrname=>$new_val},{idlist=>1}); 
1193                         return OpenILS::Event->new('USERNAME_EXISTS') if @$exist;
1194             $db_user->usrname($new_val);
1195
1196         } elsif( $api =~ /email/o ) {
1197             $db_user->email($new_val);
1198         }
1199     }
1200
1201     $e->update_actor_user($db_user) or return $e->die_event;
1202     $e->commit;
1203     return 1;
1204 }
1205
1206
1207
1208
1209 __PACKAGE__->register_method(
1210         method  => "check_user_perms",
1211         api_name        => "open-ils.actor.user.perm.check",
1212         notes           => <<"  NOTES");
1213         Takes a login session, user id, an org id, and an array of perm type strings.  For each
1214         perm type, if the user does *not* have the given permission it is added
1215         to a list which is returned from the method.  If all permissions
1216         are allowed, an empty list is returned
1217         if the logged in user does not match 'user_id', then the logged in user must
1218         have VIEW_PERMISSION priveleges.
1219         NOTES
1220
1221 sub check_user_perms {
1222         my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1223
1224         my( $staff, $evt ) = $apputils->checkses($login_session);
1225         return $evt if $evt;
1226
1227         if($staff->id ne $user_id) {
1228                 if( $evt = $apputils->check_perms(
1229                         $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1230                         return $evt;
1231                 }
1232         }
1233
1234         my @not_allowed;
1235         for my $perm (@$perm_types) {
1236                 if($apputils->check_perms($user_id, $org_id, $perm)) {
1237                         push @not_allowed, $perm;
1238                 }
1239         }
1240
1241         return \@not_allowed
1242 }
1243
1244 __PACKAGE__->register_method(
1245         method  => "check_user_perms2",
1246         api_name        => "open-ils.actor.user.perm.check.multi_org",
1247         notes           => q/
1248                 Checks the permissions on a list of perms and orgs for a user
1249                 @param authtoken The login session key
1250                 @param user_id The id of the user to check
1251                 @param orgs The array of org ids
1252                 @param perms The array of permission names
1253                 @return An array of  [ orgId, permissionName ] arrays that FAILED the check
1254                 if the logged in user does not match 'user_id', then the logged in user must
1255                 have VIEW_PERMISSION priveleges.
1256         /);
1257
1258 sub check_user_perms2 {
1259         my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1260
1261         my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1262                 $authtoken, $user_id, 'VIEW_PERMISSION' );
1263         return $evt if $evt;
1264
1265         my @not_allowed;
1266         for my $org (@$orgs) {
1267                 for my $perm (@$perms) {
1268                         if($apputils->check_perms($user_id, $org, $perm)) {
1269                                 push @not_allowed, [ $org, $perm ];
1270                         }
1271                 }
1272         }
1273
1274         return \@not_allowed
1275 }
1276
1277
1278 __PACKAGE__->register_method(
1279         method => 'check_user_perms3',
1280         api_name        => 'open-ils.actor.user.perm.highest_org',
1281         notes           => q/
1282                 Returns the highest org unit id at which a user has a given permission
1283                 If the requestor does not match the target user, the requestor must have
1284                 'VIEW_PERMISSION' rights at the home org unit of the target user
1285                 @param authtoken The login session key
1286                 @param userid The id of the user in question
1287                 @param perm The permission to check
1288                 @return The org unit highest in the org tree within which the user has
1289                 the requested permission
1290         /);
1291
1292 sub check_user_perms3 {
1293         my($self, $client, $authtoken, $user_id, $perm) = @_;
1294         my $e = new_editor(authtoken=>$authtoken);
1295         return $e->event unless $e->checkauth;
1296
1297         my $tree = $U->get_org_tree();
1298
1299     unless($e->requestor->id == $user_id) {
1300         my $user = $e->retrieve_actor_user($user_id)
1301             or return $e->event;
1302         return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1303             return $U->find_highest_perm_org($perm, $user_id, $user->home_ou, $tree );
1304     }
1305
1306     return $U->find_highest_perm_org($perm, $user_id, $e->requestor->ws_ou, $tree);
1307 }
1308
1309
1310 __PACKAGE__->register_method(
1311         method => 'check_user_work_perms',
1312         api_name        => 'open-ils.actor.user.work_perm.highest_org_set',
1313     authoritative => 1,
1314     signature => {
1315         desc => q/
1316             Returns a set of org units which represent the highest orgs in 
1317             the org tree where the user has the requested permission.  The
1318             purpose of this method is to return the smallest set of org units
1319             which represent the full expanse of the user's ability to perform
1320             the requested action.  The user whose perms this method should
1321             check is implied by the authtoken. /,
1322         params => [
1323                     {desc => 'authtoken', type => 'string'},
1324             {desc => 'permission name', type => 'string'},
1325             {desc => 'options hash, including "descendants", which will include all child orgs of the found perm orgs', type => 'hash'}
1326         ],
1327         return => {desc => 'An array of org IDs'}
1328     }
1329 );
1330
1331 __PACKAGE__->register_method(
1332         method => 'check_user_work_perms',
1333         api_name        => 'open-ils.actor.user.work_perm.org_tree_list',
1334     authoritative => 1,
1335     signature => q/
1336         @see open-ils.actor.user.work_perm.highest_org_set
1337         Returns a list of org trees.  The root of each tree
1338         is the highest org in the organization hierarchy where the user has the
1339         requested permission.  Below each tree root is its full tree of descendants.  
1340     /
1341 );
1342
1343 __PACKAGE__->register_method(
1344         method => 'check_user_work_perms',
1345         api_name        => 'open-ils.actor.user.work_perm.org_unit_list',
1346     authoritative => 1,
1347     signature => q/
1348         @see open-ils.actor.user.work_perm.highest_org_set
1349         Returns a list of list of all of the org_units where the user
1350         has the requested permission.  The first item in each list
1351         is the highest permission org for that section of the
1352         org tree.  The remaining items in each sub-list are the 
1353         descendants of that org.
1354
1355     /
1356 );
1357
1358 __PACKAGE__->register_method(
1359         method => 'check_user_work_perms',
1360         api_name        => 'open-ils.actor.user.work_perm.org_id_list',
1361     authoritative => 1,
1362     signature => q/
1363         @see open-ils.actor.user.work_perm.highest_org_set
1364         Returns a list of lists of all of the org_unit IDs where the user
1365         has the requested permission.  The first item in each list
1366         is the highest permission org for that section of the
1367         org tree.  The remaining items in each sub-list are the 
1368         descendants of that org.
1369     /
1370 );
1371
1372 __PACKAGE__->register_method(
1373         method => 'check_user_work_perms_batch',
1374         api_name        => 'open-ils.actor.user.work_perm.highest_org_set.batch',
1375     authoritative => 1,
1376 );
1377 __PACKAGE__->register_method(
1378         method => 'check_user_work_perms_batch',
1379         api_name        => 'open-ils.actor.user.work_perm.org_tree_list.batch',
1380     authoritative => 1,
1381 );
1382 __PACKAGE__->register_method(
1383         method => 'check_user_work_perms_batch',
1384         api_name        => 'open-ils.actor.user.work_perm.org_unit_list.batch',
1385     authoritative => 1,
1386 );
1387 __PACKAGE__->register_method(
1388         method => 'check_user_work_perms_batch',
1389         api_name        => 'open-ils.actor.user.work_perm.org_id_list.batch',
1390     authoritative => 1,
1391 );
1392
1393
1394 sub check_user_work_perms {
1395     my($self, $conn, $auth, $perm, $options) = @_;
1396     my $e = new_editor(authtoken=>$auth);
1397     return $e->event unless $e->checkauth;
1398     return check_user_work_perms_impl($self, $conn, $e, $perm, $options);
1399 }
1400
1401 sub check_user_work_perms_batch {
1402     my($self, $conn, $auth, $perm_list, $options) = @_;
1403     my $e = new_editor(authtoken=>$auth);
1404     return $e->event unless $e->checkauth;
1405     my $map = {};
1406     $map->{$_} = check_user_work_perms_impl($self, $conn, $e, $_, $options) for @$perm_list;
1407     return $map;
1408 }
1409
1410 sub check_user_work_perms_impl {
1411     my($self, $conn, $e, $perm, $options) = @_;
1412     my $orglist = $U->find_highest_work_orgs($e, $perm, $options);
1413
1414     return $orglist if $self->api_name =~ /highest_org_set/;
1415
1416     # build a list of org trees
1417     return get_org_descendants($self, $conn, $orglist)
1418         if $self->api_name =~ /org_tree_list/;
1419
1420     my @list;
1421     for my $orgid (@$orglist) {
1422         my @sublist = grep {$_ ne $orgid} @{$U->get_org_descendants($orgid)};
1423         unshift @sublist, $orgid; # make sure it's at the front of the list
1424         if($self->api_name =~ /org_id_list/) {
1425             push(@list, @sublist);
1426         } else {
1427             push(@list, @{$e->batch_retrieve_actor_org_unit(\@sublist)});
1428         }
1429     }
1430
1431     return \@list;
1432 }
1433
1434
1435 __PACKAGE__->register_method(
1436         method => 'check_user_perms4',
1437         api_name        => 'open-ils.actor.user.perm.highest_org.batch',
1438         notes           => q/
1439                 Returns the highest org unit id at which a user has a given permission
1440                 If the requestor does not match the target user, the requestor must have
1441                 'VIEW_PERMISSION' rights at the home org unit of the target user
1442                 @param authtoken The login session key
1443                 @param userid The id of the user in question
1444                 @param perms An array of perm names to check 
1445                 @return An array of orgId's  representing the org unit 
1446                 highest in the org tree within which the user has the requested permission
1447                 The arrah of orgId's has matches the order of the perms array
1448         /);
1449
1450 sub check_user_perms4 {
1451         my( $self, $client, $authtoken, $userid, $perms ) = @_;
1452         
1453         my( $staff, $target, $org, $evt );
1454
1455         ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1456                 $authtoken, $userid, 'VIEW_PERMISSION' );
1457         return $evt if $evt;
1458
1459         my @arr;
1460         return [] unless ref($perms);
1461         my $tree = $U->get_org_tree();
1462
1463         for my $p (@$perms) {
1464                 push( @arr, $U->find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1465         }
1466         return \@arr;
1467 }
1468
1469
1470
1471
1472 __PACKAGE__->register_method(
1473         method  => "user_fines_summary",
1474         api_name        => "open-ils.actor.user.fines.summary",
1475     authoritative => 1,
1476         notes           => <<"  NOTES");
1477         Returns a short summary of the users total open fines, excluding voided fines
1478         Params are login_session, user_id
1479         Returns a 'mous' object.
1480         NOTES
1481
1482 sub user_fines_summary {
1483         my( $self, $client, $auth, $user_id ) = @_;
1484         my $e = new_editor(authtoken=>$auth);
1485         return $e->event unless $e->checkauth;
1486         my $user = $e->retrieve_actor_user($user_id)
1487                 or return $e->event;
1488
1489         if( $user_id ne $e->requestor->id ) {
1490                 return $e->event unless 
1491                         $e->allowed('VIEW_USER_FINES_SUMMARY', $user->home_ou);
1492         }
1493         
1494         # run this inside a transaction to prevent replication delay errors
1495         my $ses = $U->start_db_session();
1496         my $s = $ses->request(
1497                 'open-ils.storage.money.open_user_summary.search', $user_id )->gather(1);
1498         $U->rollback_db_session($ses);
1499         return $s;
1500 }
1501
1502
1503
1504
1505 __PACKAGE__->register_method(
1506         method  => "user_transactions",
1507         api_name        => "open-ils.actor.user.transactions",
1508         notes           => <<"  NOTES");
1509         Returns a list of open user transactions (mbts objects);
1510         Params are login_session, user_id
1511         Optional third parameter is the transactions type.  defaults to all
1512         NOTES
1513
1514 __PACKAGE__->register_method(
1515         method  => "user_transactions",
1516         api_name        => "open-ils.actor.user.transactions.have_charge",
1517         notes           => <<"  NOTES");
1518         Returns a list of all open user transactions (mbts objects) that have an initial charge
1519         Params are login_session, user_id
1520         Optional third parameter is the transactions type.  defaults to all
1521         NOTES
1522
1523 __PACKAGE__->register_method(
1524         method  => "user_transactions",
1525         api_name        => "open-ils.actor.user.transactions.have_balance",
1526         notes           => <<"  NOTES");
1527         Returns a list of all open user transactions (mbts objects) that have a balance
1528         Params are login_session, user_id
1529         Optional third parameter is the transactions type.  defaults to all
1530         NOTES
1531
1532 __PACKAGE__->register_method(
1533         method  => "user_transactions",
1534         api_name        => "open-ils.actor.user.transactions.fleshed",
1535         notes           => <<"  NOTES");
1536         Returns an object/hash of transaction, circ, title where transaction = an open 
1537         user transactions (mbts objects), circ is the attached circluation, and title
1538         is the title the circ points to
1539         Params are login_session, user_id
1540         Optional third parameter is the transactions type.  defaults to all
1541         NOTES
1542
1543 __PACKAGE__->register_method(
1544         method  => "user_transactions",
1545         api_name        => "open-ils.actor.user.transactions.have_charge.fleshed",
1546         notes           => <<"  NOTES");
1547         Returns an object/hash of transaction, circ, title where transaction = an open 
1548         user transactions that has an initial charge (mbts objects), circ is the 
1549         attached circluation, and title is the title the circ points to
1550         Params are login_session, user_id
1551         Optional third parameter is the transactions type.  defaults to all
1552         NOTES
1553
1554 __PACKAGE__->register_method(
1555         method  => "user_transactions",
1556         api_name        => "open-ils.actor.user.transactions.have_balance.fleshed",
1557         notes           => <<"  NOTES");
1558         Returns an object/hash of transaction, circ, title where transaction = an open 
1559         user transaction that has a balance (mbts objects), circ is the attached 
1560         circluation, and title is the title the circ points to
1561         Params are login_session, user_id
1562         Optional third parameter is the transaction type.  defaults to all
1563         NOTES
1564
1565 __PACKAGE__->register_method(
1566         method  => "user_transactions",
1567         api_name        => "open-ils.actor.user.transactions.count",
1568         notes           => <<"  NOTES");
1569         Returns an object/hash of transaction, circ, title where transaction = an open 
1570         user transactions (mbts objects), circ is the attached circluation, and title
1571         is the title the circ points to
1572         Params are login_session, user_id
1573         Optional third parameter is the transactions type.  defaults to all
1574         NOTES
1575
1576 __PACKAGE__->register_method(
1577         method  => "user_transactions",
1578         api_name        => "open-ils.actor.user.transactions.have_charge.count",
1579         notes           => <<"  NOTES");
1580         Returns an object/hash of transaction, circ, title where transaction = an open 
1581         user transactions that has an initial charge (mbts objects), circ is the 
1582         attached circluation, and title is the title the circ points to
1583         Params are login_session, user_id
1584         Optional third parameter is the transactions type.  defaults to all
1585         NOTES
1586
1587 __PACKAGE__->register_method(
1588         method  => "user_transactions",
1589         api_name        => "open-ils.actor.user.transactions.have_balance.count",
1590         notes           => <<"  NOTES");
1591         Returns an object/hash of transaction, circ, title where transaction = an open 
1592         user transaction that has a balance (mbts objects), circ is the attached 
1593         circluation, and title is the title the circ points to
1594         Params are login_session, user_id
1595         Optional third parameter is the transaction type.  defaults to all
1596         NOTES
1597
1598 __PACKAGE__->register_method(
1599         method  => "user_transactions",
1600         api_name        => "open-ils.actor.user.transactions.have_balance.total",
1601         notes           => <<"  NOTES");
1602         Returns an object/hash of transaction, circ, title where transaction = an open 
1603         user transaction that has a balance (mbts objects), circ is the attached 
1604         circluation, and title is the title the circ points to
1605         Params are login_session, user_id
1606         Optional third parameter is the transaction type.  defaults to all
1607         NOTES
1608
1609
1610
1611 sub user_transactions {
1612         my( $self, $client, $login_session, $user_id, $type ) = @_;
1613
1614         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1615                 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1616         return $evt if $evt;
1617
1618         my $api = $self->api_name();
1619         my $trans;
1620         my @xact;
1621
1622         if(defined($type)) { @xact = (xact_type =>  $type); 
1623
1624         } else { @xact = (); }
1625
1626         ($trans) = $self
1627                 ->method_lookup('open-ils.actor.user.transactions.history.still_open')
1628                 ->run($login_session => $user_id => $type);
1629
1630         if($api =~ /have_charge/o) {
1631
1632                 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1633
1634         } elsif($api =~ /have_balance/o) {
1635
1636                 $trans = [ grep { int($_->balance_owed * 100) != 0 } @$trans ];
1637         } else {
1638
1639                 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1640
1641         }
1642
1643         if($api =~ /total/o) { 
1644                 my $total = 0.0;
1645                 for my $t (@$trans) {
1646                         $total += $t->balance_owed;
1647                 }
1648
1649                 $logger->debug("Total balance owed by user $user_id: $total");
1650                 return $total;
1651         }
1652
1653         if($api =~ /count/o) { return scalar @$trans; }
1654         if($api !~ /fleshed/o) { return $trans; }
1655
1656         my @resp;
1657         for my $t (@$trans) {
1658                         
1659                 if( $t->xact_type ne 'circulation' ) {
1660                         push @resp, {transaction => $t};
1661                         next;
1662                 }
1663
1664                 my $circ = $apputils->simple_scalar_request(
1665                                 "open-ils.cstore",
1666                                 "open-ils.cstore.direct.action.circulation.retrieve",
1667                                 $t->id );
1668
1669                 next unless $circ;
1670
1671                 my $title = $apputils->simple_scalar_request(
1672                         "open-ils.storage", 
1673                         "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1674                         $circ->target_copy );
1675
1676                 next unless $title;
1677
1678                 my $u = OpenILS::Utils::ModsParser->new();
1679                 $u->start_mods_batch($title->marc());
1680                 my $mods = $u->finish_mods_batch();
1681                 $mods->doc_id($title->id) if $mods;
1682
1683                 push @resp, {transaction => $t, circ => $circ, record => $mods };
1684
1685         }
1686
1687         return \@resp; 
1688
1689
1690
1691 __PACKAGE__->register_method(
1692         method  => "user_transaction_retrieve",
1693         api_name        => "open-ils.actor.user.transaction.fleshed.retrieve",
1694         argc            => 1,
1695         notes           => <<"  NOTES");
1696         Returns a fleshedtransaction record
1697         NOTES
1698 __PACKAGE__->register_method(
1699         method  => "user_transaction_retrieve",
1700         api_name        => "open-ils.actor.user.transaction.retrieve",
1701         argc            => 1,
1702         notes           => <<"  NOTES");
1703         Returns a transaction record
1704         NOTES
1705 sub user_transaction_retrieve {
1706         my( $self, $client, $login_session, $bill_id ) = @_;
1707
1708         # XXX I think I'm deprecated... make sure
1709
1710         my $trans = $apputils->simple_scalar_request( 
1711                 "open-ils.cstore",
1712                 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1713                 $bill_id
1714         );
1715
1716         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1717                 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1718         return $evt if $evt;
1719         
1720         my $api = $self->api_name();
1721         if($api !~ /fleshed/o) { return $trans; }
1722
1723         if( $trans->xact_type ne 'circulation' ) {
1724                 $logger->debug("Returning non-circ transaction");
1725                 return {transaction => $trans};
1726         }
1727
1728         my $circ = $apputils->simple_scalar_request(
1729                         "open-ils.cstore",
1730                         "open-ils..direct.action.circulation.retrieve",
1731                         $trans->id );
1732
1733         return {transaction => $trans} unless $circ;
1734         $logger->debug("Found the circ transaction");
1735
1736         my $title = $apputils->simple_scalar_request(
1737                 "open-ils.storage", 
1738                 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1739                 $circ->target_copy );
1740
1741         return {transaction => $trans, circ => $circ } unless $title;
1742         $logger->debug("Found the circ title");
1743
1744         my $mods;
1745         try {
1746                 my $u = OpenILS::Utils::ModsParser->new();
1747                 $u->start_mods_batch($title->marc());
1748                 $mods = $u->finish_mods_batch();
1749         } otherwise {
1750                 if ($title->id == OILS_PRECAT_RECORD) {
1751                         my $copy = $apputils->simple_scalar_request(
1752                                 "open-ils.cstore",
1753                                 "open-ils.cstore.direct.asset.copy.retrieve",
1754                                 $circ->target_copy );
1755
1756                         $mods = new Fieldmapper::metabib::virtual_record;
1757                         $mods->doc_id(OILS_PRECAT_RECORD);
1758                         $mods->title($copy->dummy_title);
1759                         $mods->author($copy->dummy_author);
1760                 }
1761         };
1762
1763         $logger->debug("MODSized the circ title");
1764
1765         return {transaction => $trans, circ => $circ, record => $mods };
1766 }
1767
1768
1769 __PACKAGE__->register_method(
1770         method  => "hold_request_count",
1771         api_name        => "open-ils.actor.user.hold_requests.count",
1772     authoritative => 1,
1773         argc            => 1,
1774         notes           => <<"  NOTES");
1775         Returns hold ready/total counts
1776         NOTES
1777 sub hold_request_count {
1778         my( $self, $client, $login_session, $userid ) = @_;
1779
1780         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1781                 $login_session, $userid, 'VIEW_HOLD' );
1782         return $evt if $evt;
1783         
1784
1785         my $holds = $apputils->simple_scalar_request(
1786                         "open-ils.cstore",
1787                         "open-ils.cstore.direct.action.hold_request.search.atomic",
1788                         { 
1789                                 usr => $userid,
1790                                 fulfillment_time => {"=" => undef },
1791                                 cancel_time => undef,
1792                         }
1793         );
1794
1795         my @ready;
1796         for my $h (@$holds) {
1797                 next unless $h->capture_time and $h->current_copy;
1798
1799                 my $copy = $apputils->simple_scalar_request(
1800                         "open-ils.cstore",
1801                         "open-ils.cstore.direct.asset.copy.retrieve",
1802                         $h->current_copy
1803                 );
1804
1805                 if ($copy and $copy->status == 8) {
1806                         push @ready, $h;
1807                 }
1808         }
1809
1810         return { total => scalar(@$holds), ready => scalar(@ready) };
1811 }
1812
1813
1814 __PACKAGE__->register_method(
1815         method  => "checkedout_count",
1816         api_name        => "open-ils.actor.user.checked_out.count__",
1817         argc            => 1,
1818         notes           => <<"  NOTES");
1819         Returns a transaction record
1820         NOTES
1821
1822 # XXX Deprecate Me
1823 sub checkedout_count {
1824         my( $self, $client, $login_session, $userid ) = @_;
1825
1826         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1827                 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1828         return $evt if $evt;
1829         
1830         my $circs = $apputils->simple_scalar_request(
1831                         "open-ils.cstore",
1832                         "open-ils.cstore.direct.action.circulation.search.atomic",
1833                         { usr => $userid, stop_fines => undef }
1834                         #{ usr => $userid, checkin_time => {"=" => undef } }
1835         );
1836
1837         my $parser = DateTime::Format::ISO8601->new;
1838
1839         my (@out,@overdue);
1840         for my $c (@$circs) {
1841                 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1842                 my $due = $due_dt->epoch;
1843
1844                 if ($due < DateTime->today->epoch) {
1845                         push @overdue, $c;
1846                 }
1847         }
1848
1849         return { total => scalar(@$circs), overdue => scalar(@overdue) };
1850 }
1851
1852
1853 __PACKAGE__->register_method(
1854         method          => "checked_out",
1855         api_name                => "open-ils.actor.user.checked_out",
1856     authoritative => 1,
1857         argc                    => 2,
1858         signature       => q/
1859                 Returns a structure of circulations objects sorted by
1860                 out, overdue, lost, claims_returned, long_overdue.
1861                 A list of IDs are returned of each type.
1862                 lost, long_overdue, and claims_returned circ will not
1863                 be "finished" (there is an outstanding balance or some 
1864                 other pending action on the circ). 
1865
1866                 The .count method also includes a 'total' field which 
1867                 sums all "open" circs
1868         /
1869 );
1870
1871 __PACKAGE__->register_method(
1872         method          => "checked_out",
1873         api_name                => "open-ils.actor.user.checked_out.count",
1874     authoritative => 1,
1875         argc                    => 2,
1876         signature       => q/@see open-ils.actor.user.checked_out/
1877 );
1878
1879 sub checked_out {
1880         my( $self, $conn, $auth, $userid ) = @_;
1881
1882         my $e = new_editor(authtoken=>$auth);
1883         return $e->event unless $e->checkauth;
1884
1885         if( $userid ne $e->requestor->id ) {
1886                 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1887         }
1888
1889         my $count = $self->api_name =~ /count/;
1890         return _checked_out( $count, $e, $userid );
1891 }
1892
1893 sub _checked_out {
1894         my( $iscount, $e, $userid ) = @_;
1895         my $meth = 'open-ils.storage.actor.user.checked_out';
1896         $meth = "$meth.count" if $iscount;
1897         return $U->storagereq($meth, $userid);
1898 }
1899
1900
1901 sub _checked_out_WHAT {
1902         my( $iscount, $e, $userid ) = @_;
1903
1904         my $circs = $e->search_action_circulation( 
1905                 { usr => $userid, stop_fines => undef });
1906
1907         my $mcircs = $e->search_action_circulation( 
1908                 { 
1909                         usr => $userid, 
1910                         checkin_time => undef, 
1911                         xact_finish => undef, 
1912                 });
1913
1914         
1915         push( @$circs, @$mcircs );
1916
1917         my $parser = DateTime::Format::ISO8601->new;
1918
1919         # split the circs up into overdue and not-overdue circs
1920         my (@out,@overdue);
1921         for my $c (@$circs) {
1922                 if( $c->due_date ) {
1923                         my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1924                         my $due = $due_dt->epoch;
1925                         if ($due < DateTime->today->epoch) {
1926                                 push @overdue, $c->id;
1927                         } else {
1928                                 push @out, $c->id;
1929                         }
1930                 } else {
1931                         push @out, $c->id;
1932                 }
1933         }
1934
1935         # grab all of the lost, claims-returned, and longoverdue circs
1936         #my $open = $e->search_action_circulation(
1937         #       {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1938
1939
1940         # these items have stop_fines, but no xact_finish, so money
1941         # is owed on them and they have not been checked in
1942         my $open = $e->search_action_circulation(
1943                 {
1944                         usr                             => $userid, 
1945                         stop_fines              => { in => [ qw/LOST CLAIMSRETURNED LONGOVERDUE/ ] }, 
1946                         xact_finish             => undef,
1947                         checkin_time    => undef,
1948                 }
1949         );
1950
1951
1952         my( @lost, @cr, @lo );
1953         for my $c (@$open) {
1954                 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1955                 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1956                 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1957         }
1958
1959
1960         if( $iscount ) {
1961                 return {
1962                         total           => @$circs + @lost + @cr + @lo,
1963                         out             => scalar(@out),
1964                         overdue => scalar(@overdue),
1965                         lost            => scalar(@lost),
1966                         claims_returned => scalar(@cr),
1967                         long_overdue            => scalar(@lo)
1968                 };
1969         }
1970
1971         return {
1972                 out             => \@out,
1973                 overdue => \@overdue,
1974                 lost            => \@lost,
1975                 claims_returned => \@cr,
1976                 long_overdue            => \@lo
1977         };
1978 }
1979
1980
1981
1982 __PACKAGE__->register_method(
1983         method          => "checked_in_with_fines",
1984         api_name                => "open-ils.actor.user.checked_in_with_fines",
1985     authoritative => 1,
1986         argc                    => 2,
1987         signature       => q/@see open-ils.actor.user.checked_out/
1988 );
1989 sub checked_in_with_fines {
1990         my( $self, $conn, $auth, $userid ) = @_;
1991
1992         my $e = new_editor(authtoken=>$auth);
1993         return $e->event unless $e->checkauth;
1994
1995         if( $userid ne $e->requestor->id ) {
1996                 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1997         }
1998
1999         # money is owed on these items and they are checked in
2000         my $open = $e->search_action_circulation(
2001                 {
2002                         usr                             => $userid, 
2003                         xact_finish             => undef,
2004                         checkin_time    => { "!=" => undef },
2005                 }
2006         );
2007
2008
2009         my( @lost, @cr, @lo );
2010         for my $c (@$open) {
2011                 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
2012                 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
2013                 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
2014         }
2015
2016         return {
2017                 lost            => \@lost,
2018                 claims_returned => \@cr,
2019                 long_overdue            => \@lo
2020         };
2021 }
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031 __PACKAGE__->register_method(
2032         method  => "user_transaction_history",
2033         api_name        => "open-ils.actor.user.transactions.history",
2034         argc            => 1,
2035         notes           => <<"  NOTES");
2036         Returns a list of billable transaction ids for a user, optionally by type
2037         NOTES
2038 __PACKAGE__->register_method(
2039         method  => "user_transaction_history",
2040         api_name        => "open-ils.actor.user.transactions.history.have_charge",
2041         argc            => 1,
2042         notes           => <<"  NOTES");
2043         Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
2044         NOTES
2045 __PACKAGE__->register_method(
2046         method  => "user_transaction_history",
2047         api_name        => "open-ils.actor.user.transactions.history.have_balance",
2048     authoritative => 1,
2049         argc            => 1,
2050         notes           => <<"  NOTES");
2051         Returns a list of billable transaction ids for a user that have a balance, optionally by type
2052         NOTES
2053 __PACKAGE__->register_method(
2054         method  => "user_transaction_history",
2055         api_name        => "open-ils.actor.user.transactions.history.still_open",
2056         argc            => 1,
2057         notes           => <<"  NOTES");
2058         Returns a list of billable transaction ids for a user that are not finished
2059         NOTES
2060 __PACKAGE__->register_method(
2061         method  => "user_transaction_history",
2062         api_name        => "open-ils.actor.user.transactions.history.have_bill",
2063     authoritative => 1,
2064         argc            => 1,
2065         notes           => <<"  NOTES");
2066         Returns a list of billable transaction ids for a user that has billings
2067         NOTES
2068
2069 sub user_transaction_history {
2070         my( $self, $conn, $auth, $userid, $type ) = @_;
2071
2072         # run inside of a transaction to prevent replication delays
2073         my $e = new_editor(xact=>1, authtoken=>$auth);
2074         return $e->die_event unless $e->checkauth;
2075
2076         if( $e->requestor->id ne $userid ) {
2077                 return $e->die_event 
2078                         unless $e->allowed('VIEW_USER_TRANSACTIONS');
2079         }
2080
2081         my $api = $self->api_name;
2082         my @xact_finish  = (xact_finish => undef ) if ($api =~ /history.still_open$/);
2083
2084         my @xacts = @{ $e->search_money_billable_transaction(
2085                 [       { usr => $userid, @xact_finish },
2086                         { flesh => 1,
2087                           flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
2088                           order_by => { mbt => 'xact_start DESC' },
2089                         }
2090                 ],
2091       {substream => 1}
2092         ) };
2093
2094         $e->rollback;
2095
2096         #my @mbts = _make_mbts( @xacts );
2097         my @mbts = $U->make_mbts( @xacts );
2098
2099         if(defined($type)) {
2100                 @mbts = grep { $_->xact_type eq $type } @mbts;
2101         }
2102
2103         if($api =~ /have_balance/o) {
2104                 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
2105         }
2106
2107         if($api =~ /have_charge/o) {
2108                 @mbts = grep { defined($_->last_billing_ts) } @mbts;
2109         }
2110
2111         if($api =~ /have_bill/o) {
2112                 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
2113         }
2114
2115         return [@mbts];
2116 }
2117
2118
2119
2120 __PACKAGE__->register_method(
2121         method  => "user_perms",
2122         api_name        => "open-ils.actor.permissions.user_perms.retrieve",
2123         argc            => 1,
2124         notes           => <<"  NOTES");
2125         Returns a list of permissions
2126         NOTES
2127 sub user_perms {
2128         my( $self, $client, $authtoken, $user ) = @_;
2129
2130         my( $staff, $evt ) = $apputils->checkses($authtoken);
2131         return $evt if $evt;
2132
2133         $user ||= $staff->id;
2134
2135         if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2136                 return $evt;
2137         }
2138
2139         return $apputils->simple_scalar_request(
2140                 "open-ils.storage",
2141                 "open-ils.storage.permission.user_perms.atomic",
2142                 $user);
2143 }
2144
2145 __PACKAGE__->register_method(
2146         method  => "retrieve_perms",
2147         api_name        => "open-ils.actor.permissions.retrieve",
2148         notes           => <<"  NOTES");
2149         Returns a list of permissions
2150         NOTES
2151 sub retrieve_perms {
2152         my( $self, $client ) = @_;
2153         return $apputils->simple_scalar_request(
2154                 "open-ils.cstore",
2155                 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2156                 { id => { '!=' => undef } }
2157         );
2158 }
2159
2160 __PACKAGE__->register_method(
2161         method  => "retrieve_groups",
2162         api_name        => "open-ils.actor.groups.retrieve",
2163         notes           => <<"  NOTES");
2164         Returns a list of user groupss
2165         NOTES
2166 sub retrieve_groups {
2167         my( $self, $client ) = @_;
2168         return new_editor()->retrieve_all_permission_grp_tree();
2169 }
2170
2171 __PACKAGE__->register_method(
2172         method  => "retrieve_org_address",
2173         api_name        => "open-ils.actor.org_unit.address.retrieve",
2174         notes           => <<'  NOTES');
2175         Returns an org_unit address by ID
2176         @param An org_address ID
2177         NOTES
2178 sub retrieve_org_address {
2179         my( $self, $client, $id ) = @_;
2180         return $apputils->simple_scalar_request(
2181                 "open-ils.cstore",
2182                 "open-ils.cstore.direct.actor.org_address.retrieve",
2183                 $id
2184         );
2185 }
2186
2187 __PACKAGE__->register_method(
2188         method  => "retrieve_groups_tree",
2189         api_name        => "open-ils.actor.groups.tree.retrieve",
2190         notes           => <<"  NOTES");
2191         Returns a list of user groups
2192         NOTES
2193 sub retrieve_groups_tree {
2194         my( $self, $client ) = @_;
2195         return new_editor()->search_permission_grp_tree(
2196                 [
2197                         { parent => undef},
2198                         {       
2199                                 flesh                           => -1,
2200                                 flesh_fields    => { pgt => ["children"] }, 
2201                                 order_by                        => { pgt => 'name'}
2202                         }
2203                 ]
2204         )->[0];
2205 }
2206
2207
2208 __PACKAGE__->register_method(
2209         method  => "add_user_to_groups",
2210         api_name        => "open-ils.actor.user.set_groups",
2211         notes           => <<"  NOTES");
2212         Adds a user to one or more permission groups
2213         NOTES
2214
2215 sub add_user_to_groups {
2216         my( $self, $client, $authtoken, $userid, $groups ) = @_;
2217
2218         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2219                 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2220         return $evt if $evt;
2221
2222         ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2223                 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2224         return $evt if $evt;
2225
2226         $apputils->simplereq(
2227                 'open-ils.storage',
2228                 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2229                 
2230         for my $group (@$groups) {
2231                 my $link = Fieldmapper::permission::usr_grp_map->new;
2232                 $link->grp($group);
2233                 $link->usr($userid);
2234
2235                 my $id = $apputils->simplereq(
2236                         'open-ils.storage',
2237                         'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2238         }
2239
2240         return 1;
2241 }
2242
2243 __PACKAGE__->register_method(
2244         method  => "get_user_perm_groups",
2245         api_name        => "open-ils.actor.user.get_groups",
2246         notes           => <<"  NOTES");
2247         Retrieve a user's permission groups.
2248         NOTES
2249
2250
2251 sub get_user_perm_groups {
2252         my( $self, $client, $authtoken, $userid ) = @_;
2253
2254         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2255                 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2256         return $evt if $evt;
2257
2258         return $apputils->simplereq(
2259                 'open-ils.cstore',
2260                 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2261 }       
2262
2263
2264 __PACKAGE__->register_method(
2265         method  => "get_user_work_ous",
2266         api_name        => "open-ils.actor.user.get_work_ous",
2267         notes           => <<"  NOTES");
2268         Retrieve a user's work org units.
2269         NOTES
2270 __PACKAGE__->register_method(
2271         method  => "get_user_work_ous",
2272         api_name        => "open-ils.actor.user.get_work_ous.ids",
2273         notes           => <<"  NOTES");
2274         Retrieve a user's work org units.
2275         NOTES
2276
2277
2278 sub get_user_work_ous {
2279         my( $self, $client, $auth, $userid ) = @_;
2280     my $e = new_editor(authtoken=>$auth);
2281     return $e->event unless $e->checkauth;
2282     $userid ||= $e->requestor->id;
2283
2284     if($e->requestor->id != $userid) {
2285         my $user = $e->retrieve_actor_user($userid)
2286             or return $e->event;
2287         return $e->event unless $e->allowed('ASSIGN_WORK_ORG_UNIT', $user->home_ou);
2288     }
2289
2290     return $e->search_permission_usr_work_ou_map({usr => $userid})
2291         unless $self->api_name =~ /.ids$/;
2292
2293     # client just wants a list of org IDs
2294     return $U->get_user_work_ou_ids($e, $userid);
2295 }       
2296
2297
2298
2299
2300 __PACKAGE__->register_method (
2301         method          => 'register_workstation',
2302         api_name                => 'open-ils.actor.workstation.register.override',
2303         signature       => q/@see open-ils.actor.workstation.register/);
2304
2305 __PACKAGE__->register_method (
2306         method          => 'register_workstation',
2307         api_name                => 'open-ils.actor.workstation.register',
2308         signature       => q/
2309                 Registers a new workstion in the system
2310                 @param authtoken The login session key
2311                 @param name The name of the workstation id
2312                 @param owner The org unit that owns this workstation
2313                 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2314                 if the name is already in use.
2315         /);
2316
2317 sub register_workstation {
2318         my( $self, $conn, $authtoken, $name, $owner ) = @_;
2319
2320         my $e = new_editor(authtoken=>$authtoken, xact=>1);
2321         return $e->die_event unless $e->checkauth;
2322         return $e->die_event unless $e->allowed('REGISTER_WORKSTATION', $owner);
2323         my $existing = $e->search_actor_workstation({name => $name})->[0];
2324
2325         if( $existing ) {
2326
2327                 if( $self->api_name =~ /override/o ) {
2328             # workstation with the given name exists.  
2329
2330             if($owner ne $existing->owning_lib) {
2331                 # if necessary, update the owning_lib of the workstation
2332
2333                 $logger->info("changing owning lib of workstation ".$existing->id.
2334                     " from ".$existing->owning_lib." to $owner");
2335                             return $e->die_event unless 
2336                     $e->allowed('UPDATE_WORKSTATION', $existing->owning_lib); 
2337
2338                             return $e->die_event unless $e->allowed('UPDATE_WORKSTATION', $owner); 
2339
2340                 $existing->owning_lib($owner);
2341                             return $e->die_event unless $e->update_actor_workstation($existing);
2342
2343                 $e->commit;
2344
2345             } else {
2346                 $logger->info(  
2347                     "attempt to register an existing workstation.  returning existing ID");
2348             }
2349
2350             return $existing->id;
2351
2352                 } else {
2353                         return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2354                 }
2355         }
2356
2357         my $ws = Fieldmapper::actor::workstation->new;
2358         $ws->owning_lib($owner);
2359         $ws->name($name);
2360         $e->create_actor_workstation($ws) or return $e->die_event;
2361         $e->commit;
2362         return $ws->id; # note: editor sets the id on the new object for us
2363 }
2364
2365 __PACKAGE__->register_method (
2366         method          => 'workstation_list',
2367         api_name                => 'open-ils.actor.workstation.list',
2368         signature       => q/
2369                 Returns a list of workstations registered at the given location
2370                 @param authtoken The login session key
2371                 @param ids A list of org_unit.id's for the workstation owners
2372         /);
2373
2374 sub workstation_list {
2375         my( $self, $conn, $authtoken, @orgs ) = @_;
2376
2377         my $e = new_editor(authtoken=>$authtoken);
2378         return $e->event unless $e->checkauth;
2379     my %results;
2380
2381     for my $o (@orgs) {
2382             return $e->event 
2383             unless $e->allowed('REGISTER_WORKSTATION', $o);
2384         $results{$o} = $e->search_actor_workstation({owning_lib=>$o});
2385     }
2386     return \%results;
2387 }
2388
2389
2390
2391
2392
2393
2394
2395 __PACKAGE__->register_method (
2396         method          => 'fetch_patron_note',
2397         api_name                => 'open-ils.actor.note.retrieve.all',
2398     authoritative => 1,
2399         signature       => q/
2400                 Returns a list of notes for a given user
2401                 Requestor must have VIEW_USER permission if pub==false and
2402                 @param authtoken The login session key
2403                 @param args Hash of params including
2404                         patronid : the patron's id
2405                         pub : true if retrieving only public notes
2406         /
2407 );
2408
2409 sub fetch_patron_note {
2410         my( $self, $conn, $authtoken, $args ) = @_;
2411         my $patronid = $$args{patronid};
2412
2413         my($reqr, $evt) = $U->checkses($authtoken);
2414         return $evt if $evt;
2415
2416         my $patron;
2417         ($patron, $evt) = $U->fetch_user($patronid);
2418         return $evt if $evt;
2419
2420         if($$args{pub}) {
2421                 if( $patronid ne $reqr->id ) {
2422                         $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2423                         return $evt if $evt;
2424                 }
2425                 return $U->cstorereq(
2426                         'open-ils.cstore.direct.actor.usr_note.search.atomic', 
2427                         { usr => $patronid, pub => 't' } );
2428         }
2429
2430         $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2431         return $evt if $evt;
2432
2433         return $U->cstorereq(
2434                 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2435 }
2436
2437 __PACKAGE__->register_method (
2438         method          => 'create_user_note',
2439         api_name                => 'open-ils.actor.note.create',
2440         signature       => q/
2441                 Creates a new note for the given user
2442                 @param authtoken The login session key
2443                 @param note The note object
2444         /
2445 );
2446 sub create_user_note {
2447         my( $self, $conn, $authtoken, $note ) = @_;
2448         my $e = new_editor(xact=>1, authtoken=>$authtoken);
2449         return $e->die_event unless $e->checkauth;
2450
2451         my $user = $e->retrieve_actor_user($note->usr)
2452                 or return $e->die_event;
2453
2454         return $e->die_event unless 
2455                 $e->allowed('UPDATE_USER',$user->home_ou);
2456
2457         $note->creator($e->requestor->id);
2458         $e->create_actor_usr_note($note) or return $e->die_event;
2459         $e->commit;
2460         return $note->id;
2461 }
2462
2463
2464 __PACKAGE__->register_method (
2465         method          => 'delete_user_note',
2466         api_name                => 'open-ils.actor.note.delete',
2467         signature       => q/
2468                 Deletes a note for the given user
2469                 @param authtoken The login session key
2470                 @param noteid The note id
2471         /
2472 );
2473 sub delete_user_note {
2474         my( $self, $conn, $authtoken, $noteid ) = @_;
2475
2476         my $e = new_editor(xact=>1, authtoken=>$authtoken);
2477         return $e->die_event unless $e->checkauth;
2478         my $note = $e->retrieve_actor_usr_note($noteid)
2479                 or return $e->die_event;
2480         my $user = $e->retrieve_actor_user($note->usr)
2481                 or return $e->die_event;
2482         return $e->die_event unless 
2483                 $e->allowed('UPDATE_USER', $user->home_ou);
2484         
2485         $e->delete_actor_usr_note($note) or return $e->die_event;
2486         $e->commit;
2487         return 1;
2488 }
2489
2490
2491 __PACKAGE__->register_method (
2492         method          => 'update_user_note',
2493         api_name                => 'open-ils.actor.note.update',
2494         signature       => q/
2495                 @param authtoken The login session key
2496                 @param note The note
2497         /
2498 );
2499
2500 sub update_user_note {
2501         my( $self, $conn, $auth, $note ) = @_;
2502         my $e = new_editor(authtoken=>$auth, xact=>1);
2503         return $e->event unless $e->checkauth;
2504         my $patron = $e->retrieve_actor_user($note->usr)
2505                 or return $e->event;
2506         return $e->event unless 
2507                 $e->allowed('UPDATE_USER', $patron->home_ou);
2508         $e->update_actor_user_note($note)
2509                 or return $e->event;
2510         $e->commit;
2511         return 1;
2512 }
2513
2514
2515
2516
2517 __PACKAGE__->register_method (
2518         method          => 'create_closed_date',
2519         api_name        => 'open-ils.actor.org_unit.closed_date.create',
2520         signature       => q/
2521                 Creates a new closing entry for the given org_unit
2522                 @param authtoken The login session key
2523                 @param note The closed_date object
2524         /
2525 );
2526 sub create_closed_date {
2527         my( $self, $conn, $authtoken, $cd ) = @_;
2528
2529         my( $user, $evt ) = $U->checkses($authtoken);
2530         return $evt if $evt;
2531
2532         $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2533         return $evt if $evt;
2534
2535         $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2536
2537         my $id = $U->storagereq(
2538                 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2539         return $U->DB_UPDATE_FAILED($cd) unless $id;
2540         return $id;
2541 }
2542
2543
2544 __PACKAGE__->register_method (
2545         method          => 'delete_closed_date',
2546         api_name        => 'open-ils.actor.org_unit.closed_date.delete',
2547         signature       => q/
2548                 Deletes a closing entry for the given org_unit
2549                 @param authtoken The login session key
2550                 @param noteid The close_date id
2551         /
2552 );
2553 sub delete_closed_date {
2554         my( $self, $conn, $authtoken, $cd ) = @_;
2555
2556         my( $user, $evt ) = $U->checkses($authtoken);
2557         return $evt if $evt;
2558
2559         my $cd_obj;
2560         ($cd_obj, $evt) = fetch_closed_date($cd);
2561         return $evt if $evt;
2562
2563         $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2564         return $evt if $evt;
2565
2566         $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2567
2568         my $stat = $U->storagereq(
2569                 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2570         return $U->DB_UPDATE_FAILED($cd) unless $stat;
2571         return $stat;
2572 }
2573
2574
2575 __PACKAGE__->register_method(
2576         method => 'usrname_exists',
2577         api_name        => 'open-ils.actor.username.exists',
2578         signature => q/
2579                 Returns 1 if the requested username exists, returns 0 otherwise
2580         /
2581 );
2582
2583 sub usrname_exists {
2584         my( $self, $conn, $auth, $usrname ) = @_;
2585         my $e = new_editor(authtoken=>$auth);
2586         return $e->event unless $e->checkauth;
2587         my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2588         return $$a[0] if $a and @$a;
2589         return undef;
2590 }
2591
2592 __PACKAGE__->register_method(
2593         method => 'barcode_exists',
2594         api_name        => 'open-ils.actor.barcode.exists',
2595     authoritative => 1,
2596         signature => q/
2597                 Returns 1 if the requested barcode exists, returns 0 otherwise
2598         /
2599 );
2600
2601 sub barcode_exists {
2602         my( $self, $conn, $auth, $barcode ) = @_;
2603         my $e = new_editor(authtoken=>$auth);
2604         return $e->event unless $e->checkauth;
2605         my $card = $e->search_actor_card({barcode => $barcode});
2606         if (@$card) {
2607                 return 1;
2608         } else {
2609                 return 0;
2610         }
2611         #return undef unless @$card;
2612         #return $card->[0]->usr;
2613 }
2614
2615
2616 __PACKAGE__->register_method(
2617         method => 'retrieve_net_levels',
2618         api_name        => 'open-ils.actor.net_access_level.retrieve.all',
2619 );
2620
2621 sub retrieve_net_levels {
2622         my( $self, $conn, $auth ) = @_;
2623         my $e = new_editor(authtoken=>$auth);
2624         return $e->event unless $e->checkauth;
2625         return $e->retrieve_all_config_net_access_level();
2626 }
2627
2628
2629 __PACKAGE__->register_method(
2630         method => 'fetch_org_by_shortname',
2631         api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2632 );
2633 sub fetch_org_by_shortname {
2634         my( $self, $conn, $sname ) = @_;
2635         my $e = new_editor();
2636         my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2637         return $e->event unless $org;
2638         return $org;
2639 }
2640
2641
2642 __PACKAGE__->register_method(
2643         method => 'session_home_lib',
2644         api_name => 'open-ils.actor.session.home_lib',
2645 );
2646
2647 sub session_home_lib {
2648         my( $self, $conn, $auth ) = @_;
2649         my $e = new_editor(authtoken=>$auth);
2650         return undef unless $e->checkauth;
2651         my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2652         return $org->shortname;
2653 }
2654
2655 __PACKAGE__->register_method(
2656         method => 'session_safe_token',
2657         api_name => 'open-ils.actor.session.safe_token',
2658         signature => q/
2659                 Returns a hashed session ID that is safe for export to the world.
2660                 This safe token will expire after 1 hour of non-use.
2661                 @param auth Active authentication token
2662         /
2663 );
2664
2665 sub session_safe_token {
2666         my( $self, $conn, $auth ) = @_;
2667         my $e = new_editor(authtoken=>$auth);
2668         return undef unless $e->checkauth;
2669
2670         my $safe_token = md5_hex($auth);
2671
2672         $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2673
2674         # Add more like the following if needed...
2675         $cache->put_cache(
2676                 "safe-token-home_lib-shortname-$safe_token",
2677                 $e->retrieve_actor_org_unit(
2678                         $e->requestor->home_ou
2679                 )->shortname,
2680                 60 * 60
2681         );
2682
2683         return $safe_token;
2684 }
2685
2686
2687 __PACKAGE__->register_method(
2688         method => 'safe_token_home_lib',
2689         api_name => 'open-ils.actor.safe_token.home_lib.shortname',
2690         signature => q/
2691                 Returns the home library shortname from the session
2692                 asscociated with a safe token from generated by
2693                 open-ils.actor.session.safe_token.
2694                 @param safe_token Active safe token
2695         /
2696 );
2697
2698 sub safe_token_home_lib {
2699         my( $self, $conn, $safe_token ) = @_;
2700
2701         $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2702         return $cache->get_cache( 'safe-token-home_lib-shortname-'. $safe_token );
2703 }
2704
2705
2706
2707 __PACKAGE__->register_method(
2708         method => 'slim_tree',
2709         api_name        => "open-ils.actor.org_tree.slim_hash.retrieve",
2710 );
2711 sub slim_tree {
2712         my $tree = new_editor()->search_actor_org_unit( 
2713                 [
2714                         {"parent_ou" => undef },
2715                         {
2716                                 flesh                           => -1,
2717                                 flesh_fields    => { aou =>  ['children'] },
2718                                 order_by                        => { aou => 'name'},
2719                                 select                  => { aou => ["id","shortname", "name"]},
2720                         }
2721                 ]
2722         )->[0];
2723
2724         return trim_tree($tree);
2725 }
2726
2727
2728 sub trim_tree {
2729         my $tree = shift;
2730         return undef unless $tree;
2731         my $htree = {
2732                 code => $tree->shortname,
2733                 name => $tree->name,
2734         };
2735         if( $tree->children and @{$tree->children} ) {
2736                 $htree->{children} = [];
2737                 for my $c (@{$tree->children}) {
2738                         push( @{$htree->{children}}, trim_tree($c) );
2739                 }
2740         }
2741
2742         return $htree;
2743 }
2744
2745
2746 __PACKAGE__->register_method(
2747         method  => "update_penalties",
2748         api_name        => "open-ils.actor.user.penalties.update");
2749 sub update_penalties {
2750         my( $self, $conn, $auth, $userid ) = @_;
2751         my $e = new_editor(authtoken=>$auth);
2752         return $e->event unless $e->checkauth;
2753         $U->update_patron_penalties( 
2754                 authtoken => $auth,
2755                 patronid  => $userid,
2756         );
2757         return 1;
2758 }
2759
2760
2761
2762 __PACKAGE__->register_method(
2763         method  => "user_retrieve_fleshed_by_id",
2764         api_name        => "open-ils.actor.user.fleshed.retrieve",);
2765
2766 sub user_retrieve_fleshed_by_id {
2767         my( $self, $client, $auth, $user_id, $fields ) = @_;
2768         my $e = new_editor(authtoken => $auth);
2769         return $e->event unless $e->checkauth;
2770
2771         if( $e->requestor->id != $user_id ) {
2772                 return $e->event unless $e->allowed('VIEW_USER');
2773         }
2774
2775         $fields ||= [
2776                 "cards",
2777                 "card",
2778                 "standing_penalties",
2779                 "addresses",
2780                 "billing_address",
2781                 "mailing_address",
2782                 "stat_cat_entries" ];
2783         return new_flesh_user($user_id, $fields, $e);
2784 }
2785
2786
2787 sub new_flesh_user {
2788
2789         my $id = shift;
2790         my $fields = shift || [];
2791         my $e   = shift || new_editor(xact=>1);
2792
2793         my $user = $e->retrieve_actor_user(
2794         [
2795         $id,
2796         {
2797                 "flesh"                         => 1,
2798                 "flesh_fields" =>  { "au" => $fields }
2799         }
2800         ]
2801         ) or return $e->event;
2802
2803
2804         if( grep { $_ eq 'addresses' } @$fields ) {
2805
2806                 $user->addresses([]) unless @{$user->addresses};
2807         
2808                 if( ref $user->billing_address ) {
2809                         unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2810                                 push( @{$user->addresses}, $user->billing_address );
2811                         }
2812                 }
2813         
2814                 if( ref $user->mailing_address ) {
2815                         unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2816                                 push( @{$user->addresses}, $user->mailing_address );
2817                         }
2818                 }
2819         }
2820
2821         $e->rollback;
2822         $user->clear_passwd();
2823         return $user;
2824 }
2825
2826
2827
2828
2829 __PACKAGE__->register_method(
2830         method  => "user_retrieve_parts",
2831         api_name        => "open-ils.actor.user.retrieve.parts",);
2832
2833 sub user_retrieve_parts {
2834         my( $self, $client, $auth, $user_id, $fields ) = @_;
2835         my $e = new_editor(authtoken => $auth);
2836         return $e->event unless $e->checkauth;
2837         if( $e->requestor->id != $user_id ) {
2838                 return $e->event unless $e->allowed('VIEW_USER');
2839         }
2840         my @resp;
2841         my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2842         push(@resp, $user->$_()) for(@$fields);
2843         return \@resp;
2844 }
2845
2846
2847
2848 __PACKAGE__->register_method(
2849     method => 'user_opt_in_enabled',
2850     api_name => 'open-ils.actor.user.org_unit_opt_in.enabled',
2851     signature => q/
2852         @return 1 if user opt-in is globally enabled, 0 otherwise.
2853     /);
2854
2855 sub user_opt_in_enabled {
2856     my($self, $conn) = @_;
2857     my $sc = OpenSRF::Utils::SettingsClient->new;
2858     return 1 if lc($sc->config_value(share => user => 'opt_in')) eq 'true'; 
2859     return 0;
2860 }
2861     
2862
2863 __PACKAGE__->register_method(
2864     method => 'user_opt_in_at_org',
2865     api_name => 'open-ils.actor.user.org_unit_opt_in.check',
2866     signature => q/
2867         @param $auth The auth token
2868         @param user_id The ID of the user to test
2869         @return 1 if the user has opted in at the specified org,
2870             event on error, and 0 otherwise. /);
2871 sub user_opt_in_at_org {
2872     my($self, $conn, $auth, $user_id) = @_;
2873
2874     # see if we even need to enforce the opt-in value
2875     return 1 unless user_opt_in_enabled($self);
2876
2877         my $e = new_editor(authtoken => $auth);
2878         return $e->event unless $e->checkauth;
2879     my $org_id = $e->requestor->ws_ou;
2880
2881     my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2882         return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
2883
2884     # user is automatically opted-in at the home org
2885     return 1 if $user->home_ou eq $org_id;
2886
2887     my $vals = $e->search_actor_usr_org_unit_opt_in(
2888         {org_unit=>$org_id, usr=>$user_id},{idlist=>1});
2889
2890     return 1 if @$vals;
2891     return 0;
2892 }
2893
2894 __PACKAGE__->register_method(
2895     method => 'create_user_opt_in_at_org',
2896     api_name => 'open-ils.actor.user.org_unit_opt_in.create',
2897     signature => q/
2898         @param $auth The auth token
2899         @param user_id The ID of the user to test
2900         @return The ID of the newly created object, event on error./);
2901
2902 sub create_user_opt_in_at_org {
2903     my($self, $conn, $auth, $user_id) = @_;
2904
2905         my $e = new_editor(authtoken => $auth, xact=>1);
2906         return $e->die_event unless $e->checkauth;
2907     my $org_id = $e->requestor->ws_ou;
2908
2909     my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
2910         return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2911
2912     my $opt_in = Fieldmapper::actor::usr_org_unit_opt_in->new;
2913
2914     $opt_in->org_unit($org_id);
2915     $opt_in->usr($user_id);
2916     $opt_in->staff($e->requestor->id);
2917     $opt_in->opt_in_ts('now');
2918     $opt_in->opt_in_ws($e->requestor->wsid);
2919
2920     $opt_in = $e->create_actor_usr_org_unit_opt_in($opt_in)
2921         or return $e->die_event;
2922
2923     $e->commit;
2924
2925     return $opt_in->id;
2926 }
2927
2928
2929 __PACKAGE__->register_method (
2930         method          => 'retrieve_org_hours',
2931         api_name        => 'open-ils.actor.org_unit.hours_of_operation.retrieve',
2932         signature       => q/
2933         Returns the hours of operation for a specified org unit
2934                 @param authtoken The login session key
2935                 @param org_id The org_unit ID
2936         /
2937 );
2938
2939 sub retrieve_org_hours {
2940     my($self, $conn, $auth, $org_id) = @_;
2941     my $e = new_editor(authtoken => $auth);
2942         return $e->die_event unless $e->checkauth;
2943     $org_id ||= $e->requestor->ws_ou;
2944     return $e->retrieve_actor_org_unit_hours_of_operation($org_id);
2945 }
2946
2947
2948 1;
2949