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