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