]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Actor.pm
af4b028064386aec23f44061f3ce3626b042f687
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Actor.pm
1 package OpenILS::Application::Actor;
2 use base qw/OpenSRF::Application/;
3 use strict; use warnings;
4 use Data::Dumper;
5 $Data::Dumper::Indent = 0;
6 use OpenILS::Event;
7
8 use Digest::MD5 qw(md5_hex);
9
10 use OpenSRF::EX qw(:try);
11 use OpenILS::EX;
12 use OpenILS::Perm;
13
14 use OpenILS::Application::AppUtils;
15
16 use OpenILS::Utils::Fieldmapper;
17 use OpenILS::Application::Search::Actor;
18 use OpenILS::Utils::ModsParser;
19 use OpenSRF::Utils::Logger qw/$logger/;
20
21 use OpenSRF::Utils::Cache;
22
23
24 use OpenILS::Application::Actor::Container;
25 sub initialize {
26         OpenILS::Application::Actor::Container->initialize();
27 }
28
29 my $apputils = "OpenILS::Application::AppUtils";
30 my $U = $apputils;
31
32 sub _d { warn "Patron:\n" . Dumper(shift()); }
33
34 my $cache_client;
35
36
37 my $set_user_settings;
38 my $set_ou_settings;
39
40 __PACKAGE__->register_method(
41         method  => "set_user_settings",
42         api_name        => "open-ils.actor.patron.settings.update",
43 );
44 sub set_user_settings {
45         my( $self, $client, $user_session, $uid, $settings ) = @_;
46         
47         $logger->debug("Setting user settings: $user_session, $uid, " . Dumper($settings));
48
49         my( $staff, $user, $evt ) = 
50                 $apputils->checkses_requestor( $user_session, $uid, 'UPDATE_USER' );    
51         return $evt if $evt;
52         
53
54         my ($params) = map { 
55                 [{ usr => $user->id, name => $_}, {value => $$settings{$_}}] } keys %$settings;
56
57         $logger->activity("User " . $staff->id . " updating user $uid settings with: " . Dumper($params));
58
59         return $apputils->simplereq(
60                 'open-ils.storage',
61                 'open-ils.storage.direct.actor.user_setting.batch.merge', $params );
62                 
63 }
64
65
66
67 __PACKAGE__->register_method(
68         method  => "set_ou_settings",
69         api_name        => "open-ils.actor.org_unit.settings.update",
70 );
71 sub set_ou_settings {
72         my( $self, $client, $user_session, $ouid, $settings ) = @_;
73         
74         my( $staff, $evt ) = $apputils->checkses( $user_session );
75         return $evt if $evt;
76         $evt = $apputils->check_perms( $staff->id, $ouid, 'UPDATE_ORG_UNIT' );
77         return $evt if $evt;
78
79
80         my ($params) = 
81                 map { [{ org_unit => $ouid, name => $_}, {value => $$settings{$_}}] } keys %$settings;
82
83         $logger->activity("Updating org unit [$ouid] settings with: " . Dumper($params));
84
85         return $apputils->simplereq(
86                 'open-ils.storage',
87                 'open-ils.storage.direct.actor.org_unit_setting.merge', @$params );
88 }
89
90
91 my $fetch_user_settings;
92 my $fetch_ou_settings;
93
94 __PACKAGE__->register_method(
95         method  => "user_settings",
96         api_name        => "open-ils.actor.patron.settings.retrieve",
97 );
98 sub user_settings {
99         my( $self, $client, $user_session, $uid ) = @_;
100         
101         my( $staff, $user, $evt ) = 
102                 $apputils->checkses_requestor( $user_session, $uid, 'VIEW_USER' );
103         return $evt if $evt;
104
105         $logger->debug("User " . $staff->id . " fetching user $uid\n");
106         my $s = $apputils->simplereq(
107                 'open-ils.storage',
108                 'open-ils.storage.direct.actor.user_setting.search.usr.atomic',$uid );
109
110         return { map { ($_->name,$_->value) } @$s };
111 }
112
113
114
115 __PACKAGE__->register_method(
116         method  => "ou_settings",
117         api_name        => "open-ils.actor.org_unit.settings.retrieve",
118 );
119 sub ou_settings {
120         my( $self, $client, $ouid ) = @_;
121         
122         my $s = $apputils->simplereq(
123                 'open-ils.storage',
124                 'open-ils.storage.direct.actor.org_unit_setting.search.org_unit.atomic', $ouid);
125
126         return { map { ($_->name,$_->value) } @$s };
127 }
128
129
130
131 __PACKAGE__->register_method(
132         method  => "update_patron",
133         api_name        => "open-ils.actor.patron.update",);
134
135 sub update_patron {
136         my( $self, $client, $user_session, $patron ) = @_;
137
138         my $session = $apputils->start_db_session();
139         my $err = undef;
140
141         $logger->info("Creating new patron...") if $patron->isnew; 
142         $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
143
144         my( $user_obj, $evt ) = $U->checkses($user_session);
145         return $evt if $evt;
146
147         # XXX does this user have permission to add/create users.  Granularity?
148         # $new_patron is the patron in progress.  $patron is the original patron
149         # passed in with the method.  new_patron will change as the components
150         # of patron are added/updated.
151
152         my $new_patron;
153
154         # unflesh the real items on the patron
155         $patron->card( $patron->card->id ) if(ref($patron->card));
156         $patron->billing_address( $patron->billing_address->id ) 
157                 if(ref($patron->billing_address));
158         $patron->mailing_address( $patron->mailing_address->id ) 
159                 if(ref($patron->mailing_address));
160
161         # create/update the patron first so we can use his id
162         if($patron->isnew()) {
163                 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
164                 return $evt if $evt;
165         } else { $new_patron = $patron; }
166
167         ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
168         return $evt if $evt;
169
170         ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
171         return $evt if $evt;
172
173         ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
174         return $evt if $evt;
175
176         # re-update the patron if anything has happened to him during this process
177         if($new_patron->ischanged()) {
178                 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
179                 return $evt if $evt;
180         }
181
182         #$session = OpenSRF::AppSession->create("open-ils.storage");  # why did i put this here?
183
184         ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
185         return $evt if $evt;
186
187         ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
188         return $evt if $evt;
189
190         ($new_patron, $evt) = _create_standing_penalties($session, $user_session, $patron, $new_patron, $user_obj);
191         return $evt if $evt;
192
193         $apputils->commit_db_session($session);
194
195         #warn "Patron Update/Create complete\n";
196         return flesh_user($new_patron->id());
197 }
198
199
200
201
202 __PACKAGE__->register_method(
203         method  => "user_retrieve_fleshed_by_id",
204         api_name        => "open-ils.actor.user.fleshed.retrieve",);
205
206 sub user_retrieve_fleshed_by_id {
207         my( $self, $client, $user_session, $user_id ) = @_;
208
209         my( $requestor, $target, $evt ) = $apputils->
210                 checkses_requestor( $user_session, $user_id, 'VIEW_USER' );
211         return $evt if $evt;
212
213         return flesh_user($user_id);
214 }
215
216
217 sub flesh_user {
218         my $id = shift;
219         my $session = shift;
220
221         my $kill = 0;
222
223         if(!$session) {
224                 $session = OpenSRF::AppSession->create("open-ils.storage");
225                 $kill = 1;
226         }
227
228         # grab the user with the given id 
229         my $ureq = $session->request(
230                         "open-ils.storage.direct.actor.user.retrieve", $id);
231         my $user = $ureq->gather(1);
232
233         if(!$user) { return undef; }
234
235         # grab the cards
236         my $cards_req = $session->request(
237                         "open-ils.storage.direct.actor.card.search.usr.atomic",
238                         $user->id() );
239         $user->cards( $cards_req->gather(1) );
240
241         for my $c(@{$user->cards}) {
242                 if($c->id == $user->card || $c->id eq $user->card ) {
243                         #warn "Setting my card to " . $c->id . "\n";
244                         $user->card($c);
245                 }
246         }
247
248         my $add_req = $session->request(
249                         "open-ils.storage.direct.actor.user_address.search.usr.atomic",
250                         $user->id() );
251         $user->addresses( $add_req->gather(1) );
252
253         for my $c(@{$user->addresses}) {
254                 if($c->id eq $user->billing_address ) { $user->billing_address($c); }
255                 if($c->id eq $user->mailing_address ) { $user->mailing_address($c); }
256         }
257
258         my $stat_req = $session->request(
259                 "open-ils.storage.direct.actor.stat_cat_entry_user_map.search.target_usr.atomic",
260                 $user->id() );
261         $user->stat_cat_entries($stat_req->gather(1));
262
263         my $standing_penalties_req = $session->request(
264                 "open-ils.storage.direct.actor.user_standing_penalty.search.usr.atomic",
265                 $user->id() );
266         $user->standing_penalties($standing_penalties_req->gather(1));
267
268         if($kill) { $session->disconnect(); }
269         $user->clear_passwd();
270
271         return $user;
272 }
273
274
275 # clone and clear stuff that would break the database
276 sub _clone_patron {
277         my $patron = shift;
278
279         my $new_patron = $patron->clone;
280
281         # Using the Fieldmapper clone method
282         #my $new_patron = Fieldmapper::actor::user->new();
283
284         #my $fmap = $Fieldmapper::fieldmap;
285         #no strict; # shallow clone, may be useful in the fieldmapper
286         #for my $field 
287         #       (keys %{$fmap->{"Fieldmapper::actor::user"}->{'fields'}}) {
288         #               $new_patron->$field( $patron->$field() );
289         #}
290         #use strict;
291
292         # clear these
293         $new_patron->clear_billing_address();
294         $new_patron->clear_mailing_address();
295         $new_patron->clear_addresses();
296         $new_patron->clear_card();
297         $new_patron->clear_cards();
298         $new_patron->clear_id();
299         $new_patron->clear_isnew();
300         $new_patron->clear_ischanged();
301         $new_patron->clear_isdeleted();
302         $new_patron->clear_stat_cat_entries();
303         $new_patron->clear_permissions();
304         $new_patron->clear_standing_penalties();
305
306         return $new_patron;
307 }
308
309
310 sub _add_patron {
311
312         my $session             = shift;
313         my $patron              = shift;
314         my $user_obj    = shift;
315
316         my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
317         return (undef, $evt) if $evt;
318
319         $logger->info("Creating new user in the DB with username: ".$patron->usrname());
320
321         my $id = $session->request(
322                 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
323         return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
324
325         $logger->info("Successfully created new user [$id] in DB");
326
327         return ( $session->request( 
328                 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
329 }
330
331
332 sub _update_patron {
333         my( $session, $patron, $user_obj) = @_;
334
335         $logger->info("Updating patron ".$patron->id." in DB");
336         my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
337         return (undef, $evt) if $evt;
338
339         my $stat = $session->request(
340                 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
341         return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
342
343         return ($patron);
344 }
345
346
347 sub _add_update_addresses {
348
349         my $session = shift;
350         my $patron = shift;
351         my $new_patron = shift;
352
353         my $evt;
354
355         my $current_id; # id of the address before creation
356
357         for my $address (@{$patron->addresses()}) {
358
359                 $address->usr($new_patron->id());
360
361                 if(ref($address) and $address->isnew()) {
362
363                         $current_id = $address->id();
364                         ($address, $evt) = _add_address($session,$address);
365                         return (undef, $evt) if $evt;
366
367                         if( $patron->billing_address() and 
368                                         $patron->billing_address() == $current_id ) {
369                                 $new_patron->billing_address($address->id());
370                                 $new_patron->ischanged(1);
371                         }
372
373                         if( $patron->mailing_address() and
374                                         $patron->mailing_address() == $current_id ) {
375                                 $new_patron->mailing_address($address->id());
376                                 $new_patron->ischanged(1);
377                         }
378
379                 } elsif( ref($address) and $address->ischanged() ) {
380
381                         $address->usr($new_patron->id());
382                         ($address, $evt) = _update_address($session, $address);
383                         return (undef, $evt) if $evt;
384
385                 } elsif( ref($address) and $address->isdeleted() ) {
386
387                         if( $address->id() == $new_patron->mailing_address() ) {
388                                 $new_patron->clear_mailing_address();
389                                 ($new_patron, $evt) = _update_patron($session, $new_patron);
390                                 return (undef, $evt) if $evt;
391                         }
392
393                         if( $address->id() == $new_patron->billing_address() ) {
394                                 $new_patron->clear_billing_address();
395                                 ($new_patron, $evt) = _update_patron($session, $new_patron);
396                                 return (undef, $evt) if $evt;
397                         }
398
399                         $evt = _delete_address($session, $address);
400                         return (undef, $evt) if $evt;
401                 }
402         }
403
404         return ( $new_patron, undef );
405 }
406
407
408 # adds an address to the db and returns the address with new id
409 sub _add_address {
410         my($session, $address) = @_;
411         $address->clear_id();
412
413         $logger->info("Creating new address at street ".$address->street1);
414
415         # put the address into the database
416         my $id = $session->request(
417                 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
418         return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
419
420         $address->id( $id );
421         return ($address, undef);
422 }
423
424
425 sub _update_address {
426         my( $session, $address ) = @_;
427         $logger->info("Updating address ".$address->id." in the DB");
428
429         my $stat = $session->request(
430                 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
431
432         return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
433         return ($address, undef);
434 }
435
436
437
438 sub _add_update_cards {
439
440         my $session = shift;
441         my $patron = shift;
442         my $new_patron = shift;
443
444         my $evt;
445
446         my $virtual_id; #id of the card before creation
447         for my $card (@{$patron->cards()}) {
448
449                 $card->usr($new_patron->id());
450
451                 if(ref($card) and $card->isnew()) {
452
453                         $virtual_id = $card->id();
454                         ( $card, $evt ) = _add_card($session,$card);
455                         return (undef, $evt) if $evt;
456
457                         #if(ref($patron->card)) { $patron->card($patron->card->id); }
458                         if($patron->card() == $virtual_id) {
459                                 $new_patron->card($card->id());
460                                 $new_patron->ischanged(1);
461                         }
462
463                 } elsif( ref($card) and $card->ischanged() ) {
464                         $card->usr($new_patron->id());
465                         $evt = _update_card($session, $card);
466                         return (undef, $evt) if $evt;
467                 }
468         }
469
470         return ( $new_patron, undef );
471 }
472
473
474 # adds an card to the db and returns the card with new id
475 sub _add_card {
476         my( $session, $card ) = @_;
477         $card->clear_id();
478         $logger->info("Adding new patron card ".$card->barcode);
479
480         my $id = $session->request(
481                 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
482         return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
483         $logger->info("Successfully created patron card $id");
484
485         $card->id($id);
486         return ( $card, undef );
487 }
488
489
490 # returns event on error.  returns undef otherwise
491 sub _update_card {
492         my( $session, $card ) = @_;
493         $logger->info("Updating patron card ".$card->id);
494
495         my $stat = $session->request(
496                 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
497         return $U->DB_UPDATE_FAILED($card) unless defined($stat);
498         return undef;
499 }
500
501
502
503
504 # returns event on error.  returns undef otherwise
505 sub _delete_address {
506         my( $session, $address ) = @_;
507         $logger->info("Deleting address ".$address->id." from DB");
508
509         my $stat = $session->request(
510                 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
511
512         return $U->DB_UPDATE_FAILED($address) unless defined($stat);
513         return undef;
514 }
515
516
517
518 sub _add_survey_responses {
519         my ($session, $patron, $new_patron) = @_;
520
521         $logger->info( "Updating survey responses for patron ".$new_patron->id );
522
523         my $responses = $patron->survey_responses;
524
525         if($responses) {
526
527                 $_->usr($new_patron->id) for (@$responses);
528
529                 my $evt = $U->simplereq( "open-ils.circ", 
530                         "open-ils.circ.survey.submit.user_id", $responses );
531
532                 return (undef, $evt) if defined($U->event_code($evt));
533
534         }
535
536         return ( $new_patron, undef );
537 }
538
539
540 sub _create_stat_maps {
541
542         my($session, $user_session, $patron, $new_patron) = @_;
543
544         my $maps = $patron->stat_cat_entries();
545
546         for my $map (@$maps) {
547
548                 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
549
550                 if ($map->isdeleted()) {
551                         $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
552
553                 } elsif ($map->isnew()) {
554                         $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
555                         $map->clear_id;
556                 }
557
558
559                 $map->target_usr($new_patron->id);
560
561                 #warn "
562                 $logger->info("Updating stat entry with method $method and map $map");
563
564                 my $stat = $session->request($method, $map)->gather(1);
565                 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
566
567         }
568
569         return ($new_patron, undef);
570 }
571
572 sub _create_perm_maps {
573
574         my($session, $user_session, $patron, $new_patron) = @_;
575
576         my $maps = $patron->permissions;
577
578         for my $map (@$maps) {
579
580                 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
581                 if ($map->isdeleted()) {
582                         $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
583                 } elsif ($map->isnew()) {
584                         $method = "open-ils.storage.direct.permission.usr_perm_map.create";
585                         $map->clear_id;
586                 }
587
588
589                 $map->usr($new_patron->id);
590
591                 #warn( "Updating permissions with method $method and session $user_session and map $map" );
592                 $logger->info( "Updating permissions with method $method and map $map" );
593
594                 my $stat = $session->request($method, $map)->gather(1);
595                 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
596
597         }
598
599         return ($new_patron, undef);
600 }
601
602
603 sub _create_standing_penalties {
604
605         my($session, $user_session, $patron, $new_patron) = @_;
606
607         my $maps = $patron->standing_penalties;
608         my $method;
609
610         for my $map (@$maps) {
611
612                 if ($map->isdeleted()) {
613                         $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
614                 } elsif ($map->isnew()) {
615                         $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
616                         $map->clear_id;
617                 } else {
618                         next;
619                 }
620
621                 $map->usr($new_patron->id);
622
623                 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
624
625                 my $stat = $session->request($method, $map)->gather(1);
626                 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
627         }
628
629         return ($new_patron, undef);
630 }
631
632
633
634 __PACKAGE__->register_method(
635         method  => "search_username",
636         api_name        => "open-ils.actor.user.search.username",
637 );
638
639 sub search_username {
640         my($self, $client, $username) = @_;
641         my $users = OpenILS::Application::AppUtils->simple_scalar_request(
642                         "open-ils.storage", 
643                         "open-ils.storage.direct.actor.user.search.usrname.atomic",
644                         $username );
645         return $users;
646 }
647
648
649
650
651 __PACKAGE__->register_method(
652         method  => "user_retrieve_by_barcode",
653         api_name        => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
654
655 sub user_retrieve_by_barcode {
656         my($self, $client, $user_session, $barcode) = @_;
657
658         $logger->debug("Searching for user with barcode $barcode");
659         my ($user_obj, $evt) = $apputils->checkses($user_session);
660         return $evt if $evt;
661
662         my $session = OpenSRF::AppSession->create("open-ils.storage");
663
664         # find the card with the given barcode
665         my $creq        = $session->request(
666                         "open-ils.storage.direct.actor.card.search.barcode.atomic",
667                         $barcode );
668         my $card = $creq->gather(1);
669
670         if(!$card || !$card->[0]) {
671                 $session->disconnect();
672                 return OpenILS::Event->new( 'USER_NOT_FOUND' );
673         }
674
675         $card = $card->[0];
676         my $user = flesh_user($card->usr(), $session);
677         $session->disconnect();
678         if(!$user) { return OpenILS::Event->new( 'USER_NOT_FOUND' ); }
679         return $user;
680
681 }
682
683
684
685 __PACKAGE__->register_method(
686         method  => "get_user_by_id",
687         api_name        => "open-ils.actor.user.retrieve",);
688
689 sub get_user_by_id {
690         my ($self, $client, $user_session, $id) = @_;
691
692         my $user_obj = $apputils->check_user_session( $user_session ); 
693
694         return $apputils->simple_scalar_request(
695                 "open-ils.storage",
696                 "open-ils.storage.direct.actor.user.retrieve",
697                 $id );
698 }
699
700
701
702 __PACKAGE__->register_method(
703         method  => "get_org_types",
704         api_name        => "open-ils.actor.org_types.retrieve",);
705
706 my $org_types;
707 sub get_org_types {
708         my($self, $client) = @_;
709
710         return $org_types if $org_types;
711          return $org_types = 
712                  $apputils->simple_scalar_request(
713                         "open-ils.storage",
714                         "open-ils.storage.direct.actor.org_unit_type.retrieve.all.atomic" );
715 }
716
717
718
719 __PACKAGE__->register_method(
720         method  => "get_user_profiles",
721         api_name        => "open-ils.actor.user.profiles.retrieve",
722 );
723
724 my $user_profiles;
725 sub get_user_profiles {
726         return $user_profiles if $user_profiles;
727
728         return $user_profiles = 
729                 $apputils->simple_scalar_request(
730                         "open-ils.storage",
731                         "open-ils.storage.direct.actor.profile.retrieve.all.atomic");
732 }
733
734
735
736 __PACKAGE__->register_method(
737         method  => "get_user_ident_types",
738         api_name        => "open-ils.actor.user.ident_types.retrieve",
739 );
740 my $ident_types;
741 sub get_user_ident_types {
742         return $ident_types if $ident_types;
743         return $ident_types = 
744                 $apputils->simple_scalar_request(
745                 "open-ils.storage",
746                 "open-ils.storage.direct.config.identification_type.retrieve.all.atomic" );
747 }
748
749
750
751
752 __PACKAGE__->register_method(
753         method  => "get_org_unit",
754         api_name        => "open-ils.actor.org_unit.retrieve",
755 );
756
757 sub get_org_unit {
758
759         my( $self, $client, $user_session, $org_id ) = @_;
760
761         if(defined($user_session) && !defined($org_id)) {
762                 my $user_obj = 
763                         OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
764                 if(!defined($org_id)) {
765                         $org_id = $user_obj->home_ou;
766                 }
767         }
768
769
770         my $home_ou = OpenILS::Application::AppUtils->simple_scalar_request(
771                 "open-ils.storage",
772                 "open-ils.storage.direct.actor.org_unit.retrieve", 
773                 $org_id );
774
775         return $home_ou;
776 }
777
778
779 # build the org tree
780
781 __PACKAGE__->register_method(
782         method  => "get_org_tree",
783         api_name        => "open-ils.actor.org_tree.retrieve",
784         argc            => 0, 
785         note            => "Returns the entire org tree structure",
786 );
787
788 sub get_org_tree {
789         my( $self, $client) = @_;
790
791         if(!$cache_client) {
792                 $cache_client = OpenSRF::Utils::Cache->new("global", 0);
793         }
794         # see if it's in the cache
795         #warn "Getting ORG Tree\n";
796         my $tree = $cache_client->get_cache('orgtree');
797         if($tree) { 
798                 #warn "Found orgtree in cache. returning...\n";
799                 return $tree; 
800         }
801
802         my $orglist = $apputils->simple_scalar_request( 
803                 "open-ils.storage", 
804                 "open-ils.storage.direct.actor.org_unit.retrieve.all.atomic" );
805
806         #if($orglist) {
807                 #warn "found org list\n";
808         #}
809
810         $tree = $self->build_org_tree($orglist);
811         $cache_client->put_cache('orgtree', $tree);
812
813         return $tree;
814
815 }
816
817 # turns an org list into an org tree
818 sub build_org_tree {
819
820         my( $self, $orglist) = @_;
821
822         return $orglist unless ( 
823                         ref($orglist) and @$orglist > 1 );
824
825         my @list = sort { 
826                 $a->ou_type <=> $b->ou_type ||
827                 $a->name cmp $b->name } @$orglist;
828
829         for my $org (@list) {
830
831                 next unless ($org and defined($org->parent_ou));
832                 my ($parent) = grep { $_->id == $org->parent_ou } @list;
833                 next unless $parent;
834
835                 $parent->children([]) unless defined($parent->children); 
836                 push( @{$parent->children}, $org );
837         }
838
839         return $list[0];
840
841 }
842
843
844 __PACKAGE__->register_method(
845         method  => "get_org_descendants",
846         api_name        => "open-ils.actor.org_tree.descendants.retrieve"
847 );
848
849 # depth is optional.  org_unit is the id
850 sub get_org_descendants {
851         my( $self, $client, $org_unit, $depth ) = @_;
852         my $orglist = $apputils->simple_scalar_request(
853                         "open-ils.storage", 
854                         "open-ils.storage.actor.org_unit.descendants.atomic",
855                         $org_unit, $depth );
856         return $self->build_org_tree($orglist);
857 }
858
859
860 __PACKAGE__->register_method(
861         method  => "get_org_ancestors",
862         api_name        => "open-ils.actor.org_tree.ancestors.retrieve"
863 );
864
865 # depth is optional.  org_unit is the id
866 sub get_org_ancestors {
867         my( $self, $client, $org_unit, $depth ) = @_;
868         my $orglist = $apputils->simple_scalar_request(
869                         "open-ils.storage", 
870                         "open-ils.storage.actor.org_unit.ancestors.atomic",
871                         $org_unit, $depth );
872         return $self->build_org_tree($orglist);
873 }
874
875
876 __PACKAGE__->register_method(
877         method  => "get_standings",
878         api_name        => "open-ils.actor.standings.retrieve"
879 );
880
881 my $user_standings;
882 sub get_standings {
883         return $user_standings if $user_standings;
884         return $user_standings = 
885                 $apputils->simple_scalar_request(
886                         "open-ils.storage",
887                         "open-ils.storage.direct.config.standing.retrieve.all.atomic" );
888 }
889
890
891
892 __PACKAGE__->register_method(
893         method  => "get_my_org_path",
894         api_name        => "open-ils.actor.org_unit.full_path.retrieve"
895 );
896
897 sub get_my_org_path {
898         my( $self, $client, $user_session, $org_id ) = @_;
899         my $user_obj = $apputils->check_user_session($user_session); 
900         if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
901
902         return $apputils->simple_scalar_request(
903                 "open-ils.storage",
904                 "open-ils.storage.actor.org_unit.full_path.atomic",
905                 $org_id );
906 }
907
908
909 __PACKAGE__->register_method(
910         method  => "patron_adv_search",
911         api_name        => "open-ils.actor.patron.search.advanced" );
912
913 sub patron_adv_search {
914         my( $self, $client, $staff_login, $search_hash ) = @_;
915
916         #warn "patron adv with $staff_login and search " . 
917                 #Dumper($search_hash) . "\n";
918
919         my $session = OpenSRF::AppSession->create("open-ils.storage");
920         my $req = $session->request(
921                 "open-ils.storage.actor.user.crazy_search", $search_hash);
922
923         my $ans = $req->gather(1);
924
925         my %hash = map { ($_ =>1) } @$ans;
926         $ans = [ keys %hash ];
927
928         #warn "Returning @$ans\n";
929
930         $session->disconnect();
931         return $ans;
932
933 }
934
935
936
937 sub _verify_password {
938         my($user_session, $password) = @_;
939         my $user_obj = $apputils->check_user_session($user_session); 
940
941         #grab the user with password
942         $user_obj = $apputils->simple_scalar_request(
943                 "open-ils.storage", 
944                 "open-ils.storage.direct.actor.user.retrieve",
945                 $user_obj->id );
946
947         if($user_obj->passwd eq $password) {
948                 return 1;
949         }
950
951         return 0;
952 }
953
954
955 __PACKAGE__->register_method(
956         method  => "update_password",
957         api_name        => "open-ils.actor.user.password.update");
958
959 __PACKAGE__->register_method(
960         method  => "update_password",
961         api_name        => "open-ils.actor.user.username.update");
962
963 __PACKAGE__->register_method(
964         method  => "update_password",
965         api_name        => "open-ils.actor.user.email.update");
966
967 sub update_password {
968         my( $self, $client, $user_session, $new_value, $current_password ) = @_;
969
970         my $evt;
971
972         #warn "Updating user with method " .$self->api_name . "\n";
973         my $user_obj = $apputils->check_user_session($user_session); 
974
975         if($self->api_name =~ /password/o) {
976
977                 #make sure they know the current password
978                 if(!_verify_password($user_session, md5_hex($current_password))) {
979                         return OpenILS::EX->new("USER_WRONG_PASSWORD")->ex;
980                 }
981
982                 $user_obj->passwd($new_value);
983         } 
984         elsif($self->api_name =~ /username/o) {
985                 my $users = search_username(undef, undef, $new_value); 
986                 if( $users and $users->[0] ) {
987                         return OpenILS::Event->new('USERNAME_EXISTS');
988                 }
989                 $user_obj->usrname($new_value);
990         }
991
992         elsif($self->api_name =~ /email/o) {
993                 #warn "Updating email to $new_value\n";
994                 $user_obj->email($new_value);
995         }
996
997         my $session = $apputils->start_db_session();
998
999         ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj);
1000         return $evt if $evt;
1001
1002         $apputils->commit_db_session($session);
1003
1004         if($user_obj) { return 1; }
1005         return undef;
1006 }
1007
1008
1009 __PACKAGE__->register_method(
1010         method  => "check_user_perms",
1011         api_name        => "open-ils.actor.user.perm.check",
1012         notes           => <<"  NOTES");
1013         Takes a login session, user id, an org id, and an array of perm type strings.  For each
1014         perm type, if the user does *not* have the given permission it is added
1015         to a list which is returned from the method.  If all permissions
1016         are allowed, an empty list is returned
1017         if the logged in user does not match 'user_id', then the logged in user must
1018         have VIEW_PERMISSION priveleges.
1019         NOTES
1020
1021 sub check_user_perms {
1022         my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1023
1024         my( $staff, $evt ) = $apputils->checkses($login_session);
1025         return $evt if $evt;
1026
1027         if($staff->id ne $user_id) {
1028                 if( my $evt = $apputils->check_perms(
1029                         $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1030                         return $evt;
1031                 }
1032         }
1033
1034         my @not_allowed;
1035         for my $perm (@$perm_types) {
1036                 if($apputils->check_perms($user_id, $org_id, $perm)) {
1037                         push @not_allowed, $perm;
1038                 }
1039         }
1040
1041         return \@not_allowed
1042 }
1043
1044 __PACKAGE__->register_method(
1045         method  => "check_user_perms2",
1046         api_name        => "open-ils.actor.user.perm.check.multi_org",
1047         notes           => q/
1048                 Checks the permissions on a list of perms and orgs for a user
1049                 @param authtoken The login session key
1050                 @param user_id The id of the user to check
1051                 @param orgs The array of org ids
1052                 @param perms The array of permission names
1053                 @return An array of  [ orgId, permissionName ] arrays that FAILED the check
1054                 if the logged in user does not match 'user_id', then the logged in user must
1055                 have VIEW_PERMISSION priveleges.
1056         /);
1057
1058 sub check_user_perms2 {
1059         my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1060
1061         my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1062                 $authtoken, $user_id, 'VIEW_PERMISSION' );
1063         return $evt if $evt;
1064
1065         my @not_allowed;
1066         for my $org (@$orgs) {
1067                 for my $perm (@$perms) {
1068                         if($apputils->check_perms($user_id, $org, $perm)) {
1069                                 push @not_allowed, [ $org, $perm ];
1070                         }
1071                 }
1072         }
1073
1074         return \@not_allowed
1075 }
1076
1077
1078 __PACKAGE__->register_method(
1079         method => 'check_user_perms3',
1080         api_name        => 'open-ils.actor.user.perm.highest_org',
1081         notes           => q/
1082                 Returns the highest org unit id at which a user has a given permission
1083                 If the requestor does not match the target user, the requestor must have
1084                 'VIEW_PERMISSION' rights at the home org unit of the target user
1085                 @param authtoken The login session key
1086                 @param userid The id of the user in question
1087                 @param perm The permission to check
1088                 @return The org unit highest in the org tree within which the user has
1089                 the requested permission
1090         /);
1091
1092 sub check_user_perms3 {
1093         my( $self, $client, $authtoken, $userid, $perm ) = @_;
1094
1095         my( $staff, $target, $org, $evt );
1096
1097         ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1098                 $authtoken, $userid, 'VIEW_PERMISSION' );
1099         return $evt if $evt;
1100
1101         my $tree = $self->get_org_tree();
1102         return _find_highest_perm_org( $perm, $userid, $target->home_ou, $tree );
1103 }
1104
1105
1106 sub _find_highest_perm_org {
1107         my ( $perm, $userid, $start_org, $org_tree ) = @_;
1108         my $org = $apputils->find_org($org_tree, $start_org );
1109
1110         my $lastid = undef;
1111         while( $org ) {
1112                 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1113                 $lastid = $org->id;
1114                 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1115         }
1116
1117         return $lastid;
1118 }
1119
1120 __PACKAGE__->register_method(
1121         method => 'check_user_perms4',
1122         api_name        => 'open-ils.actor.user.perm.highest_org.batch',
1123         notes           => q/
1124                 Returns the highest org unit id at which a user has a given permission
1125                 If the requestor does not match the target user, the requestor must have
1126                 'VIEW_PERMISSION' rights at the home org unit of the target user
1127                 @param authtoken The login session key
1128                 @param userid The id of the user in question
1129                 @param perms An array of perm names to check 
1130                 @return An array of orgId's  representing the org unit 
1131                 highest in the org tree within which the user has the requested permission
1132                 The arrah of orgId's has matches the order of the perms array
1133         /);
1134
1135 sub check_user_perms4 {
1136         my( $self, $client, $authtoken, $userid, $perms ) = @_;
1137         
1138         my( $staff, $target, $org, $evt );
1139
1140         ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1141                 $authtoken, $userid, 'VIEW_PERMISSION' );
1142         return $evt if $evt;
1143
1144         my @arr;
1145         return [] unless ref($perms);
1146         my $tree = $self->get_org_tree();
1147
1148         for my $p (@$perms) {
1149                 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1150         }
1151         return \@arr;
1152 }
1153
1154
1155
1156
1157 __PACKAGE__->register_method(
1158         method  => "user_fines_summary",
1159         api_name        => "open-ils.actor.user.fines.summary",
1160         notes           => <<"  NOTES");
1161         Returns a short summary of the users total open fines, excluding voided fines
1162         Params are login_session, user_id
1163         Returns a 'mous' object.
1164         NOTES
1165
1166 sub user_fines_summary {
1167         my( $self, $client, $login_session, $user_id ) = @_;
1168
1169         my $user_obj = $apputils->check_user_session($login_session); 
1170         if($user_obj->id ne $user_id) {
1171                 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1172                         return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY"); 
1173                 }
1174         }
1175
1176         return $apputils->simple_scalar_request( 
1177                 "open-ils.storage",
1178                 "open-ils.storage.direct.money.open_user_summary.search.usr",
1179                 $user_id );
1180
1181 }
1182
1183
1184
1185
1186 __PACKAGE__->register_method(
1187         method  => "user_transactions",
1188         api_name        => "open-ils.actor.user.transactions",
1189         notes           => <<"  NOTES");
1190         Returns a list of open user transactions (mbts objects);
1191         Params are login_session, user_id
1192         Optional third parameter is the transactions type.  defaults to all
1193         NOTES
1194
1195 __PACKAGE__->register_method(
1196         method  => "user_transactions",
1197         api_name        => "open-ils.actor.user.transactions.have_charge",
1198         notes           => <<"  NOTES");
1199         Returns a list of all open user transactions (mbts objects) that have an initial charge
1200         Params are login_session, user_id
1201         Optional third parameter is the transactions type.  defaults to all
1202         NOTES
1203
1204 __PACKAGE__->register_method(
1205         method  => "user_transactions",
1206         api_name        => "open-ils.actor.user.transactions.have_balance",
1207         notes           => <<"  NOTES");
1208         Returns a list of all open user transactions (mbts objects) that have a balance
1209         Params are login_session, user_id
1210         Optional third parameter is the transactions type.  defaults to all
1211         NOTES
1212
1213 __PACKAGE__->register_method(
1214         method  => "user_transactions",
1215         api_name        => "open-ils.actor.user.transactions.fleshed",
1216         notes           => <<"  NOTES");
1217         Returns an object/hash of transaction, circ, title where transaction = an open 
1218         user transactions (mbts objects), circ is the attached circluation, and title
1219         is the title the circ points to
1220         Params are login_session, user_id
1221         Optional third parameter is the transactions type.  defaults to all
1222         NOTES
1223
1224 __PACKAGE__->register_method(
1225         method  => "user_transactions",
1226         api_name        => "open-ils.actor.user.transactions.have_charge.fleshed",
1227         notes           => <<"  NOTES");
1228         Returns an object/hash of transaction, circ, title where transaction = an open 
1229         user transactions that has an initial charge (mbts objects), circ is the 
1230         attached circluation, and title is the title the circ points to
1231         Params are login_session, user_id
1232         Optional third parameter is the transactions type.  defaults to all
1233         NOTES
1234
1235 __PACKAGE__->register_method(
1236         method  => "user_transactions",
1237         api_name        => "open-ils.actor.user.transactions.have_balance.fleshed",
1238         notes           => <<"  NOTES");
1239         Returns an object/hash of transaction, circ, title where transaction = an open 
1240         user transaction that has a balance (mbts objects), circ is the attached 
1241         circluation, and title is the title the circ points to
1242         Params are login_session, user_id
1243         Optional third parameter is the transaction type.  defaults to all
1244         NOTES
1245
1246 __PACKAGE__->register_method(
1247         method  => "user_transactions",
1248         api_name        => "open-ils.actor.user.transactions.count",
1249         notes           => <<"  NOTES");
1250         Returns an object/hash of transaction, circ, title where transaction = an open 
1251         user transactions (mbts objects), circ is the attached circluation, and title
1252         is the title the circ points to
1253         Params are login_session, user_id
1254         Optional third parameter is the transactions type.  defaults to all
1255         NOTES
1256
1257 __PACKAGE__->register_method(
1258         method  => "user_transactions",
1259         api_name        => "open-ils.actor.user.transactions.have_charge.count",
1260         notes           => <<"  NOTES");
1261         Returns an object/hash of transaction, circ, title where transaction = an open 
1262         user transactions that has an initial charge (mbts objects), circ is the 
1263         attached circluation, and title is the title the circ points to
1264         Params are login_session, user_id
1265         Optional third parameter is the transactions type.  defaults to all
1266         NOTES
1267
1268 __PACKAGE__->register_method(
1269         method  => "user_transactions",
1270         api_name        => "open-ils.actor.user.transactions.have_balance.count",
1271         notes           => <<"  NOTES");
1272         Returns an object/hash of transaction, circ, title where transaction = an open 
1273         user transaction that has a balance (mbts objects), circ is the attached 
1274         circluation, and title is the title the circ points to
1275         Params are login_session, user_id
1276         Optional third parameter is the transaction type.  defaults to all
1277         NOTES
1278
1279 __PACKAGE__->register_method(
1280         method  => "user_transactions",
1281         api_name        => "open-ils.actor.user.transactions.have_balance.total",
1282         notes           => <<"  NOTES");
1283         Returns an object/hash of transaction, circ, title where transaction = an open 
1284         user transaction that has a balance (mbts objects), circ is the attached 
1285         circluation, and title is the title the circ points to
1286         Params are login_session, user_id
1287         Optional third parameter is the transaction type.  defaults to all
1288         NOTES
1289
1290
1291
1292 sub user_transactions {
1293         my( $self, $client, $login_session, $user_id, $type ) = @_;
1294
1295         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1296                 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1297         return $evt if $evt;
1298         
1299         my $api = $self->api_name();
1300         my $trans;
1301         my @xact;
1302
1303         if(defined($type)) { @xact = (xact_type =>  $type); 
1304
1305         } else { @xact = (); }
1306
1307         if($api =~ /have_charge/o) {
1308
1309                 $trans = $apputils->simple_scalar_request( 
1310                         "open-ils.storage",
1311                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1312                         { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1313
1314         } elsif($api =~ /have_balance/o) {
1315
1316                 $trans =  $apputils->simple_scalar_request( 
1317                         "open-ils.storage",
1318                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1319                         { usr => $user_id, balance_owed => { ">" => 0 }, @xact });
1320
1321         } else {
1322
1323                 $trans =  $apputils->simple_scalar_request( 
1324                         "open-ils.storage",
1325                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1326                         { usr => $user_id, @xact });
1327         }
1328
1329         if($api =~ /total/o) { 
1330                 my $total = 0.0;
1331                 for my $t (@$trans) {
1332                         $total += $t->balance_owed;
1333                 }
1334
1335                 $logger->debug("Total balance owed by user $user_id: $total");
1336                 return $total;
1337         }
1338
1339         if($api =~ /count/o) { return scalar @$trans; }
1340         if($api !~ /fleshed/o) { return $trans; }
1341
1342         #warn "API: $api\n";
1343
1344         my @resp;
1345         for my $t (@$trans) {
1346                         
1347                 #warn $t->id . "\n";
1348
1349
1350                 if( $t->xact_type ne 'circulation' ) {
1351                         push @resp, {transaction => $t};
1352                         next;
1353                 }
1354
1355                 my $circ = $apputils->simple_scalar_request(
1356                                 "open-ils.storage",
1357                                 "open-ils.storage.direct.action.circulation.retrieve",
1358                                 $t->id );
1359
1360                 next unless $circ;
1361
1362                 my $title = $apputils->simple_scalar_request(
1363                         "open-ils.storage", 
1364                         "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1365                         $circ->target_copy );
1366
1367                 next unless $title;
1368
1369                 my $u = OpenILS::Utils::ModsParser->new();
1370                 $u->start_mods_batch($title->marc());
1371                 my $mods = $u->finish_mods_batch();
1372
1373                 push @resp, {transaction => $t, circ => $circ, record => $mods };
1374
1375         }
1376
1377         return \@resp; 
1378
1379
1380
1381 __PACKAGE__->register_method(
1382         method  => "user_transaction_retrieve",
1383         api_name        => "open-ils.actor.user.transaction.fleshed.retrieve",
1384         argc            => 1,
1385         notes           => <<"  NOTES");
1386         Returns a fleshedtransaction record
1387         NOTES
1388 __PACKAGE__->register_method(
1389         method  => "user_transaction_retrieve",
1390         api_name        => "open-ils.actor.user.transaction.retrieve",
1391         argc            => 1,
1392         notes           => <<"  NOTES");
1393         Returns a transaction record
1394         NOTES
1395 sub user_transaction_retrieve {
1396         my( $self, $client, $login_session, $bill_id ) = @_;
1397
1398         my $trans = $apputils->simple_scalar_request( 
1399                 "open-ils.storage",
1400                 "open-ils.storage.direct.money.billable_transaction_summary.retrieve",
1401                 $bill_id
1402         );
1403
1404         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1405                 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1406         return $evt if $evt;
1407         
1408         my $api = $self->api_name();
1409         if($api !~ /fleshed/o) { return $trans; }
1410
1411         if( $trans->xact_type ne 'circulation' ) {
1412                 $logger->debug("Returning non-circ transaction");
1413                 return {transaction => $trans};
1414         }
1415
1416         my $circ = $apputils->simple_scalar_request(
1417                         "open-ils.storage",
1418                         "open-ils.storage.direct.action.circulation.retrieve",
1419                         $trans->id );
1420
1421         return {transaction => $trans} unless $circ;
1422         $logger->debug("Found the circ transaction");
1423
1424         my $title = $apputils->simple_scalar_request(
1425                 "open-ils.storage", 
1426                 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1427                 $circ->target_copy );
1428
1429         return {transaction => $trans, circ => $circ } unless $title;
1430         $logger->debug("Found the circ title");
1431
1432         my $mods;
1433         try {
1434                 my $u = OpenILS::Utils::ModsParser->new();
1435                 $u->start_mods_batch($title->marc());
1436                 $mods = $u->finish_mods_batch();
1437         } otherwise {
1438                 if ($title->id == -1) {
1439                         my $copy = $apputils->simple_scalar_request(
1440                                 "open-ils.storage",
1441                                 "open-ils.storage.direct.asset.copy.retrieve",
1442                                 $circ->target_copy );
1443
1444                         $mods = new Fieldmapper::metabib::virtual_record;
1445                         $mods->doc_id(-1);
1446                         $mods->title($copy->dummy_title);
1447                         $mods->author($copy->dummy_author);
1448                 }
1449         };
1450
1451         $logger->debug("MODSized the circ title");
1452
1453         return {transaction => $trans, circ => $circ, record => $mods };
1454 }
1455
1456 __PACKAGE__->register_method(
1457         method  => "user_transaction_history",
1458         api_name        => "open-ils.actor.user.transactions.history",
1459         argc            => 1,
1460         notes           => <<"  NOTES");
1461         Returns a list of billable transaction ids for a user, optionally by type
1462         NOTES
1463 sub user_transaction_history {
1464         my( $self, $client, $login_session, $user_id, $type ) = @_;
1465
1466         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1467                 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1468         return $evt if $evt;
1469         
1470         my $api = $self->api_name();
1471         my @xact;
1472
1473         @xact = (xact_type =>  $type) if(defined($type));
1474
1475         my $trans = $apputils->simple_scalar_request( 
1476                 "open-ils.storage",
1477                 "open-ils.storage.direct.money.billable_transaction_summary.search_where.atomic",
1478                 { usr => $user_id, @xact }, { order_by => 'xact_start DESC' });
1479
1480         return [ map { $_->id } @$trans ];
1481 }
1482
1483
1484 __PACKAGE__->register_method(
1485         method  => "user_perms",
1486         api_name        => "open-ils.actor.permissions.user_perms.retrieve",
1487         argc            => 1,
1488         notes           => <<"  NOTES");
1489         Returns a list of permissions
1490         NOTES
1491 sub user_perms {
1492         my( $self, $client, $authtoken, $user ) = @_;
1493
1494         my( $staff, $evt ) = $apputils->checkses($authtoken);
1495         return $evt if $evt;
1496
1497         $user ||= $staff->id;
1498
1499         if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1500                 return $evt;
1501         }
1502
1503         return $apputils->simple_scalar_request(
1504                 "open-ils.storage",
1505                 "open-ils.storage.permission.user_perms.atomic",
1506                 $user);
1507 }
1508
1509 __PACKAGE__->register_method(
1510         method  => "retrieve_perms",
1511         api_name        => "open-ils.actor.permissions.retrieve",
1512         notes           => <<"  NOTES");
1513         Returns a list of permissions
1514         NOTES
1515 sub retrieve_perms {
1516         my( $self, $client ) = @_;
1517         return $apputils->simple_scalar_request(
1518                 "open-ils.storage",
1519                 "open-ils.storage.direct.permission.perm_list.retrieve.all.atomic");
1520 }
1521
1522 __PACKAGE__->register_method(
1523         method  => "retrieve_groups",
1524         api_name        => "open-ils.actor.groups.retrieve",
1525         notes           => <<"  NOTES");
1526         Returns a list of user groupss
1527         NOTES
1528 sub retrieve_groups {
1529         my( $self, $client ) = @_;
1530         return $apputils->simple_scalar_request(
1531                 "open-ils.storage",
1532                 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1533 }
1534
1535 __PACKAGE__->register_method(
1536         method  => "retrieve_groups_tree",
1537         api_name        => "open-ils.actor.groups.tree.retrieve",
1538         notes           => <<"  NOTES");
1539         Returns a list of user groups
1540         NOTES
1541 sub retrieve_groups_tree {
1542         my( $self, $client ) = @_;
1543         my $groups = $apputils->simple_scalar_request(
1544                 "open-ils.storage",
1545                 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1546         return $self->build_group_tree($groups);        
1547 }
1548
1549
1550 # turns an org list into an org tree
1551 sub build_group_tree {
1552
1553         my( $self, $grplist) = @_;
1554
1555         return $grplist unless ( 
1556                         ref($grplist) and @$grplist > 1 );
1557
1558         my @list = sort { $a->name cmp $b->name } @$grplist;
1559
1560         my $root;
1561         for my $grp (@list) {
1562
1563                 if ($grp and !defined($grp->parent)) {
1564                         $root = $grp;
1565                         next;
1566                 }
1567                 my ($parent) = grep { $_->id == $grp->parent} @list;
1568
1569                 $parent->children([]) unless defined($parent->children); 
1570                 push( @{$parent->children}, $grp );
1571         }
1572
1573         return $root;
1574
1575 }
1576
1577
1578 __PACKAGE__->register_method(
1579         method  => "add_user_to_groups",
1580         api_name        => "open-ils.actor.user.set_groups",
1581         notes           => <<"  NOTES");
1582         Adds a user to one or more permission groups
1583         NOTES
1584
1585 sub add_user_to_groups {
1586         my( $self, $client, $authtoken, $userid, $groups ) = @_;
1587
1588         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1589                 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
1590         return $evt if $evt;
1591
1592         ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1593                 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
1594         return $evt if $evt;
1595
1596         $apputils->simplereq(
1597                 'open-ils.storage',
1598                 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
1599                 
1600         for my $group (@$groups) {
1601                 my $link = Fieldmapper::permission::usr_grp_map->new;
1602                 $link->grp($group);
1603                 $link->usr($userid);
1604
1605                 my $id = $apputils->simplereq(
1606                         'open-ils.storage',
1607                         'open-ils.storage.direct.permission.usr_grp_map.create', $link );
1608         }
1609
1610         return 1;
1611 }
1612
1613 __PACKAGE__->register_method(
1614         method  => "get_user_perm_groups",
1615         api_name        => "open-ils.actor.user.get_groups",
1616         notes           => <<"  NOTES");
1617         Retrieve a user's permission groups.
1618         NOTES
1619
1620
1621 sub get_user_perm_groups {
1622         my( $self, $client, $authtoken, $userid ) = @_;
1623
1624         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1625                 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
1626         return $evt if $evt;
1627
1628         return $apputils->simplereq(
1629                 'open-ils.storage',
1630                 'open-ils.storage.direct.permission.usr_grp_map.search.usr.atomic', $userid );
1631 }       
1632
1633
1634
1635
1636 1;
1637