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