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