]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Actor.pm
moved patron update related code to events framework
[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/) {
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/) {
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/) {
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
1247
1248 sub user_transactions {
1249         my( $self, $client, $login_session, $user_id, $type ) = @_;
1250
1251         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1252                 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1253         return $evt if $evt;
1254         
1255         my $api = $self->api_name();
1256         my $trans;
1257         my @xact;
1258
1259         if(defined($type)) { @xact = (xact_type =>  $type); 
1260
1261         } else { @xact = (); }
1262
1263         if($api =~ /have_charge/) {
1264
1265                 $trans = $apputils->simple_scalar_request( 
1266                         "open-ils.storage",
1267                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1268                         { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1269
1270         } elsif($api =~ /have_balance/) {
1271
1272                 $trans =  $apputils->simple_scalar_request( 
1273                         "open-ils.storage",
1274                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1275                         { usr => $user_id, balance_owed => { ">" => 0 }, @xact });
1276
1277         } else {
1278
1279                 $trans =  $apputils->simple_scalar_request( 
1280                         "open-ils.storage",
1281                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1282                         { usr => $user_id, @xact });
1283         }
1284
1285         if($api !~ /fleshed/) { return $trans; }
1286
1287         #warn "API: $api\n";
1288
1289         my @resp;
1290         for my $t (@$trans) {
1291                         
1292                 #warn $t->id . "\n";
1293
1294
1295                 if( $t->xact_type ne 'circulation' ) {
1296                         push @resp, {transaction => $t};
1297                         next;
1298                 }
1299
1300                 my $circ = $apputils->simple_scalar_request(
1301                                 "open-ils.storage",
1302                                 "open-ils.storage.direct.action.circulation.retrieve",
1303                                 $t->id );
1304
1305                 next unless $circ;
1306
1307                 my $title = $apputils->simple_scalar_request(
1308                         "open-ils.storage", 
1309                         "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1310                         $circ->target_copy );
1311
1312                 next unless $title;
1313
1314                 my $u = OpenILS::Utils::ModsParser->new();
1315                 $u->start_mods_batch($title->marc());
1316                 my $mods = $u->finish_mods_batch();
1317
1318                 push @resp, {transaction => $t, circ => $circ, record => $mods };
1319
1320         }
1321
1322         return \@resp; 
1323
1324
1325
1326
1327
1328 __PACKAGE__->register_method(
1329         method  => "user_perms",
1330         api_name        => "open-ils.actor.permissions.user_perms.retrieve",
1331         argc            => 1,
1332         notes           => <<"  NOTES");
1333         Returns a list of permissions
1334         NOTES
1335 sub user_perms {
1336         my( $self, $client, $authtoken, $user ) = @_;
1337
1338         my( $staff, $evt ) = $apputils->checkses($authtoken);
1339         return $evt if $evt;
1340
1341         $user ||= $staff->id;
1342
1343         if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1344                 return $evt;
1345         }
1346
1347         return $apputils->simple_scalar_request(
1348                 "open-ils.storage",
1349                 "open-ils.storage.permission.user_perms.atomic",
1350                 $user);
1351 }
1352
1353 __PACKAGE__->register_method(
1354         method  => "retrieve_perms",
1355         api_name        => "open-ils.actor.permissions.retrieve",
1356         notes           => <<"  NOTES");
1357         Returns a list of permissions
1358         NOTES
1359 sub retrieve_perms {
1360         my( $self, $client ) = @_;
1361         return $apputils->simple_scalar_request(
1362                 "open-ils.storage",
1363                 "open-ils.storage.direct.permission.perm_list.retrieve.all.atomic");
1364 }
1365
1366 __PACKAGE__->register_method(
1367         method  => "retrieve_groups",
1368         api_name        => "open-ils.actor.groups.retrieve",
1369         notes           => <<"  NOTES");
1370         Returns a list of user groupss
1371         NOTES
1372 sub retrieve_groups {
1373         my( $self, $client ) = @_;
1374         return $apputils->simple_scalar_request(
1375                 "open-ils.storage",
1376                 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1377 }
1378
1379 __PACKAGE__->register_method(
1380         method  => "retrieve_groups_tree",
1381         api_name        => "open-ils.actor.groups.tree.retrieve",
1382         notes           => <<"  NOTES");
1383         Returns a list of user groups
1384         NOTES
1385 sub retrieve_groups_tree {
1386         my( $self, $client ) = @_;
1387         my $groups = $apputils->simple_scalar_request(
1388                 "open-ils.storage",
1389                 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1390         return $self->build_group_tree($groups);        
1391 }
1392
1393
1394 # turns an org list into an org tree
1395 sub build_group_tree {
1396
1397         my( $self, $grplist) = @_;
1398
1399         return $grplist unless ( 
1400                         ref($grplist) and @$grplist > 1 );
1401
1402         my @list = sort { $a->name cmp $b->name } @$grplist;
1403
1404         my $root;
1405         for my $grp (@list) {
1406
1407                 if ($grp and !defined($grp->parent)) {
1408                         $root = $grp;
1409                         next;
1410                 }
1411                 my ($parent) = grep { $_->id == $grp->parent} @list;
1412
1413                 $parent->children([]) unless defined($parent->children); 
1414                 push( @{$parent->children}, $grp );
1415         }
1416
1417         return $root;
1418
1419 }
1420
1421
1422 __PACKAGE__->register_method(
1423         method  => "add_user_to_groups",
1424         api_name        => "open-ils.actor.user.set_groups",
1425         notes           => <<"  NOTES");
1426         Adds a user to one or more permission groups
1427         NOTES
1428
1429 sub add_user_to_groups {
1430         my( $self, $client, $authtoken, $userid, $groups ) = @_;
1431
1432         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1433                 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
1434         return $evt if $evt;
1435
1436         ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1437                 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
1438         return $evt if $evt;
1439
1440         $apputils->simplereq(
1441                 'open-ils.storage',
1442                 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
1443                 
1444         for my $group (@$groups) {
1445                 my $link = Fieldmapper::permission::usr_grp_map->new;
1446                 $link->grp($group);
1447                 $link->usr($userid);
1448
1449                 my $id = $apputils->simplereq(
1450                         'open-ils.storage',
1451                         'open-ils.storage.direct.permission.usr_grp_map.create', $link );
1452         }
1453
1454         return 1;
1455 }
1456
1457 __PACKAGE__->register_method(
1458         method  => "get_user_perm_groups",
1459         api_name        => "open-ils.actor.user.get_groups",
1460         notes           => <<"  NOTES");
1461         Retrieve a user's permission groups.
1462         NOTES
1463
1464
1465 sub get_user_perm_groups {
1466         my( $self, $client, $authtoken, $userid ) = @_;
1467
1468         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1469                 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
1470         return $evt if $evt;
1471
1472         return $apputils->simplereq(
1473                 'open-ils.storage',
1474                 'open-ils.storage.direct.permission.usr_grp_map.search.usr.atomic', $userid );
1475 }       
1476
1477
1478
1479
1480 1;
1481