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