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