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