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