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