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