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