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