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