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