]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Actor.pm
added username.exists method and added dup checks to user editor
[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;
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->usrrname())->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                 $address->usr($new_patron->id());
451
452                 if(ref($address) and $address->isnew()) {
453
454                         $current_id = $address->id();
455                         ($address, $evt) = _add_address($session,$address);
456                         return (undef, $evt) if $evt;
457
458                         if( $patron->billing_address() and 
459                                         $patron->billing_address() == $current_id ) {
460                                 $new_patron->billing_address($address->id());
461                                 $new_patron->ischanged(1);
462                         }
463
464                         if( $patron->mailing_address() and
465                                         $patron->mailing_address() == $current_id ) {
466                                 $new_patron->mailing_address($address->id());
467                                 $new_patron->ischanged(1);
468                         }
469
470                 } elsif( ref($address) and $address->ischanged() ) {
471
472                         $address->usr($new_patron->id());
473                         ($address, $evt) = _update_address($session, $address);
474                         return (undef, $evt) if $evt;
475
476                 } elsif( ref($address) and $address->isdeleted() ) {
477
478                         if( $address->id() == $new_patron->mailing_address() ) {
479                                 $new_patron->clear_mailing_address();
480                                 ($new_patron, $evt) = _update_patron($session, $new_patron);
481                                 return (undef, $evt) if $evt;
482                         }
483
484                         if( $address->id() == $new_patron->billing_address() ) {
485                                 $new_patron->clear_billing_address();
486                                 ($new_patron, $evt) = _update_patron($session, $new_patron);
487                                 return (undef, $evt) if $evt;
488                         }
489
490                         $evt = _delete_address($session, $address);
491                         return (undef, $evt) if $evt;
492                 }
493         }
494
495         return ( $new_patron, undef );
496 }
497
498
499 # adds an address to the db and returns the address with new id
500 sub _add_address {
501         my($session, $address) = @_;
502         $address->clear_id();
503
504         $logger->info("Creating new address at street ".$address->street1);
505
506         # put the address into the database
507         my $id = $session->request(
508                 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
509         return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
510
511         $address->id( $id );
512         return ($address, undef);
513 }
514
515
516 sub _update_address {
517         my( $session, $address ) = @_;
518
519         $logger->info("Updating address ".$address->id." in the DB");
520
521         my $stat = $session->request(
522                 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
523
524         return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
525         return ($address, undef);
526 }
527
528
529
530 sub _add_update_cards {
531
532         my $session = shift;
533         my $patron = shift;
534         my $new_patron = shift;
535
536         my $evt;
537
538         my $virtual_id; #id of the card before creation
539         for my $card (@{$patron->cards()}) {
540
541                 $card->usr($new_patron->id());
542
543                 if(ref($card) and $card->isnew()) {
544
545                         $virtual_id = $card->id();
546                         ( $card, $evt ) = _add_card($session,$card);
547                         return (undef, $evt) if $evt;
548
549                         #if(ref($patron->card)) { $patron->card($patron->card->id); }
550                         if($patron->card() == $virtual_id) {
551                                 $new_patron->card($card->id());
552                                 $new_patron->ischanged(1);
553                         }
554
555                 } elsif( ref($card) and $card->ischanged() ) {
556                         $card->usr($new_patron->id());
557                         $evt = _update_card($session, $card);
558                         return (undef, $evt) if $evt;
559                 }
560         }
561
562         return ( $new_patron, undef );
563 }
564
565
566 # adds an card to the db and returns the card with new id
567 sub _add_card {
568         my( $session, $card ) = @_;
569         $card->clear_id();
570
571         $logger->info("Adding new patron card ".$card->barcode);
572
573         my $id = $session->request(
574                 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
575         return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
576         $logger->info("Successfully created patron card $id");
577
578         $card->id($id);
579         return ( $card, undef );
580 }
581
582
583 # returns event on error.  returns undef otherwise
584 sub _update_card {
585         my( $session, $card ) = @_;
586         $logger->info("Updating patron card ".$card->id);
587
588         my $stat = $session->request(
589                 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
590         return $U->DB_UPDATE_FAILED($card) unless defined($stat);
591         return undef;
592 }
593
594
595
596
597 # returns event on error.  returns undef otherwise
598 sub _delete_address {
599         my( $session, $address ) = @_;
600
601         $logger->info("Deleting address ".$address->id." from DB");
602
603         my $stat = $session->request(
604                 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
605
606         return $U->DB_UPDATE_FAILED($address) unless defined($stat);
607         return undef;
608 }
609
610
611
612 sub _add_survey_responses {
613         my ($session, $patron, $new_patron) = @_;
614
615         $logger->info( "Updating survey responses for patron ".$new_patron->id );
616
617         my $responses = $patron->survey_responses;
618
619         if($responses) {
620
621                 $_->usr($new_patron->id) for (@$responses);
622
623                 my $evt = $U->simplereq( "open-ils.circ", 
624                         "open-ils.circ.survey.submit.user_id", $responses );
625
626                 return (undef, $evt) if defined($U->event_code($evt));
627
628         }
629
630         return ( $new_patron, undef );
631 }
632
633
634 sub _create_stat_maps {
635
636         my($session, $user_session, $patron, $new_patron) = @_;
637
638         my $maps = $patron->stat_cat_entries();
639
640         for my $map (@$maps) {
641
642                 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
643
644                 if ($map->isdeleted()) {
645                         $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
646
647                 } elsif ($map->isnew()) {
648                         $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
649                         $map->clear_id;
650                 }
651
652
653                 $map->target_usr($new_patron->id);
654
655                 #warn "
656                 $logger->info("Updating stat entry with method $method and map $map");
657
658                 my $stat = $session->request($method, $map)->gather(1);
659                 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
660
661         }
662
663         return ($new_patron, undef);
664 }
665
666 sub _create_perm_maps {
667
668         my($session, $user_session, $patron, $new_patron) = @_;
669
670         my $maps = $patron->permissions;
671
672         for my $map (@$maps) {
673
674                 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
675                 if ($map->isdeleted()) {
676                         $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
677                 } elsif ($map->isnew()) {
678                         $method = "open-ils.storage.direct.permission.usr_perm_map.create";
679                         $map->clear_id;
680                 }
681
682
683                 $map->usr($new_patron->id);
684
685                 #warn( "Updating permissions with method $method and session $user_session and map $map" );
686                 $logger->info( "Updating permissions with method $method and map $map" );
687
688                 my $stat = $session->request($method, $map)->gather(1);
689                 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
690
691         }
692
693         return ($new_patron, undef);
694 }
695
696
697 sub _create_standing_penalties {
698
699         my($session, $user_session, $patron, $new_patron) = @_;
700
701         my $maps = $patron->standing_penalties;
702         my $method;
703
704         for my $map (@$maps) {
705
706                 if ($map->isdeleted()) {
707                         $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
708                 } elsif ($map->isnew()) {
709                         $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
710                         $map->clear_id;
711                 } else {
712                         next;
713                 }
714
715                 $map->usr($new_patron->id);
716
717                 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
718
719                 my $stat = $session->request($method, $map)->gather(1);
720                 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
721         }
722
723         return ($new_patron, undef);
724 }
725
726
727
728 __PACKAGE__->register_method(
729         method  => "search_username",
730         api_name        => "open-ils.actor.user.search.username",
731 );
732
733 sub search_username {
734         my($self, $client, $username) = @_;
735         my $users = OpenILS::Application::AppUtils->simple_scalar_request(
736                         "open-ils.storage", 
737                         "open-ils.storage.direct.actor.user.search.usrname.atomic",
738                         $username );
739         return $users;
740 }
741
742
743
744
745 __PACKAGE__->register_method(
746         method  => "user_retrieve_by_barcode",
747         api_name        => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
748
749 sub user_retrieve_by_barcode {
750         my($self, $client, $user_session, $barcode) = @_;
751
752         $logger->debug("Searching for user with barcode $barcode");
753         my ($user_obj, $evt) = $apputils->checkses($user_session);
754         return $evt if $evt;
755
756
757         my $session = OpenSRF::AppSession->create("open-ils.storage");
758
759         # find the card with the given barcode
760         my $creq        = $session->request(
761                         "open-ils.storage.direct.actor.card.search.barcode.atomic",
762                         $barcode );
763         my $card = $creq->gather(1);
764
765         if(!$card || !$card->[0]) {
766                 $session->disconnect();
767                 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
768         }
769
770         $card = $card->[0];
771         my $user = flesh_user($card->usr(), $session);
772
773         $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
774         return $evt if $evt;
775
776         $session->disconnect();
777         if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
778         return $user;
779
780 }
781
782
783
784 __PACKAGE__->register_method(
785         method  => "get_user_by_id",
786         api_name        => "open-ils.actor.user.retrieve",);
787
788 sub get_user_by_id {
789         my ($self, $client, $user_session, $id) = @_;
790
791         my $user_obj = $apputils->check_user_session( $user_session ); 
792
793         return $apputils->simple_scalar_request(
794                 "open-ils.storage",
795                 "open-ils.storage.direct.actor.user.retrieve",
796                 $id );
797 }
798
799
800
801 __PACKAGE__->register_method(
802         method  => "get_org_types",
803         api_name        => "open-ils.actor.org_types.retrieve",);
804
805 my $org_types;
806 sub get_org_types {
807         my($self, $client) = @_;
808
809         return $org_types if $org_types;
810          return $org_types = 
811                  $apputils->simple_scalar_request(
812                         "open-ils.storage",
813                         "open-ils.storage.direct.actor.org_unit_type.retrieve.all.atomic" );
814 }
815
816
817
818 __PACKAGE__->register_method(
819         method  => "get_user_profiles",
820         api_name        => "open-ils.actor.user.profiles.retrieve",
821 );
822
823 my $user_profiles;
824 sub get_user_profiles {
825         return $user_profiles if $user_profiles;
826
827         return $user_profiles = 
828                 $apputils->simple_scalar_request(
829                         "open-ils.storage",
830                         "open-ils.storage.direct.actor.profile.retrieve.all.atomic");
831 }
832
833
834
835 __PACKAGE__->register_method(
836         method  => "get_user_ident_types",
837         api_name        => "open-ils.actor.user.ident_types.retrieve",
838 );
839 my $ident_types;
840 sub get_user_ident_types {
841         return $ident_types if $ident_types;
842         return $ident_types = 
843                 $apputils->simple_scalar_request(
844                 "open-ils.storage",
845                 "open-ils.storage.direct.config.identification_type.retrieve.all.atomic" );
846 }
847
848
849
850
851 __PACKAGE__->register_method(
852         method  => "get_org_unit",
853         api_name        => "open-ils.actor.org_unit.retrieve",
854 );
855
856 sub get_org_unit {
857
858         my( $self, $client, $user_session, $org_id ) = @_;
859
860         if(defined($user_session) && !defined($org_id)) {
861                 my $user_obj = 
862                         OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
863                 if(!defined($org_id)) {
864                         $org_id = $user_obj->home_ou;
865                 }
866         }
867
868
869         my $home_ou = OpenILS::Application::AppUtils->simple_scalar_request(
870                 "open-ils.storage",
871                 "open-ils.storage.direct.actor.org_unit.retrieve", 
872                 $org_id );
873
874         return $home_ou;
875 }
876
877 __PACKAGE__->register_method(
878         method  => "search_org_unit",
879         api_name        => "open-ils.actor.org_unit_list.search",
880 );
881
882 sub search_org_unit {
883
884         my( $self, $client, $field, $value ) = @_;
885
886         my $list = OpenILS::Application::AppUtils->simple_scalar_request(
887                 "open-ils.storage",
888                 "open-ils.storage.direct.actor.org_unit.search.$field.atomic", 
889                 $value );
890
891         return $list;
892 }
893
894
895 # build the org tree
896
897 __PACKAGE__->register_method(
898         method  => "get_org_tree",
899         api_name        => "open-ils.actor.org_tree.retrieve",
900         argc            => 0, 
901         note            => "Returns the entire org tree structure",
902 );
903
904 sub get_org_tree {
905         my( $self, $client) = @_;
906
907         if(!$cache_client) {
908                 $cache_client = OpenSRF::Utils::Cache->new("global", 0);
909         }
910         # see if it's in the cache
911         #warn "Getting ORG Tree\n";
912         my $tree = $cache_client->get_cache('orgtree');
913         if($tree) { 
914                 #warn "Found orgtree in cache. returning...\n";
915                 return $tree; 
916         }
917
918         my $orglist = $apputils->simple_scalar_request( 
919                 "open-ils.storage", 
920                 "open-ils.storage.direct.actor.org_unit.retrieve.all.atomic" );
921
922         #if($orglist) {
923                 #warn "found org list\n";
924         #}
925
926         $tree = $self->build_org_tree($orglist);
927         $cache_client->put_cache('orgtree', $tree);
928
929         return $tree;
930
931 }
932
933 # turns an org list into an org tree
934 sub build_org_tree {
935
936         my( $self, $orglist) = @_;
937
938         return $orglist unless ( 
939                         ref($orglist) and @$orglist > 1 );
940
941         my @list = sort { 
942                 $a->ou_type <=> $b->ou_type ||
943                 $a->name cmp $b->name } @$orglist;
944
945         for my $org (@list) {
946
947                 next unless ($org and defined($org->parent_ou));
948                 my ($parent) = grep { $_->id == $org->parent_ou } @list;
949                 next unless $parent;
950
951                 $parent->children([]) unless defined($parent->children); 
952                 push( @{$parent->children}, $org );
953         }
954
955         return $list[0];
956
957 }
958
959
960 __PACKAGE__->register_method(
961         method  => "get_org_descendants",
962         api_name        => "open-ils.actor.org_tree.descendants.retrieve"
963 );
964
965 # depth is optional.  org_unit is the id
966 sub get_org_descendants {
967         my( $self, $client, $org_unit, $depth ) = @_;
968         my $orglist = $apputils->simple_scalar_request(
969                         "open-ils.storage", 
970                         "open-ils.storage.actor.org_unit.descendants.atomic",
971                         $org_unit, $depth );
972         return $self->build_org_tree($orglist);
973 }
974
975
976 __PACKAGE__->register_method(
977         method  => "get_org_ancestors",
978         api_name        => "open-ils.actor.org_tree.ancestors.retrieve"
979 );
980
981 # depth is optional.  org_unit is the id
982 sub get_org_ancestors {
983         my( $self, $client, $org_unit, $depth ) = @_;
984         my $orglist = $apputils->simple_scalar_request(
985                         "open-ils.storage", 
986                         "open-ils.storage.actor.org_unit.ancestors.atomic",
987                         $org_unit, $depth );
988         return $self->build_org_tree($orglist);
989 }
990
991
992 __PACKAGE__->register_method(
993         method  => "get_standings",
994         api_name        => "open-ils.actor.standings.retrieve"
995 );
996
997 my $user_standings;
998 sub get_standings {
999         return $user_standings if $user_standings;
1000         return $user_standings = 
1001                 $apputils->simple_scalar_request(
1002                         "open-ils.storage",
1003                         "open-ils.storage.direct.config.standing.retrieve.all.atomic" );
1004 }
1005
1006
1007
1008 __PACKAGE__->register_method(
1009         method  => "get_my_org_path",
1010         api_name        => "open-ils.actor.org_unit.full_path.retrieve"
1011 );
1012
1013 sub get_my_org_path {
1014         my( $self, $client, $user_session, $org_id ) = @_;
1015         my $user_obj = $apputils->check_user_session($user_session); 
1016         if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
1017
1018         return $apputils->simple_scalar_request(
1019                 "open-ils.storage",
1020                 "open-ils.storage.actor.org_unit.full_path.atomic",
1021                 $org_id );
1022 }
1023
1024
1025 __PACKAGE__->register_method(
1026         method  => "patron_adv_search",
1027         api_name        => "open-ils.actor.patron.search.advanced" );
1028 sub patron_adv_search {
1029         my( $self, $client, $auth, $search_hash, $search_limit, $search_sort ) = @_;
1030         my $e = OpenILS::Utils::Editor->new(authtoken=>$auth);
1031         return $e->event unless $e->checkauth;
1032         return $e->event unless $e->allowed('VIEW_USER');
1033         return $e->request(
1034                 "open-ils.storage.actor.user.crazy_search", 
1035                 $search_hash, $search_limit, $search_sort);
1036 }
1037
1038
1039
1040 sub _verify_password {
1041         my($user_session, $password) = @_;
1042         my $user_obj = $apputils->check_user_session($user_session); 
1043
1044         #grab the user with password
1045         $user_obj = $apputils->simple_scalar_request(
1046                 "open-ils.storage", 
1047                 "open-ils.storage.direct.actor.user.retrieve",
1048                 $user_obj->id );
1049
1050         if($user_obj->passwd eq $password) {
1051                 return 1;
1052         }
1053
1054         return 0;
1055 }
1056
1057
1058 __PACKAGE__->register_method(
1059         method  => "update_password",
1060         api_name        => "open-ils.actor.user.password.update");
1061
1062 __PACKAGE__->register_method(
1063         method  => "update_password",
1064         api_name        => "open-ils.actor.user.username.update");
1065
1066 __PACKAGE__->register_method(
1067         method  => "update_password",
1068         api_name        => "open-ils.actor.user.email.update");
1069
1070 sub update_password {
1071         my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1072
1073         my $evt;
1074
1075         my $user_obj = $apputils->check_user_session($user_session); 
1076
1077         if($self->api_name =~ /password/o) {
1078
1079                 #make sure they know the current password
1080                 if(!_verify_password($user_session, md5_hex($current_password))) {
1081                         return OpenILS::Event->new('INCORRECT_PASSWORD');
1082                 }
1083
1084                 $logger->debug("update_password setting new password $new_value");
1085                 $user_obj->passwd($new_value);
1086
1087         } elsif($self->api_name =~ /username/o) {
1088                 my $users = search_username(undef, undef, $new_value); 
1089                 if( $users and $users->[0] ) {
1090                         return OpenILS::Event->new('USERNAME_EXISTS');
1091                 }
1092                 $user_obj->usrname($new_value);
1093
1094         } elsif($self->api_name =~ /email/o) {
1095                 #warn "Updating email to $new_value\n";
1096                 $user_obj->email($new_value);
1097         }
1098
1099         my $session = $apputils->start_db_session();
1100
1101         ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1102         return $evt if $evt;
1103
1104         $apputils->commit_db_session($session);
1105
1106         if($user_obj) { return 1; }
1107         return undef;
1108 }
1109
1110
1111 __PACKAGE__->register_method(
1112         method  => "check_user_perms",
1113         api_name        => "open-ils.actor.user.perm.check",
1114         notes           => <<"  NOTES");
1115         Takes a login session, user id, an org id, and an array of perm type strings.  For each
1116         perm type, if the user does *not* have the given permission it is added
1117         to a list which is returned from the method.  If all permissions
1118         are allowed, an empty list is returned
1119         if the logged in user does not match 'user_id', then the logged in user must
1120         have VIEW_PERMISSION priveleges.
1121         NOTES
1122
1123 sub check_user_perms {
1124         my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1125
1126         my( $staff, $evt ) = $apputils->checkses($login_session);
1127         return $evt if $evt;
1128
1129         if($staff->id ne $user_id) {
1130                 if( my $evt = $apputils->check_perms(
1131                         $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1132                         return $evt;
1133                 }
1134         }
1135
1136         my @not_allowed;
1137         for my $perm (@$perm_types) {
1138                 if($apputils->check_perms($user_id, $org_id, $perm)) {
1139                         push @not_allowed, $perm;
1140                 }
1141         }
1142
1143         return \@not_allowed
1144 }
1145
1146 __PACKAGE__->register_method(
1147         method  => "check_user_perms2",
1148         api_name        => "open-ils.actor.user.perm.check.multi_org",
1149         notes           => q/
1150                 Checks the permissions on a list of perms and orgs for a user
1151                 @param authtoken The login session key
1152                 @param user_id The id of the user to check
1153                 @param orgs The array of org ids
1154                 @param perms The array of permission names
1155                 @return An array of  [ orgId, permissionName ] arrays that FAILED the check
1156                 if the logged in user does not match 'user_id', then the logged in user must
1157                 have VIEW_PERMISSION priveleges.
1158         /);
1159
1160 sub check_user_perms2 {
1161         my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1162
1163         my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1164                 $authtoken, $user_id, 'VIEW_PERMISSION' );
1165         return $evt if $evt;
1166
1167         my @not_allowed;
1168         for my $org (@$orgs) {
1169                 for my $perm (@$perms) {
1170                         if($apputils->check_perms($user_id, $org, $perm)) {
1171                                 push @not_allowed, [ $org, $perm ];
1172                         }
1173                 }
1174         }
1175
1176         return \@not_allowed
1177 }
1178
1179
1180 __PACKAGE__->register_method(
1181         method => 'check_user_perms3',
1182         api_name        => 'open-ils.actor.user.perm.highest_org',
1183         notes           => q/
1184                 Returns the highest org unit id at which a user has a given permission
1185                 If the requestor does not match the target user, the requestor must have
1186                 'VIEW_PERMISSION' rights at the home org unit of the target user
1187                 @param authtoken The login session key
1188                 @param userid The id of the user in question
1189                 @param perm The permission to check
1190                 @return The org unit highest in the org tree within which the user has
1191                 the requested permission
1192         /);
1193
1194 sub check_user_perms3 {
1195         my( $self, $client, $authtoken, $userid, $perm ) = @_;
1196
1197         my( $staff, $target, $org, $evt );
1198
1199         ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1200                 $authtoken, $userid, 'VIEW_PERMISSION' );
1201         return $evt if $evt;
1202
1203         my $tree = $self->get_org_tree();
1204         return _find_highest_perm_org( $perm, $userid, $target->home_ou, $tree );
1205 }
1206
1207
1208 sub _find_highest_perm_org {
1209         my ( $perm, $userid, $start_org, $org_tree ) = @_;
1210         my $org = $apputils->find_org($org_tree, $start_org );
1211
1212         my $lastid = undef;
1213         while( $org ) {
1214                 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1215                 $lastid = $org->id;
1216                 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1217         }
1218
1219         return $lastid;
1220 }
1221
1222 __PACKAGE__->register_method(
1223         method => 'check_user_perms4',
1224         api_name        => 'open-ils.actor.user.perm.highest_org.batch',
1225         notes           => q/
1226                 Returns the highest org unit id at which a user has a given permission
1227                 If the requestor does not match the target user, the requestor must have
1228                 'VIEW_PERMISSION' rights at the home org unit of the target user
1229                 @param authtoken The login session key
1230                 @param userid The id of the user in question
1231                 @param perms An array of perm names to check 
1232                 @return An array of orgId's  representing the org unit 
1233                 highest in the org tree within which the user has the requested permission
1234                 The arrah of orgId's has matches the order of the perms array
1235         /);
1236
1237 sub check_user_perms4 {
1238         my( $self, $client, $authtoken, $userid, $perms ) = @_;
1239         
1240         my( $staff, $target, $org, $evt );
1241
1242         ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1243                 $authtoken, $userid, 'VIEW_PERMISSION' );
1244         return $evt if $evt;
1245
1246         my @arr;
1247         return [] unless ref($perms);
1248         my $tree = $self->get_org_tree();
1249
1250         for my $p (@$perms) {
1251                 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1252         }
1253         return \@arr;
1254 }
1255
1256
1257
1258
1259 __PACKAGE__->register_method(
1260         method  => "user_fines_summary",
1261         api_name        => "open-ils.actor.user.fines.summary",
1262         notes           => <<"  NOTES");
1263         Returns a short summary of the users total open fines, excluding voided fines
1264         Params are login_session, user_id
1265         Returns a 'mous' object.
1266         NOTES
1267
1268 sub user_fines_summary {
1269         my( $self, $client, $login_session, $user_id ) = @_;
1270
1271         my $user_obj = $apputils->check_user_session($login_session); 
1272         if($user_obj->id ne $user_id) {
1273                 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1274                         return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY"); 
1275                 }
1276         }
1277
1278         return $apputils->simple_scalar_request( 
1279                 "open-ils.storage",
1280                 "open-ils.storage.direct.money.open_user_summary.search.usr",
1281                 $user_id );
1282
1283 }
1284
1285
1286
1287
1288 __PACKAGE__->register_method(
1289         method  => "user_transactions",
1290         api_name        => "open-ils.actor.user.transactions",
1291         notes           => <<"  NOTES");
1292         Returns a list of open user transactions (mbts objects);
1293         Params are login_session, user_id
1294         Optional third parameter is the transactions type.  defaults to all
1295         NOTES
1296
1297 __PACKAGE__->register_method(
1298         method  => "user_transactions",
1299         api_name        => "open-ils.actor.user.transactions.have_charge",
1300         notes           => <<"  NOTES");
1301         Returns a list of all open user transactions (mbts objects) that have an initial charge
1302         Params are login_session, user_id
1303         Optional third parameter is the transactions type.  defaults to all
1304         NOTES
1305
1306 __PACKAGE__->register_method(
1307         method  => "user_transactions",
1308         api_name        => "open-ils.actor.user.transactions.have_balance",
1309         notes           => <<"  NOTES");
1310         Returns a list of all open user transactions (mbts objects) that have a balance
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.fleshed",
1318         notes           => <<"  NOTES");
1319         Returns an object/hash of transaction, circ, title where transaction = an open 
1320         user transactions (mbts objects), circ is the attached circluation, and title
1321         is the title the circ points to
1322         Params are login_session, user_id
1323         Optional third parameter is the transactions type.  defaults to all
1324         NOTES
1325
1326 __PACKAGE__->register_method(
1327         method  => "user_transactions",
1328         api_name        => "open-ils.actor.user.transactions.have_charge.fleshed",
1329         notes           => <<"  NOTES");
1330         Returns an object/hash of transaction, circ, title where transaction = an open 
1331         user transactions that has an initial charge (mbts objects), circ is the 
1332         attached circluation, and title is the title the circ points to
1333         Params are login_session, user_id
1334         Optional third parameter is the transactions type.  defaults to all
1335         NOTES
1336
1337 __PACKAGE__->register_method(
1338         method  => "user_transactions",
1339         api_name        => "open-ils.actor.user.transactions.have_balance.fleshed",
1340         notes           => <<"  NOTES");
1341         Returns an object/hash of transaction, circ, title where transaction = an open 
1342         user transaction that has a balance (mbts objects), circ is the attached 
1343         circluation, and title is the title the circ points to
1344         Params are login_session, user_id
1345         Optional third parameter is the transaction type.  defaults to all
1346         NOTES
1347
1348 __PACKAGE__->register_method(
1349         method  => "user_transactions",
1350         api_name        => "open-ils.actor.user.transactions.count",
1351         notes           => <<"  NOTES");
1352         Returns an object/hash of transaction, circ, title where transaction = an open 
1353         user transactions (mbts objects), circ is the attached circluation, and title
1354         is the title the circ points to
1355         Params are login_session, user_id
1356         Optional third parameter is the transactions type.  defaults to all
1357         NOTES
1358
1359 __PACKAGE__->register_method(
1360         method  => "user_transactions",
1361         api_name        => "open-ils.actor.user.transactions.have_charge.count",
1362         notes           => <<"  NOTES");
1363         Returns an object/hash of transaction, circ, title where transaction = an open 
1364         user transactions that has an initial charge (mbts objects), circ is the 
1365         attached circluation, and title is the title the circ points to
1366         Params are login_session, user_id
1367         Optional third parameter is the transactions type.  defaults to all
1368         NOTES
1369
1370 __PACKAGE__->register_method(
1371         method  => "user_transactions",
1372         api_name        => "open-ils.actor.user.transactions.have_balance.count",
1373         notes           => <<"  NOTES");
1374         Returns an object/hash of transaction, circ, title where transaction = an open 
1375         user transaction that has a balance (mbts objects), circ is the attached 
1376         circluation, and title is the title the circ points to
1377         Params are login_session, user_id
1378         Optional third parameter is the transaction type.  defaults to all
1379         NOTES
1380
1381 __PACKAGE__->register_method(
1382         method  => "user_transactions",
1383         api_name        => "open-ils.actor.user.transactions.have_balance.total",
1384         notes           => <<"  NOTES");
1385         Returns an object/hash of transaction, circ, title where transaction = an open 
1386         user transaction that has a balance (mbts objects), circ is the attached 
1387         circluation, and title is the title the circ points to
1388         Params are login_session, user_id
1389         Optional third parameter is the transaction type.  defaults to all
1390         NOTES
1391
1392
1393
1394 sub user_transactions {
1395         my( $self, $client, $login_session, $user_id, $type ) = @_;
1396
1397         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1398                 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1399         return $evt if $evt;
1400         
1401         my $api = $self->api_name();
1402         my $trans;
1403         my @xact;
1404
1405         if(defined($type)) { @xact = (xact_type =>  $type); 
1406
1407         } else { @xact = (); }
1408
1409         if($api =~ /have_charge/o) {
1410
1411                 $trans = $apputils->simple_scalar_request( 
1412                         "open-ils.storage",
1413                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1414                         { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1415
1416         } elsif($api =~ /have_balance/o) {
1417
1418                 $trans =  $apputils->simple_scalar_request( 
1419                         "open-ils.storage",
1420                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1421                         { usr => $user_id, balance_owed => { "<>" => 0 }, @xact });
1422
1423         } else {
1424
1425                 $trans =  $apputils->simple_scalar_request( 
1426                         "open-ils.storage",
1427                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1428                         { usr => $user_id, @xact });
1429         }
1430
1431         if($api =~ /total/o) { 
1432                 my $total = 0.0;
1433                 for my $t (@$trans) {
1434                         $total += $t->balance_owed;
1435                 }
1436
1437                 $logger->debug("Total balance owed by user $user_id: $total");
1438                 return $total;
1439         }
1440
1441         if($api =~ /count/o) { return scalar @$trans; }
1442         if($api !~ /fleshed/o) { return $trans; }
1443
1444         my @resp;
1445         for my $t (@$trans) {
1446                         
1447                 if( $t->xact_type ne 'circulation' ) {
1448                         push @resp, {transaction => $t};
1449                         next;
1450                 }
1451
1452                 my $circ = $apputils->simple_scalar_request(
1453                                 "open-ils.storage",
1454                                 "open-ils.storage.direct.action.circulation.retrieve",
1455                                 $t->id );
1456
1457                 next unless $circ;
1458
1459                 my $title = $apputils->simple_scalar_request(
1460                         "open-ils.storage", 
1461                         "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1462                         $circ->target_copy );
1463
1464                 next unless $title;
1465
1466                 my $u = OpenILS::Utils::ModsParser->new();
1467                 $u->start_mods_batch($title->marc());
1468                 my $mods = $u->finish_mods_batch();
1469
1470                 push @resp, {transaction => $t, circ => $circ, record => $mods };
1471
1472         }
1473
1474         return \@resp; 
1475
1476
1477
1478 __PACKAGE__->register_method(
1479         method  => "user_transaction_retrieve",
1480         api_name        => "open-ils.actor.user.transaction.fleshed.retrieve",
1481         argc            => 1,
1482         notes           => <<"  NOTES");
1483         Returns a fleshedtransaction record
1484         NOTES
1485 __PACKAGE__->register_method(
1486         method  => "user_transaction_retrieve",
1487         api_name        => "open-ils.actor.user.transaction.retrieve",
1488         argc            => 1,
1489         notes           => <<"  NOTES");
1490         Returns a transaction record
1491         NOTES
1492 sub user_transaction_retrieve {
1493         my( $self, $client, $login_session, $bill_id ) = @_;
1494
1495         my $trans = $apputils->simple_scalar_request( 
1496                 "open-ils.storage",
1497                 "open-ils.storage.direct.money.billable_transaction_summary.retrieve",
1498                 $bill_id
1499         );
1500
1501         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1502                 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1503         return $evt if $evt;
1504         
1505         my $api = $self->api_name();
1506         if($api !~ /fleshed/o) { return $trans; }
1507
1508         if( $trans->xact_type ne 'circulation' ) {
1509                 $logger->debug("Returning non-circ transaction");
1510                 return {transaction => $trans};
1511         }
1512
1513         my $circ = $apputils->simple_scalar_request(
1514                         "open-ils.storage",
1515                         "open-ils.storage.direct.action.circulation.retrieve",
1516                         $trans->id );
1517
1518         return {transaction => $trans} unless $circ;
1519         $logger->debug("Found the circ transaction");
1520
1521         my $title = $apputils->simple_scalar_request(
1522                 "open-ils.storage", 
1523                 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1524                 $circ->target_copy );
1525
1526         return {transaction => $trans, circ => $circ } unless $title;
1527         $logger->debug("Found the circ title");
1528
1529         my $mods;
1530         try {
1531                 my $u = OpenILS::Utils::ModsParser->new();
1532                 $u->start_mods_batch($title->marc());
1533                 $mods = $u->finish_mods_batch();
1534         } otherwise {
1535                 if ($title->id == -1) {
1536                         my $copy = $apputils->simple_scalar_request(
1537                                 "open-ils.storage",
1538                                 "open-ils.storage.direct.asset.copy.retrieve",
1539                                 $circ->target_copy );
1540
1541                         $mods = new Fieldmapper::metabib::virtual_record;
1542                         $mods->doc_id(-1);
1543                         $mods->title($copy->dummy_title);
1544                         $mods->author($copy->dummy_author);
1545                 }
1546         };
1547
1548         $logger->debug("MODSized the circ title");
1549
1550         return {transaction => $trans, circ => $circ, record => $mods };
1551 }
1552
1553
1554 __PACKAGE__->register_method(
1555         method  => "hold_request_count",
1556         api_name        => "open-ils.actor.user.hold_requests.count",
1557         argc            => 1,
1558         notes           => <<"  NOTES");
1559         Returns hold ready/total counts
1560         NOTES
1561 sub hold_request_count {
1562         my( $self, $client, $login_session, $userid ) = @_;
1563
1564         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1565                 $login_session, $userid, 'VIEW_HOLD' );
1566         return $evt if $evt;
1567         
1568
1569         my $holds = $apputils->simple_scalar_request(
1570                         "open-ils.storage",
1571                         "open-ils.storage.direct.action.hold_request.search_where.atomic",
1572                         { usr => $userid,
1573                           fulfillment_time => {"=" => undef } }
1574         );
1575
1576         my @ready;
1577         for my $h (@$holds) {
1578                 next unless $h->capture_time;
1579
1580                 my $copy = $apputils->simple_scalar_request(
1581                         "open-ils.storage",
1582                         "open-ils.storage.direct.asset.copy.retrieve",
1583                         $h->current_copy
1584                 );
1585
1586                 if ($copy->status == 8) {
1587                         push @ready, $h;
1588                 }
1589         }
1590
1591         return { total => scalar(@$holds), ready => scalar(@ready) };
1592 }
1593
1594
1595 __PACKAGE__->register_method(
1596         method  => "checkedout_count",
1597         api_name        => "open-ils.actor.user.checked_out.count",
1598         argc            => 1,
1599         notes           => <<"  NOTES");
1600         Returns a transaction record
1601         NOTES
1602 sub checkedout_count {
1603         my( $self, $client, $login_session, $userid ) = @_;
1604
1605         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1606                 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1607         return $evt if $evt;
1608         
1609
1610         my $circs = $apputils->simple_scalar_request(
1611                         "open-ils.storage",
1612                         "open-ils.storage.direct.action.circulation.search_where.atomic",
1613                         { usr => $userid,
1614                           checkin_time => {"=" => undef } }
1615         );
1616
1617         my $parser = DateTime::Format::ISO8601->new;
1618
1619         my (@out,@overdue);
1620         for my $c (@$circs) {
1621                 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1622                 my $due = $due_dt->epoch;
1623
1624                 if ($due < DateTime->today->epoch) {
1625                         push @overdue, $c;
1626                 }
1627         }
1628
1629         return { total => scalar(@$circs), overdue => scalar(@overdue) };
1630 }
1631
1632 __PACKAGE__->register_method(
1633         method  => "user_transaction_history",
1634         api_name        => "open-ils.actor.user.transactions.history",
1635         argc            => 1,
1636         notes           => <<"  NOTES");
1637         Returns a list of billable transaction ids for a user, optionally by type
1638         NOTES
1639 __PACKAGE__->register_method(
1640         method  => "user_transaction_history",
1641         api_name        => "open-ils.actor.user.transactions.history.have_charge",
1642         argc            => 1,
1643         notes           => <<"  NOTES");
1644         Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1645         NOTES
1646 sub user_transaction_history {
1647         my( $self, $client, $login_session, $user_id, $type ) = @_;
1648
1649         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1650                 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1651         return $evt if $evt;
1652         
1653         my $api = $self->api_name();
1654         my @xact;
1655         my @charge;
1656
1657         @xact = (xact_type =>  $type) if(defined($type));
1658         @charge = (total_owed => { ">" => 0}) if($api =~ /have_charge/);
1659
1660         my $trans = $apputils->simple_scalar_request( 
1661                 "open-ils.storage",
1662                 "open-ils.storage.direct.money.billable_transaction_summary.search_where.atomic",
1663                 { usr => $user_id, @xact, @charge }, { order_by => 'xact_start DESC' });
1664
1665         return [ map { $_->id } @$trans ];
1666 }
1667
1668
1669 __PACKAGE__->register_method(
1670         method  => "user_perms",
1671         api_name        => "open-ils.actor.permissions.user_perms.retrieve",
1672         argc            => 1,
1673         notes           => <<"  NOTES");
1674         Returns a list of permissions
1675         NOTES
1676 sub user_perms {
1677         my( $self, $client, $authtoken, $user ) = @_;
1678
1679         my( $staff, $evt ) = $apputils->checkses($authtoken);
1680         return $evt if $evt;
1681
1682         $user ||= $staff->id;
1683
1684         if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1685                 return $evt;
1686         }
1687
1688         return $apputils->simple_scalar_request(
1689                 "open-ils.storage",
1690                 "open-ils.storage.permission.user_perms.atomic",
1691                 $user);
1692 }
1693
1694 __PACKAGE__->register_method(
1695         method  => "retrieve_perms",
1696         api_name        => "open-ils.actor.permissions.retrieve",
1697         notes           => <<"  NOTES");
1698         Returns a list of permissions
1699         NOTES
1700 sub retrieve_perms {
1701         my( $self, $client ) = @_;
1702         return $apputils->simple_scalar_request(
1703                 "open-ils.storage",
1704                 "open-ils.storage.direct.permission.perm_list.retrieve.all.atomic");
1705 }
1706
1707 __PACKAGE__->register_method(
1708         method  => "retrieve_groups",
1709         api_name        => "open-ils.actor.groups.retrieve",
1710         notes           => <<"  NOTES");
1711         Returns a list of user groupss
1712         NOTES
1713 sub retrieve_groups {
1714         my( $self, $client ) = @_;
1715         return $apputils->simple_scalar_request(
1716                 "open-ils.storage",
1717                 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1718 }
1719
1720 __PACKAGE__->register_method(
1721         method  => "retrieve_org_address",
1722         api_name        => "open-ils.actor.org_unit.address.retrieve",
1723         notes           => <<'  NOTES');
1724         Returns an org_unit address by ID
1725         @param An org_address ID
1726         NOTES
1727 sub retrieve_org_address {
1728         my( $self, $client, $id ) = @_;
1729         return $apputils->simple_scalar_request(
1730                 "open-ils.storage",
1731                 "open-ils.storage.direct.actor.org_address.retrieve",
1732                 $id
1733         );
1734 }
1735
1736 __PACKAGE__->register_method(
1737         method  => "retrieve_groups_tree",
1738         api_name        => "open-ils.actor.groups.tree.retrieve",
1739         notes           => <<"  NOTES");
1740         Returns a list of user groups
1741         NOTES
1742 sub retrieve_groups_tree {
1743         my( $self, $client ) = @_;
1744         my $groups = $apputils->simple_scalar_request(
1745                 "open-ils.storage",
1746                 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1747         return $self->build_group_tree($groups);        
1748 }
1749
1750
1751 # turns an org list into an org tree
1752 sub build_group_tree {
1753
1754         my( $self, $grplist) = @_;
1755
1756         return $grplist unless ( 
1757                         ref($grplist) and @$grplist > 1 );
1758
1759         my @list = sort { $a->name cmp $b->name } @$grplist;
1760
1761         my $root;
1762         for my $grp (@list) {
1763
1764                 if ($grp and !defined($grp->parent)) {
1765                         $root = $grp;
1766                         next;
1767                 }
1768                 my ($parent) = grep { $_->id == $grp->parent} @list;
1769
1770                 $parent->children([]) unless defined($parent->children); 
1771                 push( @{$parent->children}, $grp );
1772         }
1773
1774         return $root;
1775
1776 }
1777
1778
1779 __PACKAGE__->register_method(
1780         method  => "add_user_to_groups",
1781         api_name        => "open-ils.actor.user.set_groups",
1782         notes           => <<"  NOTES");
1783         Adds a user to one or more permission groups
1784         NOTES
1785
1786 sub add_user_to_groups {
1787         my( $self, $client, $authtoken, $userid, $groups ) = @_;
1788
1789         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1790                 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
1791         return $evt if $evt;
1792
1793         ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1794                 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
1795         return $evt if $evt;
1796
1797         $apputils->simplereq(
1798                 'open-ils.storage',
1799                 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
1800                 
1801         for my $group (@$groups) {
1802                 my $link = Fieldmapper::permission::usr_grp_map->new;
1803                 $link->grp($group);
1804                 $link->usr($userid);
1805
1806                 my $id = $apputils->simplereq(
1807                         'open-ils.storage',
1808                         'open-ils.storage.direct.permission.usr_grp_map.create', $link );
1809         }
1810
1811         return 1;
1812 }
1813
1814 __PACKAGE__->register_method(
1815         method  => "get_user_perm_groups",
1816         api_name        => "open-ils.actor.user.get_groups",
1817         notes           => <<"  NOTES");
1818         Retrieve a user's permission groups.
1819         NOTES
1820
1821
1822 sub get_user_perm_groups {
1823         my( $self, $client, $authtoken, $userid ) = @_;
1824
1825         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1826                 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
1827         return $evt if $evt;
1828
1829         return $apputils->simplereq(
1830                 'open-ils.storage',
1831                 'open-ils.storage.direct.permission.usr_grp_map.search.usr.atomic', $userid );
1832 }       
1833
1834
1835
1836 __PACKAGE__->register_method (
1837         method          => 'register_workstation',
1838         api_name                => 'open-ils.actor.workstation.register.override',
1839         signature       => q/@see open-ils.actor.workstation.register/);
1840
1841 __PACKAGE__->register_method (
1842         method          => 'register_workstation',
1843         api_name                => 'open-ils.actor.workstation.register',
1844         signature       => q/
1845                 Registers a new workstion in the system
1846                 @param authtoken The login session key
1847                 @param name The name of the workstation id
1848                 @param owner The org unit that owns this workstation
1849                 @return The workstation id on success, WORKSTATION_NAME_EXISTS
1850                 if the name is already in use.
1851         /);
1852
1853 sub _register_workstation {
1854         my( $self, $connection, $authtoken, $name, $owner ) = @_;
1855         my( $requestor, $evt ) = $U->checkses($authtoken);
1856         return $evt if $evt;
1857         $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
1858         return $evt if $evt;
1859
1860         my $ws = $U->storagereq(
1861                 'open-ils.storage.direct.actor.workstation.search.name', $name );
1862         return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
1863
1864         $ws = Fieldmapper::actor::workstation->new;
1865         $ws->owning_lib($owner);
1866         $ws->name($name);
1867
1868         my $id = $U->storagereq(
1869                 'open-ils.storage.direct.actor.workstation.create', $ws );
1870         return $U->DB_UPDATE_FAILED($ws) unless $id;
1871
1872         $ws->id($id);
1873         return $ws->id();
1874 }
1875
1876 sub register_workstation {
1877         my( $self, $conn, $authtoken, $name, $owner ) = @_;
1878
1879         my $e = OpenILS::Utils::Editor->new(authtoken=>$authtoken, xact=>1); 
1880         return $e->event unless $e->checkauth;
1881         return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
1882         my $existing = $e->search_actor_workstation({name => $name});
1883
1884         if( @$existing ) {
1885                 if( $self->api_name =~ /override/o ) {
1886                         return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
1887                         return $e->event unless $e->delete_actor_workstation($$existing[0]);
1888                 } else {
1889                         return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
1890                 }
1891         }
1892
1893         my $ws = Fieldmapper::actor::workstation->new;
1894         $ws->owning_lib($owner);
1895         $ws->name($name);
1896         $e->create_actor_workstation($ws) or return $e->event;
1897         $e->finish;
1898         return $ws->id; # note: editor sets the id on the new object for us
1899 }
1900
1901
1902 __PACKAGE__->register_method (
1903         method          => 'fetch_patron_note',
1904         api_name                => 'open-ils.actor.note.retrieve.all',
1905         signature       => q/
1906                 Returns a list of notes for a given user
1907                 Requestor must have VIEW_USER permission if pub==false and
1908                 @param authtoken The login session key
1909                 @param args Hash of params including
1910                         patronid : the patron's id
1911                         pub : true if retrieving only public notes
1912         /
1913 );
1914
1915 sub fetch_patron_note {
1916         my( $self, $conn, $authtoken, $args ) = @_;
1917         my $patronid = $$args{patronid};
1918
1919         my($reqr, $evt) = $U->checkses($authtoken);
1920
1921         my $patron;
1922         ($patron, $evt) = $U->fetch_user($patronid);
1923         return $evt if $evt;
1924
1925         if($$args{pub}) {
1926                 if( $patronid ne $reqr->id ) {
1927                         $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
1928                         return $evt if $evt;
1929                 }
1930                 return $U->storagereq(
1931                         'open-ils.storage.direct.actor.usr_note.search_where.atomic', 
1932                         { usr => $patronid, pub => 't' } );
1933         }
1934
1935         $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
1936         return $evt if $evt;
1937
1938         return $U->storagereq(
1939                 'open-ils.storage.direct.actor.usr_note.search.usr.atomic', $patronid );
1940 }
1941
1942 __PACKAGE__->register_method (
1943         method          => 'create_user_note',
1944         api_name                => 'open-ils.actor.note.create',
1945         signature       => q/
1946                 Creates a new note for the given user
1947                 @param authtoken The login session key
1948                 @param note The note object
1949         /
1950 );
1951 sub create_user_note {
1952         my( $self, $conn, $authtoken, $note ) = @_;
1953         my( $reqr, $patron, $evt ) = 
1954                 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
1955         return $evt if $evt;
1956         $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
1957
1958         $note->pub('f') unless $note->pub;
1959         $note->creator($reqr->id);
1960         my $id = $U->storagereq(
1961                 'open-ils.storage.direct.actor.usr_note.create', $note );
1962         return $U->DB_UPDATE_FAILED($note) unless $id;
1963         return $id;
1964 }
1965
1966
1967 __PACKAGE__->register_method (
1968         method          => 'delete_user_note',
1969         api_name                => 'open-ils.actor.note.delete',
1970         signature       => q/
1971                 Deletes a note for the given user
1972                 @param authtoken The login session key
1973                 @param noteid The note id
1974         /
1975 );
1976 sub delete_user_note {
1977         my( $self, $conn, $authtoken, $noteid ) = @_;
1978
1979         my $note = $U->storagereq(
1980                 'open-ils.storage.direct.actor.usr_note.retrieve', $noteid);
1981         return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
1982
1983         my( $reqr, $patron, $evt ) = 
1984                 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
1985         return $evt if $evt;
1986         $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
1987
1988         my $stat = $U->storagereq(
1989                 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
1990         return $U->DB_UPDATE_FAILED($note) unless defined $stat;
1991         return $stat;
1992 }
1993
1994
1995
1996 __PACKAGE__->register_method (
1997         method          => 'create_closed_date',
1998         api_name        => 'open-ils.actor.org_unit.closed_date.create',
1999         signature       => q/
2000                 Creates a new closing entry for the given org_unit
2001                 @param authtoken The login session key
2002                 @param note The closed_date object
2003         /
2004 );
2005 sub create_closed_date {
2006         my( $self, $conn, $authtoken, $cd ) = @_;
2007
2008         my( $user, $evt ) = $U->checkses($authtoken);
2009         return $evt if $evt;
2010
2011         $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2012         return $evt if $evt;
2013
2014         $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2015
2016         my $id = $U->storagereq(
2017                 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2018         return $U->DB_UPDATE_FAILED($cd) unless $id;
2019         return $id;
2020 }
2021
2022
2023 __PACKAGE__->register_method (
2024         method          => 'delete_closed_date',
2025         api_name        => 'open-ils.actor.org_unit.closed_date.delete',
2026         signature       => q/
2027                 Deletes a closing entry for the given org_unit
2028                 @param authtoken The login session key
2029                 @param noteid The close_date id
2030         /
2031 );
2032 sub delete_closed_date {
2033         my( $self, $conn, $authtoken, $cd ) = @_;
2034
2035         my( $user, $evt ) = $U->checkses($authtoken);
2036         return $evt if $evt;
2037
2038         my $cd_obj;
2039         ($cd_obj, $evt) = fetch_closed_date($cd);
2040         return $evt if $evt;
2041
2042         $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2043         return $evt if $evt;
2044
2045         $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2046
2047         my $stat = $U->storagereq(
2048                 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2049         return $U->DB_UPDATE_FAILED($cd) unless $stat;
2050         return $stat;
2051 }
2052
2053
2054 __PACKAGE__->register_method(
2055         method => 'usrname_exists',
2056         api_name        => 'open-ils.actor.username.exists',
2057         signature => q/
2058                 Returns 1 if the requested username exists, returns 0 otherwise
2059         /
2060 );
2061
2062 sub usrname_exists {
2063         my( $self, $conn, $usrname ) = @_;
2064         my $e = OpenILS::Utils::Editor->new;
2065         my $a = $e->search_actor_user({usrname => $usrname}, {idlist=>1});
2066         return 1 if $a and @$a;
2067         return 0;
2068 }
2069
2070
2071 1;
2072