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