f2dfc5343f17ed31041bb343a5f4a35b78fa18bd
[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
230         my( $requestor, $target, $evt ) = $apputils->
231                 checkses_requestor( $user_session, $user_id, 'VIEW_USER' );
232         return $evt if $evt;
233
234         return flesh_user($user_id);
235 }
236
237
238 sub flesh_user {
239         my $id = shift;
240         my $session = shift;
241
242         my $kill = 0;
243
244         if(!$session) {
245                 $session = OpenSRF::AppSession->create("open-ils.storage");
246                 $kill = 1;
247         }
248
249         # grab the user with the given id 
250         my $ureq = $session->request(
251                         "open-ils.storage.direct.actor.user.retrieve", $id);
252         my $user = $ureq->gather(1);
253
254         if(!$user) { return undef; }
255
256         # grab the cards
257         my $cards_req = $session->request(
258                         "open-ils.storage.direct.actor.card.search.usr.atomic",
259                         $user->id() );
260         $user->cards( $cards_req->gather(1) );
261
262         for my $c(@{$user->cards}) {
263                 if($c->id == $user->card || $c->id eq $user->card ) {
264                         warn "Setting my card to " . $c->id . "\n";
265                         $user->card($c);
266                 }
267         }
268
269         my $add_req = $session->request(
270                         "open-ils.storage.direct.actor.user_address.search.usr.atomic",
271                         $user->id() );
272         $user->addresses( $add_req->gather(1) );
273
274         for my $c(@{$user->addresses}) {
275                 if($c->id eq $user->billing_address ) { $user->billing_address($c); }
276                 if($c->id eq $user->mailing_address ) { $user->mailing_address($c); }
277         }
278
279         my $stat_req = $session->request(
280                 "open-ils.storage.direct.actor.stat_cat_entry_user_map.search.target_usr.atomic",
281                 $user->id() );
282         $user->stat_cat_entries($stat_req->gather(1));
283
284         if($kill) { $session->disconnect(); }
285         $user->clear_passwd();
286
287         return $user;
288 }
289
290
291 # clone and clear stuff that would break the database
292 sub _clone_patron {
293         my $patron = shift;
294
295         my $new_patron = $patron->clone;
296
297         # Using the Fieldmapper clone method
298         #my $new_patron = Fieldmapper::actor::user->new();
299
300         #my $fmap = $Fieldmapper::fieldmap;
301         #no strict; # shallow clone, may be useful in the fieldmapper
302         #for my $field 
303         #       (keys %{$fmap->{"Fieldmapper::actor::user"}->{'fields'}}) {
304         #               $new_patron->$field( $patron->$field() );
305         #}
306         #use strict;
307
308         # clear these
309         $new_patron->clear_billing_address();
310         $new_patron->clear_mailing_address();
311         $new_patron->clear_addresses();
312         $new_patron->clear_card();
313         $new_patron->clear_cards();
314         $new_patron->clear_id();
315         $new_patron->clear_isnew();
316         $new_patron->clear_ischanged();
317         $new_patron->clear_isdeleted();
318         $new_patron->clear_stat_cat_entries();
319
320         return $new_patron;
321 }
322
323
324 sub _add_patron {
325         my $session             = shift;
326         my $patron              = shift;
327         my $user_obj    = shift;
328
329
330         if($apputils->check_user_perms(
331                                 $user_obj->id, $user_obj->home_ou, "CREATE_USER")) {
332                 return OpenILS::Perm->new("CREATE_USER");
333         }
334
335         warn "Creating new patron\n";
336         _d($patron);
337
338         my $req = $session->request(
339                 "open-ils.storage.direct.actor.user.create",$patron);
340         my $id = $req->gather(1);
341         if(!$id) { 
342                 return OpenILS::EX->new("DUPLICATE_USER_USERNAME");
343         }
344
345         # retrieve the patron from the db to collect defaults
346         my $ureq = $session->request(
347                         "open-ils.storage.direct.actor.user.retrieve",
348                         $id);
349
350         warn "Created new patron with id $id\n";
351
352         return $ureq->gather(1);
353 }
354
355
356 sub _update_patron {
357         my( $session, $patron, $user_obj) = @_;
358
359
360         if($patron->id ne $user_obj->id) {
361                 if($apputils->check_user_perms(
362                                         $user_obj->id, $user_obj->home_ou, "UPDATE_USER")) {
363                         return OpenILS::Perm->new("UPDATE_USER");
364                 }
365         }
366
367         warn "updating patron " . Dumper($patron) . "\n";
368
369         my $req = $session->request(
370                 "open-ils.storage.direct.actor.user.update",$patron );
371         my $status = $req->gather(1);
372         if(!defined($status)) { 
373                 throw OpenSRF::EX::ERROR 
374                         ("Unknown error updating patron"); 
375         }
376         return $patron;
377 }
378
379
380 sub _add_update_addresses {
381         my $session = shift;
382         my $patron = shift;
383         my $new_patron = shift;
384
385         my $current_id; # id of the address before creation
386
387         for my $address (@{$patron->addresses()}) {
388
389                 $address->usr($new_patron->id());
390
391                 if(ref($address) and $address->isnew()) {
392                         warn "Adding new address at street " . $address->street1() . "\n";
393
394                         $current_id = $address->id();
395                         $address = _add_address($session,$address);
396
397                         if( $patron->billing_address() and 
398                                         $patron->billing_address() == $current_id ) {
399                                 $new_patron->billing_address($address->id());
400                                 $new_patron->ischanged(1);
401                         }
402
403                         if( $patron->mailing_address() and
404                                         $patron->mailing_address() == $current_id ) {
405                                 $new_patron->mailing_address($address->id());
406                                 $new_patron->ischanged(1);
407                         }
408
409                 } elsif( ref($address) and $address->ischanged() ) {
410                         warn "Updating address at street " . $address->street1();
411                         $address->usr($new_patron->id());
412                         _update_address($session,$address);
413
414                 } elsif( ref($address) and $address->isdeleted() ) {
415                         warn "Deleting address at street " . $address->street1();
416
417                         if( $address->id() == $new_patron->mailing_address() ) {
418                                 $new_patron->clear_mailing_address();
419                                 _update_patron($session, $new_patron);
420                         }
421
422                         if( $address->id() == $new_patron->billing_address() ) {
423                                 $new_patron->clear_billing_address();
424                                 _update_patron($session, $new_patron);
425                         }
426
427                         _delete_address($session,$address);
428                 }
429         }
430
431         return $new_patron;
432 }
433
434
435 # adds an address to the db and returns the address with new id
436 sub _add_address {
437         my($session, $address) = @_;
438         $address->clear_id();
439
440         use Data::Dumper;
441         warn "Adding Address:\n";
442         warn Dumper($address);
443
444         # put the address into the database
445         my $req = $session->request(
446                 "open-ils.storage.direct.actor.user_address.create",
447                 $address );
448
449         #update the id
450         my $id = $req->gather(1);
451         if(!$id) { 
452                 throw OpenSRF::EX::ERROR 
453                         ("Unable to create new user address"); 
454         }
455
456         warn "Created address with id $id\n";
457
458         # update all the necessary id's
459         $address->id( $id );
460         return $address;
461 }
462
463
464 sub _update_address {
465         my( $session, $address ) = @_;
466         my $req = $session->request(
467                 "open-ils.storage.direct.actor.user_address.update",
468                 $address );
469         my $status = $req->gather(1);
470         if(!defined($status)) { 
471                 throw OpenSRF::EX::ERROR 
472                         ("Unknown error updating address"); 
473         }
474         return $address;
475 }
476
477
478
479 sub _add_update_cards {
480
481         my $session = shift;
482         my $patron = shift;
483         my $new_patron = shift;
484
485         my $virtual_id; #id of the card before creation
486         for my $card (@{$patron->cards()}) {
487
488                 $card->usr($new_patron->id());
489
490                 if(ref($card) and $card->isnew()) {
491
492                         $virtual_id = $card->id();
493                         $card = _add_card($session,$card);
494                         if(UNIVERSAL::isa($card,"OpenILS::EX")) {
495                                 return $card;
496                         }
497
498                         #if(ref($patron->card)) { $patron->card($patron->card->id); }
499                         if($patron->card() == $virtual_id) {
500                                 $new_patron->card($card->id());
501                                 $new_patron->ischanged(1);
502                         }
503
504                 } elsif( ref($card) and $card->ischanged() ) {
505                         $card->usr($new_patron->id());
506                         _update_card($session, $card);
507                 }
508         }
509         return $new_patron;
510 }
511
512
513 # adds an card to the db and returns the card with new id
514 sub _add_card {
515         my( $session, $card ) = @_;
516         $card->clear_id();
517
518         warn "Adding card with barcode " . $card->barcode() . "\n";
519         my $req = $session->request(
520                 "open-ils.storage.direct.actor.card.create",
521                 $card );
522
523         my $id = $req->gather(1);
524         if(!$id) { 
525                 return OpenILS::EX->new("DUPLICATE_INVALID_USER_BARCODE");
526         }
527
528         $card->id($id);
529         warn "Created patron card with id $id\n";
530         return $card;
531 }
532
533
534 sub _update_card {
535         my( $session, $card ) = @_;
536         warn Dumper $card;
537
538         my $req = $session->request(
539                 "open-ils.storage.direct.actor.card.update",
540                 $card );
541         my $status = $req->gather(1);
542         if(!defined($status)) { 
543                 throw OpenSRF::EX::ERROR 
544                         ("Unknown error updating card"); 
545         }
546         return $card;
547 }
548
549
550
551
552 sub _delete_address {
553         my( $session, $address ) = @_;
554
555         warn "Deleting address " . $address->street1() . "\n";
556
557         my $req = $session->request(
558                 "open-ils.storage.direct.actor.user_address.delete",
559                 $address );
560         my $status = $req->gather(1);
561         if(!defined($status)) { 
562                 throw OpenSRF::EX::ERROR 
563                         ("Unknown error updating address"); 
564         }
565         warn "Delete address status is $status\n";
566 }
567
568
569
570 sub _add_survey_responses {
571         my ($session, $patron, $new_patron) = @_;
572
573         warn "updating responses for user " . $new_patron->id . "\n";
574
575         my $responses = $patron->survey_responses;
576
577         if($responses) {
578
579                 for my $resp( @$responses ) {
580                         $resp->usr($new_patron->id);
581                 }
582
583                 my $status = $apputils->simple_scalar_request(
584                         "open-ils.circ", 
585                         "open-ils.circ.survey.submit.user_id",
586                         $responses );
587
588         }
589
590         return $new_patron;
591 }
592
593
594 sub _create_stat_maps {
595
596         my($session, $user_session, $patron, $new_patron) = @_;
597
598         my $maps = $patron->stat_cat_entries();
599
600         for my $map (@$maps) {
601
602                 next unless($map->isnew() || $map->ischanged());
603
604                 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
605                 if($map->isnew()) {
606                         $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
607                         $map->clear_id;
608                 }
609
610                 $map->target_usr($new_patron->id);
611
612                 warn "Updating stat entry with method $method and session $user_session and map $map\n";
613
614                 my $req = $session->request($method, $map);
615                 my $status = $req->gather(1);
616
617                 warn "Updated\n";
618
619                 if(!$status) {
620                         throw OpenSRF::EX::ERROR 
621                                 ("Error updating stat map with method $method");        
622                 }
623
624         }
625
626         return $new_patron;
627 }
628
629
630
631 __PACKAGE__->register_method(
632         method  => "search_username",
633         api_name        => "open-ils.actor.user.search.username",
634 );
635
636 sub search_username {
637         my($self, $client, $username) = @_;
638         my $users = OpenILS::Application::AppUtils->simple_scalar_request(
639                         "open-ils.storage", 
640                         "open-ils.storage.direct.actor.user.search.usrname.atomic",
641                         $username );
642         return $users;
643 }
644
645
646
647
648 __PACKAGE__->register_method(
649         method  => "user_retrieve_by_barcode",
650         api_name        => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
651
652 sub user_retrieve_by_barcode {
653         my($self, $client, $user_session, $barcode) = @_;
654
655         $logger->debug("Searching for user with barcode $barcode");
656         #my $user_obj = $apputils->check_user_session( $user_session ); 
657         my ($user_obj, $evt) = $apputils->check_ses($user_session);
658         return $evt if $evt;
659
660         my $session = OpenSRF::AppSession->create("open-ils.storage");
661
662         # find the card with the given barcode
663         my $creq        = $session->request(
664                         "open-ils.storage.direct.actor.card.search.barcode.atomic",
665                         $barcode );
666         my $card = $creq->gather(1);
667
668         if(!$card || !$card->[0]) {
669                 $session->disconnect();
670                 return OpenILS::Event->new( 'USER_NOT_FOUND' );
671         }
672
673         $card = $card->[0];
674         my $user = flesh_user($card->usr(), $session);
675         $session->disconnect();
676         if(!$user) { return OpenILS::Event->new( 'USER_NOT_FOUND' ); }
677         return $user;
678
679 }
680
681
682
683 __PACKAGE__->register_method(
684         method  => "get_user_by_id",
685         api_name        => "open-ils.actor.user.retrieve",);
686
687 sub get_user_by_id {
688         my ($self, $client, $user_session, $id) = @_;
689
690         my $user_obj = $apputils->check_user_session( $user_session ); 
691
692         return $apputils->simple_scalar_request(
693                 "open-ils.storage",
694                 "open-ils.storage.direct.actor.user.retrieve",
695                 $id );
696 }
697
698
699
700 __PACKAGE__->register_method(
701         method  => "get_org_types",
702         api_name        => "open-ils.actor.org_types.retrieve",);
703
704 my $org_types;
705 sub get_org_types {
706         my($self, $client) = @_;
707
708         return $org_types if $org_types;
709          return $org_types = 
710                  $apputils->simple_scalar_request(
711                         "open-ils.storage",
712                         "open-ils.storage.direct.actor.org_unit_type.retrieve.all.atomic" );
713 }
714
715
716
717 __PACKAGE__->register_method(
718         method  => "get_user_profiles",
719         api_name        => "open-ils.actor.user.profiles.retrieve",
720 );
721
722 my $user_profiles;
723 sub get_user_profiles {
724         return $user_profiles if $user_profiles;
725
726         return $user_profiles = 
727                 $apputils->simple_scalar_request(
728                         "open-ils.storage",
729                         "open-ils.storage.direct.actor.profile.retrieve.all.atomic");
730 }
731
732
733
734 __PACKAGE__->register_method(
735         method  => "get_user_ident_types",
736         api_name        => "open-ils.actor.user.ident_types.retrieve",
737 );
738 my $ident_types;
739 sub get_user_ident_types {
740         return $ident_types if $ident_types;
741         return $ident_types = 
742                 $apputils->simple_scalar_request(
743                 "open-ils.storage",
744                 "open-ils.storage.direct.config.identification_type.retrieve.all.atomic" );
745 }
746
747
748
749
750 __PACKAGE__->register_method(
751         method  => "get_org_unit",
752         api_name        => "open-ils.actor.org_unit.retrieve",
753 );
754
755 sub get_org_unit {
756
757         my( $self, $client, $user_session, $org_id ) = @_;
758
759         if(defined($user_session) && !defined($org_id)) {
760                 my $user_obj = 
761                         OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
762                 if(!defined($org_id)) {
763                         $org_id = $user_obj->home_ou;
764                 }
765         }
766
767
768         my $home_ou = OpenILS::Application::AppUtils->simple_scalar_request(
769                 "open-ils.storage",
770                 "open-ils.storage.direct.actor.org_unit.retrieve", 
771                 $org_id );
772
773         return $home_ou;
774 }
775
776
777 # build the org tree
778
779 __PACKAGE__->register_method(
780         method  => "get_org_tree",
781         api_name        => "open-ils.actor.org_tree.retrieve",
782         argc            => 1, 
783         note            => "Returns the entire org tree structure",
784 );
785
786 sub get_org_tree {
787         my( $self, $client) = @_;
788
789         if(!$cache_client) {
790                 $cache_client = OpenSRF::Utils::Cache->new("global", 0);
791         }
792         # see if it's in the cache
793         warn "Getting ORG Tree\n";
794         my $tree = $cache_client->get_cache('orgtree');
795         if($tree) { 
796                 warn "Found orgtree in cache. returning...\n";
797                 return $tree; 
798         }
799
800         my $orglist = $apputils->simple_scalar_request( 
801                 "open-ils.storage", 
802                 "open-ils.storage.direct.actor.org_unit.retrieve.all.atomic" );
803
804         if($orglist) {
805                 warn "found org list\n";
806         }
807
808         $tree = $self->build_org_tree($orglist);
809         $cache_client->put_cache('orgtree', $tree);
810
811         return $tree;
812
813 }
814
815 # turns an org list into an org tree
816 sub build_org_tree {
817
818         my( $self, $orglist) = @_;
819
820         return $orglist unless ( 
821                         ref($orglist) and @$orglist > 1 );
822
823         my @list = sort { 
824                 $a->ou_type <=> $b->ou_type ||
825                 $a->name cmp $b->name } @$orglist;
826
827         for my $org (@list) {
828
829                 next unless ($org and defined($org->parent_ou));
830                 my ($parent) = grep { $_->id == $org->parent_ou } @list;
831                 next unless $parent;
832
833                 $parent->children([]) unless defined($parent->children); 
834                 push( @{$parent->children}, $org );
835         }
836
837         return $list[0];
838
839 }
840
841
842 __PACKAGE__->register_method(
843         method  => "get_org_descendants",
844         api_name        => "open-ils.actor.org_tree.descendants.retrieve"
845 );
846
847 # depth is optional.  org_unit is the id
848 sub get_org_descendants {
849         my( $self, $client, $org_unit, $depth ) = @_;
850         my $orglist = $apputils->simple_scalar_request(
851                         "open-ils.storage", 
852                         "open-ils.storage.actor.org_unit.descendants.atomic",
853                         $org_unit, $depth );
854         return $self->build_org_tree($orglist);
855 }
856
857
858 __PACKAGE__->register_method(
859         method  => "get_org_ancestors",
860         api_name        => "open-ils.actor.org_tree.ancestors.retrieve"
861 );
862
863 # depth is optional.  org_unit is the id
864 sub get_org_ancestors {
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.ancestors.atomic",
869                         $org_unit, $depth );
870         return $self->build_org_tree($orglist);
871 }
872
873
874 __PACKAGE__->register_method(
875         method  => "get_standings",
876         api_name        => "open-ils.actor.standings.retrieve"
877 );
878
879 my $user_standings;
880 sub get_standings {
881         return $user_standings if $user_standings;
882         return $user_standings = 
883                 $apputils->simple_scalar_request(
884                         "open-ils.storage",
885                         "open-ils.storage.direct.config.standing.retrieve.all.atomic" );
886 }
887
888
889
890 __PACKAGE__->register_method(
891         method  => "get_my_org_path",
892         api_name        => "open-ils.actor.org_unit.full_path.retrieve"
893 );
894
895 sub get_my_org_path {
896         my( $self, $client, $user_session, $org_id ) = @_;
897         my $user_obj = $apputils->check_user_session($user_session); 
898         if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
899
900         return $apputils->simple_scalar_request(
901                 "open-ils.storage",
902                 "open-ils.storage.actor.org_unit.full_path.atomic",
903                 $org_id );
904 }
905
906
907 __PACKAGE__->register_method(
908         method  => "patron_adv_search",
909         api_name        => "open-ils.actor.patron.search.advanced" );
910
911 sub patron_adv_search {
912         my( $self, $client, $staff_login, $search_hash ) = @_;
913
914         use Data::Dumper;
915         warn "patron adv with $staff_login and search " . 
916                 Dumper($search_hash) . "\n";
917
918         my $session = OpenSRF::AppSession->create("open-ils.storage");
919         my $req = $session->request(
920                 "open-ils.storage.actor.user.crazy_search", $search_hash);
921
922         my $ans = $req->gather(1);
923
924         my %hash = map { ($_ =>1) } @$ans;
925         $ans = [ keys %hash ];
926
927         warn "Returning @$ans\n";
928
929         $session->disconnect();
930         return $ans;
931
932 }
933
934
935
936 sub _verify_password {
937         my($user_session, $password) = @_;
938         my $user_obj = $apputils->check_user_session($user_session); 
939
940         #grab the user with password
941         $user_obj = $apputils->simple_scalar_request(
942                 "open-ils.storage", 
943                 "open-ils.storage.direct.actor.user.retrieve",
944                 $user_obj->id );
945
946         if($user_obj->passwd eq $password) {
947                 return 1;
948         }
949
950         return 0;
951 }
952
953
954 __PACKAGE__->register_method(
955         method  => "update_password",
956         api_name        => "open-ils.actor.user.password.update");
957
958 __PACKAGE__->register_method(
959         method  => "update_password",
960         api_name        => "open-ils.actor.user.username.update");
961
962 __PACKAGE__->register_method(
963         method  => "update_password",
964         api_name        => "open-ils.actor.user.email.update");
965
966 sub update_password {
967         my( $self, $client, $user_session, $new_value, $current_password ) = @_;
968
969         warn "Updating user with method " .$self->api_name . "\n";
970         my $user_obj = $apputils->check_user_session($user_session); 
971
972         if($self->api_name =~ /password/) {
973
974                 #make sure they know the current password
975                 if(!_verify_password($user_session, md5_hex($current_password))) {
976                         return OpenILS::EX->new("USER_WRONG_PASSWORD")->ex;
977                 }
978
979                 $user_obj->passwd($new_value);
980         } 
981         elsif($self->api_name =~ /username/) {
982                 my $users = search_username(undef, undef, $new_value); 
983                 if( $users and $users->[0] ) {
984                         return OpenILS::Event->new('USERNAME_EXISTS');
985                 }
986                 $user_obj->usrname($new_value);
987         }
988
989         elsif($self->api_name =~ /email/) {
990                 warn "Updating email to $new_value\n";
991                 $user_obj->email($new_value);
992         }
993
994         my $session = $apputils->start_db_session();
995         $user_obj = _update_patron($session, $user_obj, $user_obj);
996         $apputils->commit_db_session($session);
997
998         if($user_obj) { return 1; }
999         return undef;
1000 }
1001
1002
1003 __PACKAGE__->register_method(
1004         method  => "check_user_perms",
1005         api_name        => "open-ils.actor.user.perm.check",
1006         notes           => <<"  NOTES");
1007         Takes a login session, user id, an org id, and an array of perm type strings.  For each
1008         perm type, if the user does *not* have the given permission it is added
1009         to a list which is returned from the method.  If all permissions
1010         are allowed, an empty list is returned
1011         if the logged in user does not match 'user_id', then the logged in user must
1012         have VIEW_PERMISSION priveleges.
1013         NOTES
1014
1015 sub check_user_perms {
1016         my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1017
1018         my( $staff, $evt ) = $apputils->checkses($login_session);
1019         return $evt if $evt;
1020
1021         if($staff->id ne $user_id) {
1022                 if( my $evt = $apputils->check_perms(
1023                         $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1024                         return $evt;
1025                 }
1026         }
1027
1028         my @not_allowed;
1029         for my $perm (@$perm_types) {
1030                 if($apputils->check_user_perms($user_id, $org_id, $perm)) {
1031                         push @not_allowed, $perm;
1032                 }
1033         }
1034
1035         return \@not_allowed
1036 }
1037
1038
1039
1040 __PACKAGE__->register_method(
1041         method  => "user_fines_summary",
1042         api_name        => "open-ils.actor.user.fines.summary",
1043         notes           => <<"  NOTES");
1044         Returns a short summary of the users total open fines, excluding voided fines
1045         Params are login_session, user_id
1046         Returns a 'mous' object.
1047         NOTES
1048
1049 sub user_fines_summary {
1050         my( $self, $client, $login_session, $user_id ) = @_;
1051
1052         my $user_obj = $apputils->check_user_session($login_session); 
1053         if($user_obj->id ne $user_id) {
1054                 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1055                         return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY"); 
1056                 }
1057         }
1058
1059         return $apputils->simple_scalar_request( 
1060                 "open-ils.storage",
1061                 "open-ils.storage.direct.money.open_user_summary.search.usr",
1062                 $user_id );
1063
1064 }
1065
1066
1067
1068
1069 __PACKAGE__->register_method(
1070         method  => "user_transactions",
1071         api_name        => "open-ils.actor.user.transactions",
1072         notes           => <<"  NOTES");
1073         Returns a list of open user transactions (mbts objects);
1074         Params are login_session, user_id
1075         Optional third parameter is the transactions type.  defaults to all
1076         NOTES
1077
1078 __PACKAGE__->register_method(
1079         method  => "user_transactions",
1080         api_name        => "open-ils.actor.user.transactions.have_charge",
1081         notes           => <<"  NOTES");
1082         Returns a list of all open user transactions (mbts objects) that have an initial charge
1083         Params are login_session, user_id
1084         Optional third parameter is the transactions type.  defaults to all
1085         NOTES
1086
1087 __PACKAGE__->register_method(
1088         method  => "user_transactions",
1089         api_name        => "open-ils.actor.user.transactions.have_balance",
1090         notes           => <<"  NOTES");
1091         Returns a list of all open user transactions (mbts objects) that have a balance
1092         Params are login_session, user_id
1093         Optional third parameter is the transactions type.  defaults to all
1094         NOTES
1095
1096 __PACKAGE__->register_method(
1097         method  => "user_transactions",
1098         api_name        => "open-ils.actor.user.transactions.fleshed",
1099         notes           => <<"  NOTES");
1100         Returns an object/hash of transaction, circ, title where transaction = an open 
1101         user transactions (mbts objects), circ is the attached circluation, and title
1102         is the title the circ points to
1103         Params are login_session, user_id
1104         Optional third parameter is the transactions type.  defaults to all
1105         NOTES
1106
1107 __PACKAGE__->register_method(
1108         method  => "user_transactions",
1109         api_name        => "open-ils.actor.user.transactions.have_charge.fleshed",
1110         notes           => <<"  NOTES");
1111         Returns an object/hash of transaction, circ, title where transaction = an open 
1112         user transactions that has an initial charge (mbts objects), circ is the 
1113         attached circluation, and title is the title the circ points to
1114         Params are login_session, user_id
1115         Optional third parameter is the transactions type.  defaults to all
1116         NOTES
1117
1118 __PACKAGE__->register_method(
1119         method  => "user_transactions",
1120         api_name        => "open-ils.actor.user.transactions.have_balance.fleshed",
1121         notes           => <<"  NOTES");
1122         Returns an object/hash of transaction, circ, title where transaction = an open 
1123         user transaction that has a balance (mbts objects), circ is the attached 
1124         circluation, and title is the title the circ points to
1125         Params are login_session, user_id
1126         Optional third parameter is the transaction type.  defaults to all
1127         NOTES
1128
1129
1130
1131 sub user_transactions {
1132         my( $self, $client, $login_session, $user_id, $type ) = @_;
1133
1134         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1135                 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1136         return $evt if $evt;
1137         
1138         my $api = $self->api_name();
1139         my $trans;
1140         my @xact;
1141
1142         if(defined($type)) { @xact = (xact_type =>  $type); 
1143
1144         } else { @xact = (); }
1145
1146         if($api =~ /have_charge/) {
1147
1148                 $trans = $apputils->simple_scalar_request( 
1149                         "open-ils.storage",
1150                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1151                         { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1152
1153         } elsif($api =~ /have_balance/) {
1154
1155                 $trans =  $apputils->simple_scalar_request( 
1156                         "open-ils.storage",
1157                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1158                         { usr => $user_id, balance_owed => { ">" => 0 }, @xact });
1159
1160         } else {
1161
1162                 $trans =  $apputils->simple_scalar_request( 
1163                         "open-ils.storage",
1164                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1165                         { usr => $user_id, @xact });
1166         }
1167
1168         if($api !~ /fleshed/) { return $trans; }
1169
1170         warn "API: $api\n";
1171
1172         my @resp;
1173         for my $t (@$trans) {
1174                         
1175                 warn $t->id . "\n";
1176
1177                 my $circ = $apputils->simple_scalar_request(
1178                                 "open-ils.storage",
1179                                 "open-ils.storage.direct.action.circulation.retrieve",
1180                                 $t->id );
1181
1182                 next unless $circ;
1183
1184                 my $title = $apputils->simple_scalar_request(
1185                         "open-ils.storage", 
1186                         "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1187                         $circ->target_copy );
1188
1189                 next unless $title;
1190
1191                 my $u = OpenILS::Utils::ModsParser->new();
1192                 $u->start_mods_batch($title->marc());
1193                 my $mods = $u->finish_mods_batch();
1194
1195                 push @resp, {transaction => $t, circ => $circ, record => $mods };
1196
1197         }
1198
1199         return \@resp; 
1200
1201
1202
1203
1204
1205 __PACKAGE__->register_method(
1206         method  => "retrieve_groups",
1207         api_name        => "open-ils.actor.groups.retrieve",
1208         notes           => <<"  NOTES");
1209         Returns a list of user groupss
1210         NOTES
1211 sub retrieve_groups {
1212         my( $self, $client ) = @_;
1213         return $apputils->simple_scalar_request(
1214                 "open-ils.storage",
1215                 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1216 }
1217
1218 __PACKAGE__->register_method(
1219         method  => "retrieve_groups_tree",
1220         api_name        => "open-ils.actor.groups.tree.retrieve",
1221         notes           => <<"  NOTES");
1222         Returns a list of user groups
1223         NOTES
1224 sub retrieve_groups_tree {
1225         my( $self, $client ) = @_;
1226         my $groups = $apputils->simple_scalar_request(
1227                 "open-ils.storage",
1228                 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1229         return $self->build_group_tree($groups);        
1230 }
1231
1232
1233 # turns an org list into an org tree
1234 sub build_group_tree {
1235
1236         my( $self, $grplist) = @_;
1237
1238         return $grplist unless ( 
1239                         ref($grplist) and @$grplist > 1 );
1240
1241         my @list = sort { $a->name cmp $b->name } @$grplist;
1242
1243         my $root;
1244         for my $grp (@list) {
1245
1246                 if ($grp and !defined($grp->parent)) {
1247                         $root = $grp;
1248                         next;
1249                 }
1250                 my ($parent) = grep { $_->id == $grp->parent} @list;
1251
1252                 $parent->children([]) unless defined($parent->children); 
1253                 push( @{$parent->children}, $grp );
1254         }
1255
1256         return $root;
1257
1258 }
1259
1260
1261 __PACKAGE__->register_method(
1262         method  => "add_user_to_groups",
1263         api_name        => "open-ils.actor.user.set_groups",
1264         notes           => <<"  NOTES");
1265         Adds a user to one or more permission groups
1266         NOTES
1267
1268 sub add_user_to_groups {
1269         my( $self, $client, $authtoken, $userid, $groups ) = @_;
1270
1271         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1272                 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
1273         return $evt if $evt;
1274
1275         ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1276                 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
1277         return $evt if $evt;
1278
1279         $apputils->simplereq(
1280                 'open-ils.storage',
1281                 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
1282                 
1283         for my $group (@$groups) {
1284                 my $link = Fieldmapper::permission::usr_grp_map->new;
1285                 $link->grp($group);
1286                 $link->usr($userid);
1287
1288                 my $id = $apputils->simplereq(
1289                         'open-ils.storage',
1290                         'open-ils.storage.direct.permission.usr_grp_map.create', $link );
1291         }
1292
1293         return 1;
1294 }
1295
1296 __PACKAGE__->register_method(
1297         method  => "get_user_perm_groups",
1298         api_name        => "open-ils.actor.user.get_groups",
1299         notes           => <<"  NOTES");
1300         Retrieve a user's permission groups.
1301         NOTES
1302
1303
1304 sub get_user_perm_groups {
1305         my( $self, $client, $authtoken, $userid ) = @_;
1306
1307         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1308                 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
1309         return $evt if $evt;
1310
1311         return $apputils->simplereq(
1312                 'open-ils.storage',
1313                 'open-ils.storage.direct.permission.usr_grp_map.search.usr.atomic', $userid );
1314 }       
1315
1316
1317 1;
1318