]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Actor.pm
00a8a6c23d5abd054fcf33b5cb69462def0a4997
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Actor.pm
1 package OpenILS::Application::Actor;
2 use base qw/OpenSRF::Application/;
3 use strict; use warnings;
4 use Data::Dumper;
5 $Data::Dumper::Indent = 0;
6 use OpenILS::Event;
7
8 use Digest::MD5 qw(md5_hex);
9
10 use OpenSRF::EX qw(:try);
11 use OpenILS::Perm;
12
13 use OpenILS::Application::AppUtils;
14
15 use OpenILS::Utils::Fieldmapper;
16 use OpenILS::Utils::ModsParser;
17 use OpenSRF::Utils::Logger qw/$logger/;
18 use OpenSRF::Utils qw/:datetime/;
19
20 use OpenSRF::Utils::Cache;
21
22 use JSON;
23 use DateTime;
24 use DateTime::Format::ISO8601;
25 use OpenILS::Const qw/:const/;
26
27 use OpenILS::Application::Actor::Container;
28 use OpenILS::Application::Actor::ClosedDates;
29
30 use OpenILS::Utils::CStoreEditor qw/:funcs/;
31
32 use OpenILS::Application::Actor::UserGroups;
33 sub initialize {
34         OpenILS::Application::Actor::Container->initialize();
35         OpenILS::Application::Actor::UserGroups->initialize();
36         OpenILS::Application::Actor::ClosedDates->initialize();
37 }
38
39 my $apputils = "OpenILS::Application::AppUtils";
40 my $U = $apputils;
41
42 sub _d { warn "Patron:\n" . Dumper(shift()); }
43
44 my $cache;
45
46
47 my $set_user_settings;
48 my $set_ou_settings;
49
50 __PACKAGE__->register_method(
51         method  => "set_user_settings",
52         api_name        => "open-ils.actor.patron.settings.update",
53 );
54 sub set_user_settings {
55         my( $self, $client, $user_session, $uid, $settings ) = @_;
56         
57         $logger->debug("Setting user settings: $user_session, $uid, " . Dumper($settings));
58
59         my( $staff, $user, $evt ) = 
60                 $apputils->checkses_requestor( $user_session, $uid, 'UPDATE_USER' );    
61         return $evt if $evt;
62         
63         my @params = map { 
64                 [{ usr => $user->id, name => $_}, {value => $$settings{$_}}] } keys %$settings;
65                 
66         $_->[1]->{value} = JSON->perl2JSON($_->[1]->{value}) for @params;
67
68         $logger->activity("User " . $staff->id . " updating user $uid settings with: " . Dumper(\@params));
69
70         my $ses = $U->start_db_session();
71         my $stat = $ses->request(
72                 'open-ils.storage.direct.actor.user_setting.batch.merge', @params )->gather(1);
73         $U->commit_db_session($ses);
74
75         return $stat;
76 }
77
78
79
80 __PACKAGE__->register_method(
81         method  => "set_ou_settings",
82         api_name        => "open-ils.actor.org_unit.settings.update",
83 );
84 sub set_ou_settings {
85         my( $self, $client, $user_session, $ouid, $settings ) = @_;
86         
87         my( $staff, $evt ) = $apputils->checkses( $user_session );
88         return $evt if $evt;
89         $evt = $apputils->check_perms( $staff->id, $ouid, 'UPDATE_ORG_SETTING' );
90         return $evt if $evt;
91
92         my @params;
93         for my $set (keys %$settings) {
94
95                 my $json = JSON->perl2JSON($$settings{$set});
96                 $logger->activity("updating org_unit.setting: $ouid : $set : $json");
97
98                 push( @params, 
99                         { org_unit => $ouid, name => $set }, 
100                         { value => $json } );
101         }
102
103         my $ses = $U->start_db_session();
104         my $stat = $ses->request(
105                 'open-ils.storage.direct.actor.org_unit_setting.merge', @params )->gather(1);
106         $U->commit_db_session($ses);
107
108         return $stat;
109 }
110
111
112 my $fetch_user_settings;
113 my $fetch_ou_settings;
114
115 __PACKAGE__->register_method(
116         method  => "user_settings",
117         api_name        => "open-ils.actor.patron.settings.retrieve",
118 );
119 sub user_settings {
120         my( $self, $client, $user_session, $uid ) = @_;
121         
122         my( $staff, $user, $evt ) = 
123                 $apputils->checkses_requestor( $user_session, $uid, 'VIEW_USER' );
124         return $evt if $evt;
125
126         $logger->debug("User " . $staff->id . " fetching user $uid\n");
127         my $s = $apputils->simplereq(
128                 'open-ils.cstore',
129                 'open-ils.cstore.direct.actor.user_setting.search.atomic', { usr => $uid } );
130
131         return { map { ( $_->name => JSON->JSON2perl($_->value) ) } @$s };
132 }
133
134
135
136 __PACKAGE__->register_method(
137         method  => "ou_settings",
138         api_name        => "open-ils.actor.org_unit.settings.retrieve",
139 );
140 sub ou_settings {
141         my( $self, $client, $ouid ) = @_;
142         
143         $logger->info("Fetching org unit settings for org $ouid");
144
145         my $s = $apputils->simplereq(
146                 'open-ils.cstore',
147                 'open-ils.cstore.direct.actor.org_unit_setting.search.atomic', {org_unit => $ouid});
148
149         return { map { ( $_->name => JSON->JSON2perl($_->value) ) } @$s };
150 }
151
152 __PACKAGE__->register_method (
153         method          => "ou_setting_delete",
154         api_name                => 'open-ils.actor.org_setting.delete',
155         signature       => q/
156                 Deletes a specific org unit setting for a specific location
157                 @param authtoken The login session key
158                 @param orgid The org unit whose setting we're changing
159                 @param setting The name of the setting to delete
160                 @return True value on success.
161         /
162 );
163
164 sub ou_setting_delete {
165         my( $self, $conn, $authtoken, $orgid, $setting ) = @_;
166         my( $reqr, $evt) = $U->checkses($authtoken);
167         return $evt if $evt;
168         $evt = $U->check_perms($reqr->id, $orgid, 'UPDATE_ORG_SETTING');
169         return $evt if $evt;
170
171         my $id = $U->cstorereq(
172                 'open-ils.cstore.direct.actor.org_unit_setting.id_list', 
173                 { name => $setting, org_unit => $orgid } );
174
175         $logger->debug("Retrieved setting $id in org unit setting delete");
176
177         my $s = $U->cstorereq(
178                 'open-ils.cstore.direct.actor.org_unit_setting.delete', $id );
179
180         $logger->activity("User ".$reqr->id." deleted org unit setting $id") if $s;
181         return $s;
182 }
183
184
185
186 __PACKAGE__->register_method(
187         method  => "update_patron",
188         api_name        => "open-ils.actor.patron.update",);
189
190 sub update_patron {
191         my( $self, $client, $user_session, $patron ) = @_;
192
193         my $session = $apputils->start_db_session();
194         my $err = undef;
195
196         $logger->info("Creating new patron...") if $patron->isnew; 
197         $logger->info("Updating Patron: " . $patron->id) unless $patron->isnew;
198
199         my( $user_obj, $evt ) = $U->checkses($user_session);
200         return $evt if $evt;
201
202         # XXX does this user have permission to add/create users.  Granularity?
203         # $new_patron is the patron in progress.  $patron is the original patron
204         # passed in with the method.  new_patron will change as the components
205         # of patron are added/updated.
206
207         my $new_patron;
208
209         # unflesh the real items on the patron
210         $patron->card( $patron->card->id ) if(ref($patron->card));
211         $patron->billing_address( $patron->billing_address->id ) 
212                 if(ref($patron->billing_address));
213         $patron->mailing_address( $patron->mailing_address->id ) 
214                 if(ref($patron->mailing_address));
215
216         # create/update the patron first so we can use his id
217         if($patron->isnew()) {
218                 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
219                 return $evt if $evt;
220         } else { $new_patron = $patron; }
221
222         ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
223         return $evt if $evt;
224
225         ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
226         return $evt if $evt;
227
228         ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
229         return $evt if $evt;
230
231         # re-update the patron if anything has happened to him during this process
232         if($new_patron->ischanged()) {
233                 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
234                 return $evt if $evt;
235         }
236
237         #$session = OpenSRF::AppSession->create("open-ils.storage");  # why did i put this here?
238
239         ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
240         return $evt if $evt;
241
242         ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
243         return $evt if $evt;
244
245         ($new_patron, $evt) = _create_standing_penalties($session, $user_session, $patron, $new_patron, $user_obj);
246         return $evt if $evt;
247
248         $logger->activity("user ".$user_obj->id." updating/creating  user ".$new_patron->id);
249
250         my $opatron;
251         if(!$patron->isnew) {
252                 $opatron = new_editor()->retrieve_actor_user($new_patron->id);
253         }
254
255         $apputils->commit_db_session($session);
256         my $fuser =  flesh_user($new_patron->id());
257
258         if( $opatron ) {
259                 # Log the new and old patron for investigation
260                 $logger->info("$user_session updating patron object. orig patron object = ".
261                         JSON->perl2JSON($opatron). " |||| new patron = ".JSON->perl2JSON($fuser));
262         }
263
264
265         return $fuser;
266 }
267
268
269 sub flesh_user {
270         my $id = shift;
271         return new_flesh_user($id, [
272                 "cards",
273                 "card",
274                 "standing_penalties",
275                 "addresses",
276                 "billing_address",
277                 "mailing_address",
278                 "stat_cat_entries" ] );
279 }
280
281
282
283
284
285
286 # clone and clear stuff that would break the database
287 sub _clone_patron {
288         my $patron = shift;
289
290         my $new_patron = $patron->clone;
291         # clear these
292         $new_patron->clear_billing_address();
293         $new_patron->clear_mailing_address();
294         $new_patron->clear_addresses();
295         $new_patron->clear_card();
296         $new_patron->clear_cards();
297         $new_patron->clear_id();
298         $new_patron->clear_isnew();
299         $new_patron->clear_ischanged();
300         $new_patron->clear_isdeleted();
301         $new_patron->clear_stat_cat_entries();
302         $new_patron->clear_permissions();
303         $new_patron->clear_standing_penalties();
304
305         return $new_patron;
306 }
307
308
309 sub _add_patron {
310
311         my $session             = shift;
312         my $patron              = shift;
313         my $user_obj    = shift;
314
315         my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
316         return (undef, $evt) if $evt;
317
318         my $ex = $session->request(
319                 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
320         if( $ex and @$ex ) {
321                 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
322         }
323
324         $logger->info("Creating new user in the DB with username: ".$patron->usrname());
325
326         my $id = $session->request(
327                 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
328         return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
329
330         $logger->info("Successfully created new user [$id] in DB");
331
332         return ( $session->request( 
333                 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
334 }
335
336
337 sub _update_patron {
338         my( $session, $patron, $user_obj, $noperm) = @_;
339
340         $logger->info("Updating patron ".$patron->id." in DB");
341
342         my $evt;
343
344         if(!$noperm) {
345                 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
346                 return (undef, $evt) if $evt;
347         }
348
349         # update the password by itself to avoid the password protection magic
350         if( $patron->passwd ) {
351                 my $s = $session->request(
352                         'open-ils.storage.direct.actor.user.remote_update',
353                         {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
354                 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
355                 $patron->clear_passwd;
356         }
357
358         if(!$patron->ident_type) {
359                 $patron->clear_ident_type;
360                 $patron->clear_ident_value;
361         }
362
363         my $stat = $session->request(
364                 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
365         return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
366
367         return ($patron);
368 }
369
370 sub _check_dup_ident {
371         my( $session, $patron ) = @_;
372
373         return undef unless $patron->ident_value;
374
375         my $search = {
376                 ident_type      => $patron->ident_type, 
377                 ident_value => $patron->ident_value,
378         };
379
380         $logger->debug("patron update searching for dup ident values: " . 
381                 $patron->ident_type . ':' . $patron->ident_value);
382
383         $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
384
385         my $dups = $session->request(
386                 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
387
388
389         return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
390                 if $dups and @$dups;
391
392         return undef;
393 }
394
395
396 sub _add_update_addresses {
397
398         my $session = shift;
399         my $patron = shift;
400         my $new_patron = shift;
401
402         my $evt;
403
404         my $current_id; # id of the address before creation
405
406         for my $address (@{$patron->addresses()}) {
407
408                 next unless ref $address;
409                 $current_id = $address->id();
410
411                 if( $patron->billing_address() and
412                         $patron->billing_address() == $current_id ) {
413                         $logger->info("setting billing addr to $current_id");
414                         $new_patron->billing_address($address->id());
415                         $new_patron->ischanged(1);
416                 }
417         
418                 if( $patron->mailing_address() and
419                         $patron->mailing_address() == $current_id ) {
420                         $new_patron->mailing_address($address->id());
421                         $logger->info("setting mailing addr to $current_id");
422                         $new_patron->ischanged(1);
423                 }
424
425
426                 if($address->isnew()) {
427
428                         $address->usr($new_patron->id());
429
430                         ($address, $evt) = _add_address($session,$address);
431                         return (undef, $evt) if $evt;
432
433                         # we need to get the new id
434                         if( $patron->billing_address() and 
435                                         $patron->billing_address() == $current_id ) {
436                                 $new_patron->billing_address($address->id());
437                                 $logger->info("setting billing addr to $current_id");
438                                 $new_patron->ischanged(1);
439                         }
440
441                         if( $patron->mailing_address() and
442                                         $patron->mailing_address() == $current_id ) {
443                                 $new_patron->mailing_address($address->id());
444                                 $logger->info("setting mailing addr to $current_id");
445                                 $new_patron->ischanged(1);
446                         }
447
448                 } elsif($address->ischanged() ) {
449
450                         ($address, $evt) = _update_address($session, $address);
451                         return (undef, $evt) if $evt;
452
453                 } elsif($address->isdeleted() ) {
454
455                         if( $address->id() == $new_patron->mailing_address() ) {
456                                 $new_patron->clear_mailing_address();
457                                 ($new_patron, $evt) = _update_patron($session, $new_patron);
458                                 return (undef, $evt) if $evt;
459                         }
460
461                         if( $address->id() == $new_patron->billing_address() ) {
462                                 $new_patron->clear_billing_address();
463                                 ($new_patron, $evt) = _update_patron($session, $new_patron);
464                                 return (undef, $evt) if $evt;
465                         }
466
467                         $evt = _delete_address($session, $address);
468                         return (undef, $evt) if $evt;
469                 } 
470         }
471
472         return ( $new_patron, undef );
473 }
474
475
476 # adds an address to the db and returns the address with new id
477 sub _add_address {
478         my($session, $address) = @_;
479         $address->clear_id();
480
481         $logger->info("Creating new address at street ".$address->street1);
482
483         # put the address into the database
484         my $id = $session->request(
485                 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
486         return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
487
488         $address->id( $id );
489         return ($address, undef);
490 }
491
492
493 sub _update_address {
494         my( $session, $address ) = @_;
495
496         $logger->info("Updating address ".$address->id." in the DB");
497
498         my $stat = $session->request(
499                 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
500
501         return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
502         return ($address, undef);
503 }
504
505
506
507 sub _add_update_cards {
508
509         my $session = shift;
510         my $patron = shift;
511         my $new_patron = shift;
512
513         my $evt;
514
515         my $virtual_id; #id of the card before creation
516         for my $card (@{$patron->cards()}) {
517
518                 $card->usr($new_patron->id());
519
520                 if(ref($card) and $card->isnew()) {
521
522                         $virtual_id = $card->id();
523                         ( $card, $evt ) = _add_card($session,$card);
524                         return (undef, $evt) if $evt;
525
526                         #if(ref($patron->card)) { $patron->card($patron->card->id); }
527                         if($patron->card() == $virtual_id) {
528                                 $new_patron->card($card->id());
529                                 $new_patron->ischanged(1);
530                         }
531
532                 } elsif( ref($card) and $card->ischanged() ) {
533                         $evt = _update_card($session, $card);
534                         return (undef, $evt) if $evt;
535                 }
536         }
537
538         return ( $new_patron, undef );
539 }
540
541
542 # adds an card to the db and returns the card with new id
543 sub _add_card {
544         my( $session, $card ) = @_;
545         $card->clear_id();
546
547         $logger->info("Adding new patron card ".$card->barcode);
548
549         my $id = $session->request(
550                 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
551         return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
552         $logger->info("Successfully created patron card $id");
553
554         $card->id($id);
555         return ( $card, undef );
556 }
557
558
559 # returns event on error.  returns undef otherwise
560 sub _update_card {
561         my( $session, $card ) = @_;
562         $logger->info("Updating patron card ".$card->id);
563
564         my $stat = $session->request(
565                 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
566         return $U->DB_UPDATE_FAILED($card) unless defined($stat);
567         return undef;
568 }
569
570
571
572
573 # returns event on error.  returns undef otherwise
574 sub _delete_address {
575         my( $session, $address ) = @_;
576
577         $logger->info("Deleting address ".$address->id." from DB");
578
579         my $stat = $session->request(
580                 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
581
582         return $U->DB_UPDATE_FAILED($address) unless defined($stat);
583         return undef;
584 }
585
586
587
588 sub _add_survey_responses {
589         my ($session, $patron, $new_patron) = @_;
590
591         $logger->info( "Updating survey responses for patron ".$new_patron->id );
592
593         my $responses = $patron->survey_responses;
594
595         if($responses) {
596
597                 $_->usr($new_patron->id) for (@$responses);
598
599                 my $evt = $U->simplereq( "open-ils.circ", 
600                         "open-ils.circ.survey.submit.user_id", $responses );
601
602                 return (undef, $evt) if defined($U->event_code($evt));
603
604         }
605
606         return ( $new_patron, undef );
607 }
608
609
610 sub _create_stat_maps {
611
612         my($session, $user_session, $patron, $new_patron) = @_;
613
614         my $maps = $patron->stat_cat_entries();
615
616         for my $map (@$maps) {
617
618                 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
619
620                 if ($map->isdeleted()) {
621                         $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
622
623                 } elsif ($map->isnew()) {
624                         $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
625                         $map->clear_id;
626                 }
627
628
629                 $map->target_usr($new_patron->id);
630
631                 #warn "
632                 $logger->info("Updating stat entry with method $method and map $map");
633
634                 my $stat = $session->request($method, $map)->gather(1);
635                 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
636
637         }
638
639         return ($new_patron, undef);
640 }
641
642 sub _create_perm_maps {
643
644         my($session, $user_session, $patron, $new_patron) = @_;
645
646         my $maps = $patron->permissions;
647
648         for my $map (@$maps) {
649
650                 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
651                 if ($map->isdeleted()) {
652                         $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
653                 } elsif ($map->isnew()) {
654                         $method = "open-ils.storage.direct.permission.usr_perm_map.create";
655                         $map->clear_id;
656                 }
657
658
659                 $map->usr($new_patron->id);
660
661                 #warn( "Updating permissions with method $method and session $user_session and map $map" );
662                 $logger->info( "Updating permissions with method $method and map $map" );
663
664                 my $stat = $session->request($method, $map)->gather(1);
665                 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
666
667         }
668
669         return ($new_patron, undef);
670 }
671
672
673 sub _create_standing_penalties {
674
675         my($session, $user_session, $patron, $new_patron) = @_;
676
677         my $maps = $patron->standing_penalties;
678         my $method;
679
680         for my $map (@$maps) {
681
682                 if ($map->isdeleted()) {
683                         $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
684                 } elsif ($map->isnew()) {
685                         $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
686                         $map->clear_id;
687                 } else {
688                         next;
689                 }
690
691                 $map->usr($new_patron->id);
692
693                 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
694
695                 my $stat = $session->request($method, $map)->gather(1);
696                 return (undef, $U->DB_UPDATE_FAILED($map)) unless $stat;
697         }
698
699         return ($new_patron, undef);
700 }
701
702
703
704 __PACKAGE__->register_method(
705         method  => "search_username",
706         api_name        => "open-ils.actor.user.search.username",
707 );
708
709 sub search_username {
710         my($self, $client, $username) = @_;
711         my $users = OpenILS::Application::AppUtils->simple_scalar_request(
712                         "open-ils.cstore", 
713                         "open-ils.cstore.direct.actor.user.search.atomic",
714                         { usrname => $username }
715         );
716         return $users;
717 }
718
719
720
721
722 __PACKAGE__->register_method(
723         method  => "user_retrieve_by_barcode",
724         api_name        => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
725
726 sub user_retrieve_by_barcode {
727         my($self, $client, $user_session, $barcode) = @_;
728
729         $logger->debug("Searching for user with barcode $barcode");
730         my ($user_obj, $evt) = $apputils->checkses($user_session);
731         return $evt if $evt;
732
733         my $card = OpenILS::Application::AppUtils->simple_scalar_request(
734                         "open-ils.cstore", 
735                         "open-ils.cstore.direct.actor.card.search.atomic",
736                         { barcode => $barcode }
737         );
738
739         if(!$card || !$card->[0]) {
740                 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
741         }
742
743         $card = $card->[0];
744         my $user = flesh_user($card->usr());
745
746         $evt = $U->check_perms($user_obj->id, $user->home_ou, 'VIEW_USER');
747         return $evt if $evt;
748
749         if(!$user) { return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); }
750         return $user;
751
752 }
753
754
755
756 __PACKAGE__->register_method(
757         method  => "get_user_by_id",
758         api_name        => "open-ils.actor.user.retrieve",);
759
760 sub get_user_by_id {
761         my ($self, $client, $auth, $id) = @_;
762         my $e = new_editor(authtoken=>$auth);
763         return $e->event unless $e->checkauth;
764         my $user = $e->retrieve_actor_user($id)
765                 or return $e->event;
766         return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);       
767         return $user;
768 }
769
770
771
772 __PACKAGE__->register_method(
773         method  => "get_org_types",
774         api_name        => "open-ils.actor.org_types.retrieve",);
775
776 my $org_types;
777 sub get_org_types {
778         my($self, $client) = @_;
779         return $org_types if $org_types;
780         return $org_types = new_editor()->retrieve_all_actor_org_unit_type();
781 }
782
783
784
785 __PACKAGE__->register_method(
786         method  => "get_user_ident_types",
787         api_name        => "open-ils.actor.user.ident_types.retrieve",
788 );
789 my $ident_types;
790 sub get_user_ident_types {
791         return $ident_types if $ident_types;
792         return $ident_types = 
793                 new_editor()->retrieve_all_config_identification_type();
794 }
795
796
797
798
799 __PACKAGE__->register_method(
800         method  => "get_org_unit",
801         api_name        => "open-ils.actor.org_unit.retrieve",
802 );
803
804 sub get_org_unit {
805         my( $self, $client, $user_session, $org_id ) = @_;
806         my $e = new_editor(authtoken => $user_session);
807         if(!$org_id) {
808                 return $e->event unless $e->checkauth;
809                 $org_id = $e->requestor->ws_ou;
810         }
811         my $o = $e->retrieve_actor_org_unit($org_id)
812                 or return $e->event;
813         return $o;
814 }
815
816 __PACKAGE__->register_method(
817         method  => "search_org_unit",
818         api_name        => "open-ils.actor.org_unit_list.search",
819 );
820
821 sub search_org_unit {
822
823         my( $self, $client, $field, $value ) = @_;
824
825         my $list = OpenILS::Application::AppUtils->simple_scalar_request(
826                 "open-ils.cstore",
827                 "open-ils.cstore.direct.actor.org_unit.search.atomic", 
828                 { $field => $value } );
829
830         return $list;
831 }
832
833
834 # build the org tree
835
836 __PACKAGE__->register_method(
837         method  => "get_org_tree",
838         api_name        => "open-ils.actor.org_tree.retrieve",
839         argc            => 0, 
840         note            => "Returns the entire org tree structure",
841 );
842
843 sub get_org_tree {
844         my( $self, $client) = @_;
845
846         $cache  = OpenSRF::Utils::Cache->new("global", 0) unless $cache;
847         my $tree = $cache->get_cache('orgtree');
848         return $tree if $tree;
849
850         $tree = new_editor()->search_actor_org_unit( 
851                 [
852                         {"parent_ou" => undef },
853                         {
854                                 flesh                           => 2,
855                                 flesh_fields    => { aou =>  ['children'] },
856                                 order_by                        => { aou => 'name'}
857                         }
858                 ]
859         )->[0];
860
861         $cache->put_cache('orgtree', $tree);
862         return $tree;
863 }
864
865
866 # turns an org list into an org tree
867 sub build_org_tree {
868
869         my( $self, $orglist) = @_;
870
871         return $orglist unless ( 
872                         ref($orglist) and @$orglist > 1 );
873
874         my @list = sort { 
875                 $a->ou_type <=> $b->ou_type ||
876                 $a->name cmp $b->name } @$orglist;
877
878         for my $org (@list) {
879
880                 next unless ($org and defined($org->parent_ou));
881                 my ($parent) = grep { $_->id == $org->parent_ou } @list;
882                 next unless $parent;
883
884                 $parent->children([]) unless defined($parent->children); 
885                 push( @{$parent->children}, $org );
886         }
887
888         return $list[0];
889
890 }
891
892
893 __PACKAGE__->register_method(
894         method  => "get_org_descendants",
895         api_name        => "open-ils.actor.org_tree.descendants.retrieve"
896 );
897
898 # depth is optional.  org_unit is the id
899 sub get_org_descendants {
900         my( $self, $client, $org_unit, $depth ) = @_;
901         my $orglist = $apputils->simple_scalar_request(
902                         "open-ils.storage", 
903                         "open-ils.storage.actor.org_unit.descendants.atomic",
904                         $org_unit, $depth );
905         return $self->build_org_tree($orglist);
906 }
907
908
909 __PACKAGE__->register_method(
910         method  => "get_org_ancestors",
911         api_name        => "open-ils.actor.org_tree.ancestors.retrieve"
912 );
913
914 # depth is optional.  org_unit is the id
915 sub get_org_ancestors {
916         my( $self, $client, $org_unit, $depth ) = @_;
917         my $orglist = $apputils->simple_scalar_request(
918                         "open-ils.storage", 
919                         "open-ils.storage.actor.org_unit.ancestors.atomic",
920                         $org_unit, $depth );
921         return $self->build_org_tree($orglist);
922 }
923
924
925 __PACKAGE__->register_method(
926         method  => "get_standings",
927         api_name        => "open-ils.actor.standings.retrieve"
928 );
929
930 my $user_standings;
931 sub get_standings {
932         return $user_standings if $user_standings;
933         return $user_standings = 
934                 $apputils->simple_scalar_request(
935                         "open-ils.cstore",
936                         "open-ils.cstore.direct.config.standing.search.atomic",
937                         { id => { "!=" => undef } }
938                 );
939 }
940
941
942
943 __PACKAGE__->register_method(
944         method  => "get_my_org_path",
945         api_name        => "open-ils.actor.org_unit.full_path.retrieve"
946 );
947
948 sub get_my_org_path {
949         my( $self, $client, $user_session, $org_id ) = @_;
950         my $user_obj = $apputils->check_user_session($user_session); 
951         if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
952
953         return $apputils->simple_scalar_request(
954                 "open-ils.storage",
955                 "open-ils.storage.actor.org_unit.full_path.atomic",
956                 $org_id );
957 }
958
959
960 __PACKAGE__->register_method(
961         method  => "patron_adv_search",
962         api_name        => "open-ils.actor.patron.search.advanced" );
963 sub patron_adv_search {
964         my( $self, $client, $auth, $search_hash, $search_limit, $search_sort, $include_inactive ) = @_;
965         my $e = new_editor(authtoken=>$auth);
966         return $e->event unless $e->checkauth;
967         return $e->event unless $e->allowed('VIEW_USER');
968         return $U->storagereq(
969                 "open-ils.storage.actor.user.crazy_search", 
970                 $search_hash, $search_limit, $search_sort, $include_inactive);
971 }
972
973
974
975 sub _verify_password {
976         my($user_session, $password) = @_;
977         my $user_obj = $apputils->check_user_session($user_session); 
978
979         #grab the user with password
980         $user_obj = $apputils->simple_scalar_request(
981                 "open-ils.cstore", 
982                 "open-ils.cstore.direct.actor.user.retrieve",
983                 $user_obj->id );
984
985         if($user_obj->passwd eq $password) {
986                 return 1;
987         }
988
989         return 0;
990 }
991
992
993 __PACKAGE__->register_method(
994         method  => "update_password",
995         api_name        => "open-ils.actor.user.password.update");
996
997 __PACKAGE__->register_method(
998         method  => "update_password",
999         api_name        => "open-ils.actor.user.username.update");
1000
1001 __PACKAGE__->register_method(
1002         method  => "update_password",
1003         api_name        => "open-ils.actor.user.email.update");
1004
1005 sub update_password {
1006         my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1007
1008         my $evt;
1009
1010         my $user_obj = $apputils->check_user_session($user_session); 
1011
1012         if($self->api_name =~ /password/o) {
1013
1014                 #make sure they know the current password
1015                 if(!_verify_password($user_session, md5_hex($current_password))) {
1016                         return OpenILS::Event->new('INCORRECT_PASSWORD');
1017                 }
1018
1019                 $logger->debug("update_password setting new password $new_value");
1020                 $user_obj->passwd($new_value);
1021
1022         } elsif($self->api_name =~ /username/o) {
1023                 my $users = search_username(undef, undef, $new_value); 
1024                 if( $users and $users->[0] ) {
1025                         return OpenILS::Event->new('USERNAME_EXISTS');
1026                 }
1027                 $user_obj->usrname($new_value);
1028
1029         } elsif($self->api_name =~ /email/o) {
1030                 #warn "Updating email to $new_value\n";
1031                 $user_obj->email($new_value);
1032         }
1033
1034         my $session = $apputils->start_db_session();
1035
1036         ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1037         return $evt if $evt;
1038
1039         $apputils->commit_db_session($session);
1040
1041         if($user_obj) { return 1; }
1042         return undef;
1043 }
1044
1045
1046 __PACKAGE__->register_method(
1047         method  => "check_user_perms",
1048         api_name        => "open-ils.actor.user.perm.check",
1049         notes           => <<"  NOTES");
1050         Takes a login session, user id, an org id, and an array of perm type strings.  For each
1051         perm type, if the user does *not* have the given permission it is added
1052         to a list which is returned from the method.  If all permissions
1053         are allowed, an empty list is returned
1054         if the logged in user does not match 'user_id', then the logged in user must
1055         have VIEW_PERMISSION priveleges.
1056         NOTES
1057
1058 sub check_user_perms {
1059         my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1060
1061         my( $staff, $evt ) = $apputils->checkses($login_session);
1062         return $evt if $evt;
1063
1064         if($staff->id ne $user_id) {
1065                 if( $evt = $apputils->check_perms(
1066                         $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1067                         return $evt;
1068                 }
1069         }
1070
1071         my @not_allowed;
1072         for my $perm (@$perm_types) {
1073                 if($apputils->check_perms($user_id, $org_id, $perm)) {
1074                         push @not_allowed, $perm;
1075                 }
1076         }
1077
1078         return \@not_allowed
1079 }
1080
1081 __PACKAGE__->register_method(
1082         method  => "check_user_perms2",
1083         api_name        => "open-ils.actor.user.perm.check.multi_org",
1084         notes           => q/
1085                 Checks the permissions on a list of perms and orgs for a user
1086                 @param authtoken The login session key
1087                 @param user_id The id of the user to check
1088                 @param orgs The array of org ids
1089                 @param perms The array of permission names
1090                 @return An array of  [ orgId, permissionName ] arrays that FAILED the check
1091                 if the logged in user does not match 'user_id', then the logged in user must
1092                 have VIEW_PERMISSION priveleges.
1093         /);
1094
1095 sub check_user_perms2 {
1096         my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1097
1098         my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1099                 $authtoken, $user_id, 'VIEW_PERMISSION' );
1100         return $evt if $evt;
1101
1102         my @not_allowed;
1103         for my $org (@$orgs) {
1104                 for my $perm (@$perms) {
1105                         if($apputils->check_perms($user_id, $org, $perm)) {
1106                                 push @not_allowed, [ $org, $perm ];
1107                         }
1108                 }
1109         }
1110
1111         return \@not_allowed
1112 }
1113
1114
1115 __PACKAGE__->register_method(
1116         method => 'check_user_perms3',
1117         api_name        => 'open-ils.actor.user.perm.highest_org',
1118         notes           => q/
1119                 Returns the highest org unit id at which a user has a given permission
1120                 If the requestor does not match the target user, the requestor must have
1121                 'VIEW_PERMISSION' rights at the home org unit of the target user
1122                 @param authtoken The login session key
1123                 @param userid The id of the user in question
1124                 @param perm The permission to check
1125                 @return The org unit highest in the org tree within which the user has
1126                 the requested permission
1127         /);
1128
1129 sub check_user_perms3 {
1130         my( $self, $client, $authtoken, $userid, $perm ) = @_;
1131
1132         my( $staff, $target, $org, $evt );
1133
1134         ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1135                 $authtoken, $userid, 'VIEW_PERMISSION' );
1136         return $evt if $evt;
1137
1138         my $tree = $self->get_org_tree();
1139         return _find_highest_perm_org( $perm, $userid, $target->ws_ou, $tree );
1140 }
1141
1142
1143 sub _find_highest_perm_org {
1144         my ( $perm, $userid, $start_org, $org_tree ) = @_;
1145         my $org = $apputils->find_org($org_tree, $start_org );
1146
1147         my $lastid = undef;
1148         while( $org ) {
1149                 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1150                 $lastid = $org->id;
1151                 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1152         }
1153
1154         return $lastid;
1155 }
1156
1157 __PACKAGE__->register_method(
1158         method => 'check_user_perms4',
1159         api_name        => 'open-ils.actor.user.perm.highest_org.batch',
1160         notes           => q/
1161                 Returns the highest org unit id at which a user has a given permission
1162                 If the requestor does not match the target user, the requestor must have
1163                 'VIEW_PERMISSION' rights at the home org unit of the target user
1164                 @param authtoken The login session key
1165                 @param userid The id of the user in question
1166                 @param perms An array of perm names to check 
1167                 @return An array of orgId's  representing the org unit 
1168                 highest in the org tree within which the user has the requested permission
1169                 The arrah of orgId's has matches the order of the perms array
1170         /);
1171
1172 sub check_user_perms4 {
1173         my( $self, $client, $authtoken, $userid, $perms ) = @_;
1174         
1175         my( $staff, $target, $org, $evt );
1176
1177         ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1178                 $authtoken, $userid, 'VIEW_PERMISSION' );
1179         return $evt if $evt;
1180
1181         my @arr;
1182         return [] unless ref($perms);
1183         my $tree = $self->get_org_tree();
1184
1185         for my $p (@$perms) {
1186                 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1187         }
1188         return \@arr;
1189 }
1190
1191
1192
1193
1194 __PACKAGE__->register_method(
1195         method  => "user_fines_summary",
1196         api_name        => "open-ils.actor.user.fines.summary",
1197         notes           => <<"  NOTES");
1198         Returns a short summary of the users total open fines, excluding voided fines
1199         Params are login_session, user_id
1200         Returns a 'mous' object.
1201         NOTES
1202
1203 sub user_fines_summary {
1204         my( $self, $client, $login_session, $user_id ) = @_;
1205
1206         my $user_obj = $apputils->check_user_session($login_session); 
1207         if($user_obj->id ne $user_id) {
1208                 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1209                         return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY"); 
1210                 }
1211         }
1212
1213         return $apputils->simple_scalar_request( 
1214                 "open-ils.cstore",
1215                 "open-ils.cstore.direct.money.open_user_summary.search",
1216                 { usr => $user_id } );
1217
1218 }
1219
1220
1221
1222
1223 __PACKAGE__->register_method(
1224         method  => "user_transactions",
1225         api_name        => "open-ils.actor.user.transactions",
1226         notes           => <<"  NOTES");
1227         Returns a list of open user transactions (mbts objects);
1228         Params are login_session, user_id
1229         Optional third parameter is the transactions type.  defaults to all
1230         NOTES
1231
1232 __PACKAGE__->register_method(
1233         method  => "user_transactions",
1234         api_name        => "open-ils.actor.user.transactions.have_charge",
1235         notes           => <<"  NOTES");
1236         Returns a list of all open user transactions (mbts objects) that have an initial charge
1237         Params are login_session, user_id
1238         Optional third parameter is the transactions type.  defaults to all
1239         NOTES
1240
1241 __PACKAGE__->register_method(
1242         method  => "user_transactions",
1243         api_name        => "open-ils.actor.user.transactions.have_balance",
1244         notes           => <<"  NOTES");
1245         Returns a list of all open user transactions (mbts objects) that have a balance
1246         Params are login_session, user_id
1247         Optional third parameter is the transactions type.  defaults to all
1248         NOTES
1249
1250 __PACKAGE__->register_method(
1251         method  => "user_transactions",
1252         api_name        => "open-ils.actor.user.transactions.fleshed",
1253         notes           => <<"  NOTES");
1254         Returns an object/hash of transaction, circ, title where transaction = an open 
1255         user transactions (mbts objects), circ is the attached circluation, and title
1256         is the title the circ points to
1257         Params are login_session, user_id
1258         Optional third parameter is the transactions type.  defaults to all
1259         NOTES
1260
1261 __PACKAGE__->register_method(
1262         method  => "user_transactions",
1263         api_name        => "open-ils.actor.user.transactions.have_charge.fleshed",
1264         notes           => <<"  NOTES");
1265         Returns an object/hash of transaction, circ, title where transaction = an open 
1266         user transactions that has an initial charge (mbts objects), circ is the 
1267         attached circluation, and title is the title the circ points to
1268         Params are login_session, user_id
1269         Optional third parameter is the transactions type.  defaults to all
1270         NOTES
1271
1272 __PACKAGE__->register_method(
1273         method  => "user_transactions",
1274         api_name        => "open-ils.actor.user.transactions.have_balance.fleshed",
1275         notes           => <<"  NOTES");
1276         Returns an object/hash of transaction, circ, title where transaction = an open 
1277         user transaction that has a balance (mbts objects), circ is the attached 
1278         circluation, and title is the title the circ points to
1279         Params are login_session, user_id
1280         Optional third parameter is the transaction type.  defaults to all
1281         NOTES
1282
1283 __PACKAGE__->register_method(
1284         method  => "user_transactions",
1285         api_name        => "open-ils.actor.user.transactions.count",
1286         notes           => <<"  NOTES");
1287         Returns an object/hash of transaction, circ, title where transaction = an open 
1288         user transactions (mbts objects), circ is the attached circluation, and title
1289         is the title the circ points to
1290         Params are login_session, user_id
1291         Optional third parameter is the transactions type.  defaults to all
1292         NOTES
1293
1294 __PACKAGE__->register_method(
1295         method  => "user_transactions",
1296         api_name        => "open-ils.actor.user.transactions.have_charge.count",
1297         notes           => <<"  NOTES");
1298         Returns an object/hash of transaction, circ, title where transaction = an open 
1299         user transactions that has an initial charge (mbts objects), circ is the 
1300         attached circluation, and title is the title the circ points to
1301         Params are login_session, user_id
1302         Optional third parameter is the transactions type.  defaults to all
1303         NOTES
1304
1305 __PACKAGE__->register_method(
1306         method  => "user_transactions",
1307         api_name        => "open-ils.actor.user.transactions.have_balance.count",
1308         notes           => <<"  NOTES");
1309         Returns an object/hash of transaction, circ, title where transaction = an open 
1310         user transaction that has a balance (mbts objects), circ is the attached 
1311         circluation, and title is the title the circ points to
1312         Params are login_session, user_id
1313         Optional third parameter is the transaction type.  defaults to all
1314         NOTES
1315
1316 __PACKAGE__->register_method(
1317         method  => "user_transactions",
1318         api_name        => "open-ils.actor.user.transactions.have_balance.total",
1319         notes           => <<"  NOTES");
1320         Returns an object/hash of transaction, circ, title where transaction = an open 
1321         user transaction that has a balance (mbts objects), circ is the attached 
1322         circluation, and title is the title the circ points to
1323         Params are login_session, user_id
1324         Optional third parameter is the transaction type.  defaults to all
1325         NOTES
1326
1327
1328
1329 sub user_transactions {
1330         my( $self, $client, $login_session, $user_id, $type ) = @_;
1331
1332         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1333                 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1334         return $evt if $evt;
1335
1336         my $api = $self->api_name();
1337         my $trans;
1338         my @xact;
1339
1340         if(defined($type)) { @xact = (xact_type =>  $type); 
1341
1342         } else { @xact = (); }
1343
1344         if($api =~ /have_charge/o) {
1345
1346                 ($trans) = $self
1347                         ->method_lookup('open-ils.actor.user.transactions.history.have_bill')
1348                         ->run($login_session => $user_id => $type);
1349                 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1350
1351         } elsif($api =~ /have_balance/o) {
1352
1353                 ($trans) = $self
1354                         ->method_lookup('open-ils.actor.user.transactions.history.have_balance')
1355                         ->run($login_session => $user_id => $type);
1356
1357         } else {
1358
1359                 ($trans) = $self
1360                         ->method_lookup('open-ils.actor.user.transactions.history.still_open')
1361                         ->run($login_session => $user_id => $type);
1362                 $trans = [ grep { int($_->total_owed * 100) > 0 } @$trans ];
1363
1364         }
1365         
1366         $trans = [ grep { !$_->xact_finish } @$trans ];
1367
1368         if($api =~ /total/o) { 
1369                 my $total = 0.0;
1370                 for my $t (@$trans) {
1371                         $total += $t->balance_owed;
1372                 }
1373
1374                 $logger->debug("Total balance owed by user $user_id: $total");
1375                 return $total;
1376         }
1377
1378         if($api =~ /count/o) { return scalar @$trans; }
1379         if($api !~ /fleshed/o) { return $trans; }
1380
1381         my @resp;
1382         for my $t (@$trans) {
1383                         
1384                 if( $t->xact_type ne 'circulation' ) {
1385                         push @resp, {transaction => $t};
1386                         next;
1387                 }
1388
1389                 my $circ = $apputils->simple_scalar_request(
1390                                 "open-ils.cstore",
1391                                 "open-ils.cstore.direct.action.circulation.retrieve",
1392                                 $t->id );
1393
1394                 next unless $circ;
1395
1396                 my $title = $apputils->simple_scalar_request(
1397                         "open-ils.storage", 
1398                         "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1399                         $circ->target_copy );
1400
1401                 next unless $title;
1402
1403                 my $u = OpenILS::Utils::ModsParser->new();
1404                 $u->start_mods_batch($title->marc());
1405                 my $mods = $u->finish_mods_batch();
1406                 $mods->doc_id($title->id) if $mods;
1407
1408                 push @resp, {transaction => $t, circ => $circ, record => $mods };
1409
1410         }
1411
1412         return \@resp; 
1413
1414
1415
1416 __PACKAGE__->register_method(
1417         method  => "user_transaction_retrieve",
1418         api_name        => "open-ils.actor.user.transaction.fleshed.retrieve",
1419         argc            => 1,
1420         notes           => <<"  NOTES");
1421         Returns a fleshedtransaction record
1422         NOTES
1423 __PACKAGE__->register_method(
1424         method  => "user_transaction_retrieve",
1425         api_name        => "open-ils.actor.user.transaction.retrieve",
1426         argc            => 1,
1427         notes           => <<"  NOTES");
1428         Returns a transaction record
1429         NOTES
1430 sub user_transaction_retrieve {
1431         my( $self, $client, $login_session, $bill_id ) = @_;
1432
1433         my $trans = $apputils->simple_scalar_request( 
1434                 "open-ils.cstore",
1435                 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1436                 $bill_id
1437         );
1438
1439         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1440                 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1441         return $evt if $evt;
1442         
1443         my $api = $self->api_name();
1444         if($api !~ /fleshed/o) { return $trans; }
1445
1446         if( $trans->xact_type ne 'circulation' ) {
1447                 $logger->debug("Returning non-circ transaction");
1448                 return {transaction => $trans};
1449         }
1450
1451         my $circ = $apputils->simple_scalar_request(
1452                         "open-ils.cstore",
1453                         "open-ils..direct.action.circulation.retrieve",
1454                         $trans->id );
1455
1456         return {transaction => $trans} unless $circ;
1457         $logger->debug("Found the circ transaction");
1458
1459         my $title = $apputils->simple_scalar_request(
1460                 "open-ils.storage", 
1461                 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1462                 $circ->target_copy );
1463
1464         return {transaction => $trans, circ => $circ } unless $title;
1465         $logger->debug("Found the circ title");
1466
1467         my $mods;
1468         try {
1469                 my $u = OpenILS::Utils::ModsParser->new();
1470                 $u->start_mods_batch($title->marc());
1471                 $mods = $u->finish_mods_batch();
1472         } otherwise {
1473                 if ($title->id == OILS_PRECAT_RECORD) {
1474                         my $copy = $apputils->simple_scalar_request(
1475                                 "open-ils.cstore",
1476                                 "open-ils.cstore.direct.asset.copy.retrieve",
1477                                 $circ->target_copy );
1478
1479                         $mods = new Fieldmapper::metabib::virtual_record;
1480                         $mods->doc_id(OILS_PRECAT_RECORD);
1481                         $mods->title($copy->dummy_title);
1482                         $mods->author($copy->dummy_author);
1483                 }
1484         };
1485
1486         $logger->debug("MODSized the circ title");
1487
1488         return {transaction => $trans, circ => $circ, record => $mods };
1489 }
1490
1491
1492 __PACKAGE__->register_method(
1493         method  => "hold_request_count",
1494         api_name        => "open-ils.actor.user.hold_requests.count",
1495         argc            => 1,
1496         notes           => <<"  NOTES");
1497         Returns hold ready/total counts
1498         NOTES
1499 sub hold_request_count {
1500         my( $self, $client, $login_session, $userid ) = @_;
1501
1502         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1503                 $login_session, $userid, 'VIEW_HOLD' );
1504         return $evt if $evt;
1505         
1506
1507         my $holds = $apputils->simple_scalar_request(
1508                         "open-ils.cstore",
1509                         "open-ils.cstore.direct.action.hold_request.search.atomic",
1510                         { 
1511                                 usr => $userid,
1512                                 fulfillment_time => {"=" => undef },
1513                                 cancel_time => undef,
1514                         }
1515         );
1516
1517         my @ready;
1518         for my $h (@$holds) {
1519                 next unless $h->capture_time and $h->current_copy;
1520
1521                 my $copy = $apputils->simple_scalar_request(
1522                         "open-ils.cstore",
1523                         "open-ils.cstore.direct.asset.copy.retrieve",
1524                         $h->current_copy
1525                 );
1526
1527                 if ($copy and $copy->status == 8) {
1528                         push @ready, $h;
1529                 }
1530         }
1531
1532         return { total => scalar(@$holds), ready => scalar(@ready) };
1533 }
1534
1535
1536 __PACKAGE__->register_method(
1537         method  => "checkedout_count",
1538         api_name        => "open-ils.actor.user.checked_out.count__",
1539         argc            => 1,
1540         notes           => <<"  NOTES");
1541         Returns a transaction record
1542         NOTES
1543
1544 # XXX Deprecate Me
1545 sub checkedout_count {
1546         my( $self, $client, $login_session, $userid ) = @_;
1547
1548         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1549                 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1550         return $evt if $evt;
1551         
1552         my $circs = $apputils->simple_scalar_request(
1553                         "open-ils.cstore",
1554                         "open-ils.cstore.direct.action.circulation.search.atomic",
1555                         { usr => $userid, stop_fines => undef }
1556                         #{ usr => $userid, checkin_time => {"=" => undef } }
1557         );
1558
1559         my $parser = DateTime::Format::ISO8601->new;
1560
1561         my (@out,@overdue);
1562         for my $c (@$circs) {
1563                 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1564                 my $due = $due_dt->epoch;
1565
1566                 if ($due < DateTime->today->epoch) {
1567                         push @overdue, $c;
1568                 }
1569         }
1570
1571         return { total => scalar(@$circs), overdue => scalar(@overdue) };
1572 }
1573
1574
1575 __PACKAGE__->register_method(
1576         method          => "checked_out",
1577         api_name                => "open-ils.actor.user.checked_out",
1578         argc                    => 2,
1579         signature       => q/
1580                 Returns a structure of circulations objects sorted by
1581                 out, overdue, lost, claims_returned, long_overdue.
1582                 A list of IDs are returned of each type.
1583                 lost, long_overdue, and claims_returned circ will not
1584                 be "finished" (there is an outstanding balance or some 
1585                 other pending action on the circ). 
1586
1587                 The .count method also includes a 'total' field which 
1588                 sums all "open" circs
1589         /
1590 );
1591
1592 __PACKAGE__->register_method(
1593         method          => "checked_out",
1594         api_name                => "open-ils.actor.user.checked_out.count",
1595         argc                    => 2,
1596         signature       => q/@see open-ils.actor.user.checked_out/
1597 );
1598
1599 sub checked_out {
1600         my( $self, $conn, $auth, $userid ) = @_;
1601
1602         my $e = new_editor(authtoken=>$auth);
1603         return $e->event unless $e->checkauth;
1604
1605         if( $userid ne $e->requestor->id ) {
1606                 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1607         }
1608
1609         my $count = $self->api_name =~ /count/;
1610         return _checked_out( $count, $e, $userid );
1611 }
1612
1613 sub _checked_out {
1614         my( $iscount, $e, $userid ) = @_;
1615
1616         my $circs = $e->search_action_circulation( 
1617                 { usr => $userid, stop_fines => undef });
1618
1619         my $mcircs = $e->search_action_circulation( 
1620                 { 
1621                         usr => $userid, 
1622                         checkin_time => undef, 
1623                         xact_finish => undef, 
1624                         stop_fines => OILS_STOP_FINES_MAX_FINES 
1625                 });
1626
1627         
1628         push( @$circs, @$mcircs );
1629
1630         my $parser = DateTime::Format::ISO8601->new;
1631
1632         # split the circs up into overdue and not-overdue circs
1633         my (@out,@overdue);
1634         for my $c (@$circs) {
1635                 if( $c->due_date ) {
1636                         my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1637                         my $due = $due_dt->epoch;
1638                         if ($due < DateTime->today->epoch) {
1639                                 push @overdue, $c->id;
1640                         } else {
1641                                 push @out, $c->id;
1642                         }
1643                 } else {
1644                         push @out, $c->id;
1645                 }
1646         }
1647
1648         # grab all of the lost, claims-returned, and longoverdue circs
1649         #my $open = $e->search_action_circulation(
1650         #       {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1651
1652
1653         # these items have stop_fines, but no xact_finish, so money
1654         # is owed on them and they have not been checked in
1655         my $open = $e->search_action_circulation(
1656                 {
1657                         usr                             => $userid, 
1658                         stop_fines              => { '!=' => undef }, 
1659                         xact_finish             => undef,
1660                         checkin_time    => undef,
1661                 }
1662         );
1663
1664
1665         my( @lost, @cr, @lo );
1666         for my $c (@$open) {
1667                 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1668                 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1669                 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1670         }
1671
1672
1673         if( $iscount ) {
1674                 return {
1675                         total           => @$circs + @lost + @cr + @lo,
1676                         out             => scalar(@out),
1677                         overdue => scalar(@overdue),
1678                         lost            => scalar(@lost),
1679                         claims_returned => scalar(@cr),
1680                         long_overdue            => scalar(@lo)
1681                 };
1682         }
1683
1684         return {
1685                 out             => \@out,
1686                 overdue => \@overdue,
1687                 lost            => \@lost,
1688                 claims_returned => \@cr,
1689                 long_overdue            => \@lo
1690         };
1691 }
1692
1693
1694
1695 __PACKAGE__->register_method(
1696         method          => "checked_in_with_fines",
1697         api_name                => "open-ils.actor.user.checked_in_with_fines",
1698         argc                    => 2,
1699         signature       => q/@see open-ils.actor.user.checked_out/
1700 );
1701 sub checked_in_with_fines {
1702         my( $self, $conn, $auth, $userid ) = @_;
1703
1704         my $e = new_editor(authtoken=>$auth);
1705         return $e->event unless $e->checkauth;
1706
1707         if( $userid ne $e->requestor->id ) {
1708                 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1709         }
1710
1711         # money is owed on these items and they are checked in
1712         my $open = $e->search_action_circulation(
1713                 {
1714                         usr                             => $userid, 
1715                         xact_finish             => undef,
1716                         checkin_time    => { "!=" => undef },
1717                 }
1718         );
1719
1720
1721         my( @lost, @cr, @lo );
1722         for my $c (@$open) {
1723                 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1724                 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1725                 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1726         }
1727
1728         return {
1729                 lost            => \@lost,
1730                 claims_returned => \@cr,
1731                 long_overdue            => \@lo
1732         };
1733 }
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743 __PACKAGE__->register_method(
1744         method  => "user_transaction_history",
1745         api_name        => "open-ils.actor.user.transactions.history",
1746         argc            => 1,
1747         notes           => <<"  NOTES");
1748         Returns a list of billable transaction ids for a user, optionally by type
1749         NOTES
1750 __PACKAGE__->register_method(
1751         method  => "user_transaction_history",
1752         api_name        => "open-ils.actor.user.transactions.history.have_charge",
1753         argc            => 1,
1754         notes           => <<"  NOTES");
1755         Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1756         NOTES
1757 __PACKAGE__->register_method(
1758         method  => "user_transaction_history",
1759         api_name        => "open-ils.actor.user.transactions.history.have_balance",
1760         argc            => 1,
1761         notes           => <<"  NOTES");
1762         Returns a list of billable transaction ids for a user that have a balance, optionally by type
1763         NOTES
1764 __PACKAGE__->register_method(
1765         method  => "user_transaction_history",
1766         api_name        => "open-ils.actor.user.transactions.history.still_open",
1767         argc            => 1,
1768         notes           => <<"  NOTES");
1769         Returns a list of billable transaction ids for a user that are not finished
1770         NOTES
1771 __PACKAGE__->register_method(
1772         method  => "user_transaction_history",
1773         api_name        => "open-ils.actor.user.transactions.history.have_bill",
1774         argc            => 1,
1775         notes           => <<"  NOTES");
1776         Returns a list of billable transaction ids for a user that has billings
1777         NOTES
1778
1779
1780
1781 =head old
1782 sub _user_transaction_history {
1783         my( $self, $client, $login_session, $user_id, $type ) = @_;
1784
1785         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1786                 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1787         return $evt if $evt;
1788
1789         my $api = $self->api_name();
1790         my @xact;
1791         my @charge;
1792         my @balance;
1793
1794         @xact = (xact_type =>  $type) if(defined($type));
1795         @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1796         @charge  = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1797
1798         $logger->debug("searching for transaction history: @xact : @balance, @charge");
1799
1800         my $trans = $apputils->simple_scalar_request( 
1801                 "open-ils.cstore",
1802                 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1803                 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1804
1805         return [ map { $_->id } @$trans ];
1806 }
1807 =cut
1808
1809 sub _make_mbts {
1810         my @xacts = @_;
1811
1812         my @mbts;
1813         for my $x (@xacts) {
1814                 my $s = new Fieldmapper::money::billable_transaction_summary;
1815                 $s->id( $x->id );
1816                 $s->usr( $x->usr );
1817                 $s->xact_start( $x->xact_start );
1818                 $s->xact_finish( $x->xact_finish );
1819
1820                 my $to = 0.0;
1821                 my $lb = undef;
1822                 for my $b (@{ $x->billings }) {
1823                         next if ($b->voided eq 'f' or !$b->voided);
1824                         $to += $b->amount;
1825                         $lb ||= $b->billing_ts;
1826                         if ($b->billing_ts ge $lb) {
1827                                 $lb = $b->billing_ts;
1828                                 $s->last_billing_note($b->note);
1829                                 $s->last_billing_ts($b->billing_ts);
1830                                 $s->last_billing_type($b->billing_type);
1831                         }
1832                 }
1833                 $s->total_owed( $to );
1834
1835                 my $tp = 0.0;
1836                 my $lp = undef;
1837                 for my $p (@{ $x->payments }) {
1838                         next if ($p->voided eq 'f' or !$p->voided);
1839                         $tp += $p->amount;
1840                         $lp ||= $p->payment_ts;
1841                         if ($b->payment_ts ge $lp) {
1842                                 $lp = $b->payment_ts;
1843                                 $s->last_payment_note($b->note);
1844                                 $s->last_payment_ts($b->payment_ts);
1845                                 $s->last_payment_type($b->payment_type);
1846                         }
1847                 }
1848                 $s->total_paid( $tp );
1849
1850                 $s->balance_owed( $s->total_owed - $s->total_paid );
1851
1852                 $s->xact_type( 'grocery' ) if ($x->grocery);
1853                 $s->xact_type( 'circulation' ) if ($x->circulation);
1854
1855                 push @mbts, $s;
1856         }
1857
1858         return @mbts;
1859 }
1860
1861 sub user_transaction_history {
1862         my( $self, $conn, $auth, $userid, $type ) = @_;
1863         my $e = new_editor(authtoken=>$auth);
1864         return $e->event unless $e->checkauth;
1865         return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1866
1867         my $api = $self->api_name;
1868         my @xact_finish  = (xact_finish => undef ) if $api =~ /still_open/;
1869
1870         my @xacts = @{ $e->search_money_billable_transaction(
1871                 [       { usr => $userid, @xact_finish },
1872                         { flesh => 1,
1873                           flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] },
1874                           order_by => { mbt => 'xact_start DESC' },
1875                         }
1876                 ]
1877         ) };
1878
1879         my @mbts = _make_mbts( @xacts );
1880
1881         if(defined($type)) {
1882                 @mbts = grep { $_->xact_type eq $type } @mbts;
1883         }
1884
1885         if($api =~ /have_balance/o) {
1886                 @mbts = grep { int($_->balance_owed * 100) != 0 } @mbts;
1887         }
1888
1889         if($api =~ /have_charge/o) {
1890                 @mbts = grep { defined($_->last_billing_ts) } @mbts;
1891         }
1892
1893         if($api =~ /have_bill/o) {
1894                 @mbts = grep { int($_->total_owed * 100) != 0 } @mbts;
1895         }
1896
1897         return [@mbts];
1898 }
1899
1900
1901
1902 __PACKAGE__->register_method(
1903         method  => "user_perms",
1904         api_name        => "open-ils.actor.permissions.user_perms.retrieve",
1905         argc            => 1,
1906         notes           => <<"  NOTES");
1907         Returns a list of permissions
1908         NOTES
1909 sub user_perms {
1910         my( $self, $client, $authtoken, $user ) = @_;
1911
1912         my( $staff, $evt ) = $apputils->checkses($authtoken);
1913         return $evt if $evt;
1914
1915         $user ||= $staff->id;
1916
1917         if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1918                 return $evt;
1919         }
1920
1921         return $apputils->simple_scalar_request(
1922                 "open-ils.storage",
1923                 "open-ils.storage.permission.user_perms.atomic",
1924                 $user);
1925 }
1926
1927 __PACKAGE__->register_method(
1928         method  => "retrieve_perms",
1929         api_name        => "open-ils.actor.permissions.retrieve",
1930         notes           => <<"  NOTES");
1931         Returns a list of permissions
1932         NOTES
1933 sub retrieve_perms {
1934         my( $self, $client ) = @_;
1935         return $apputils->simple_scalar_request(
1936                 "open-ils.cstore",
1937                 "open-ils.cstore.direct.permission.perm_list.search.atomic",
1938                 { id => { '!=' => undef } }
1939         );
1940 }
1941
1942 __PACKAGE__->register_method(
1943         method  => "retrieve_groups",
1944         api_name        => "open-ils.actor.groups.retrieve",
1945         notes           => <<"  NOTES");
1946         Returns a list of user groupss
1947         NOTES
1948 sub retrieve_groups {
1949         my( $self, $client ) = @_;
1950         return new_editor()->retrieve_all_permission_grp_tree();
1951 }
1952
1953 __PACKAGE__->register_method(
1954         method  => "retrieve_org_address",
1955         api_name        => "open-ils.actor.org_unit.address.retrieve",
1956         notes           => <<'  NOTES');
1957         Returns an org_unit address by ID
1958         @param An org_address ID
1959         NOTES
1960 sub retrieve_org_address {
1961         my( $self, $client, $id ) = @_;
1962         return $apputils->simple_scalar_request(
1963                 "open-ils.cstore",
1964                 "open-ils.cstore.direct.actor.org_address.retrieve",
1965                 $id
1966         );
1967 }
1968
1969 __PACKAGE__->register_method(
1970         method  => "retrieve_groups_tree",
1971         api_name        => "open-ils.actor.groups.tree.retrieve",
1972         notes           => <<"  NOTES");
1973         Returns a list of user groups
1974         NOTES
1975 sub retrieve_groups_tree {
1976         my( $self, $client ) = @_;
1977         return new_editor()->search_permission_grp_tree(
1978                 [
1979                         { parent => undef},
1980                         {       
1981                                 flesh                           => 10, 
1982                                 flesh_fields    => { pgt => ["children"] }, 
1983                                 order_by                        => { pgt => 'name'}
1984                         }
1985                 ]
1986         )->[0];
1987 }
1988
1989
1990 # turns an org list into an org tree
1991 =head old code
1992 sub build_group_tree {
1993
1994         my( $self, $grplist) = @_;
1995
1996         return $grplist unless ( 
1997                         ref($grplist) and @$grplist > 1 );
1998
1999         my @list = sort { $a->name cmp $b->name } @$grplist;
2000
2001         my $root;
2002         for my $grp (@list) {
2003
2004                 if ($grp and !defined($grp->parent)) {
2005                         $root = $grp;
2006                         next;
2007                 }
2008                 my ($parent) = grep { $_->id == $grp->parent} @list;
2009
2010                 $parent->children([]) unless defined($parent->children); 
2011                 push( @{$parent->children}, $grp );
2012         }
2013
2014         return $root;
2015 }
2016 =cut
2017
2018
2019 __PACKAGE__->register_method(
2020         method  => "add_user_to_groups",
2021         api_name        => "open-ils.actor.user.set_groups",
2022         notes           => <<"  NOTES");
2023         Adds a user to one or more permission groups
2024         NOTES
2025
2026 sub add_user_to_groups {
2027         my( $self, $client, $authtoken, $userid, $groups ) = @_;
2028
2029         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2030                 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2031         return $evt if $evt;
2032
2033         ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2034                 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2035         return $evt if $evt;
2036
2037         $apputils->simplereq(
2038                 'open-ils.storage',
2039                 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2040                 
2041         for my $group (@$groups) {
2042                 my $link = Fieldmapper::permission::usr_grp_map->new;
2043                 $link->grp($group);
2044                 $link->usr($userid);
2045
2046                 my $id = $apputils->simplereq(
2047                         'open-ils.storage',
2048                         'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2049         }
2050
2051         return 1;
2052 }
2053
2054 __PACKAGE__->register_method(
2055         method  => "get_user_perm_groups",
2056         api_name        => "open-ils.actor.user.get_groups",
2057         notes           => <<"  NOTES");
2058         Retrieve a user's permission groups.
2059         NOTES
2060
2061
2062 sub get_user_perm_groups {
2063         my( $self, $client, $authtoken, $userid ) = @_;
2064
2065         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2066                 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2067         return $evt if $evt;
2068
2069         return $apputils->simplereq(
2070                 'open-ils.cstore',
2071                 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2072 }       
2073
2074
2075
2076 __PACKAGE__->register_method (
2077         method          => 'register_workstation',
2078         api_name                => 'open-ils.actor.workstation.register.override',
2079         signature       => q/@see open-ils.actor.workstation.register/);
2080
2081 __PACKAGE__->register_method (
2082         method          => 'register_workstation',
2083         api_name                => 'open-ils.actor.workstation.register',
2084         signature       => q/
2085                 Registers a new workstion in the system
2086                 @param authtoken The login session key
2087                 @param name The name of the workstation id
2088                 @param owner The org unit that owns this workstation
2089                 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2090                 if the name is already in use.
2091         /);
2092
2093 sub _register_workstation {
2094         my( $self, $connection, $authtoken, $name, $owner ) = @_;
2095         my( $requestor, $evt ) = $U->checkses($authtoken);
2096         return $evt if $evt;
2097         $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2098         return $evt if $evt;
2099
2100         my $ws = $U->cstorereq(
2101                 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2102         return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2103
2104         $ws = Fieldmapper::actor::workstation->new;
2105         $ws->owning_lib($owner);
2106         $ws->name($name);
2107
2108         my $id = $U->storagereq(
2109                 'open-ils.storage.direct.actor.workstation.create', $ws );
2110         return $U->DB_UPDATE_FAILED($ws) unless $id;
2111
2112         $ws->id($id);
2113         return $ws->id();
2114 }
2115
2116 sub register_workstation {
2117         my( $self, $conn, $authtoken, $name, $owner ) = @_;
2118
2119         my $e = new_editor(authtoken=>$authtoken, xact=>1);
2120         return $e->event unless $e->checkauth;
2121         return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2122         my $existing = $e->search_actor_workstation({name => $name});
2123
2124         if( @$existing ) {
2125                 if( $self->api_name =~ /override/o ) {
2126                         return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2127                         return $e->event unless $e->delete_actor_workstation($$existing[0]);
2128                 } else {
2129                         return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2130                 }
2131         }
2132
2133         my $ws = Fieldmapper::actor::workstation->new;
2134         $ws->owning_lib($owner);
2135         $ws->name($name);
2136         $e->create_actor_workstation($ws) or return $e->event;
2137         $e->commit;
2138         return $ws->id; # note: editor sets the id on the new object for us
2139 }
2140
2141
2142 __PACKAGE__->register_method (
2143         method          => 'fetch_patron_note',
2144         api_name                => 'open-ils.actor.note.retrieve.all',
2145         signature       => q/
2146                 Returns a list of notes for a given user
2147                 Requestor must have VIEW_USER permission if pub==false and
2148                 @param authtoken The login session key
2149                 @param args Hash of params including
2150                         patronid : the patron's id
2151                         pub : true if retrieving only public notes
2152         /
2153 );
2154
2155 sub fetch_patron_note {
2156         my( $self, $conn, $authtoken, $args ) = @_;
2157         my $patronid = $$args{patronid};
2158
2159         my($reqr, $evt) = $U->checkses($authtoken);
2160
2161         my $patron;
2162         ($patron, $evt) = $U->fetch_user($patronid);
2163         return $evt if $evt;
2164
2165         if($$args{pub}) {
2166                 if( $patronid ne $reqr->id ) {
2167                         $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2168                         return $evt if $evt;
2169                 }
2170                 return $U->cstorereq(
2171                         'open-ils.cstore.direct.actor.usr_note.search.atomic', 
2172                         { usr => $patronid, pub => 't' } );
2173         }
2174
2175         $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2176         return $evt if $evt;
2177
2178         return $U->cstorereq(
2179                 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2180 }
2181
2182 __PACKAGE__->register_method (
2183         method          => 'create_user_note',
2184         api_name                => 'open-ils.actor.note.create',
2185         signature       => q/
2186                 Creates a new note for the given user
2187                 @param authtoken The login session key
2188                 @param note The note object
2189         /
2190 );
2191 sub create_user_note {
2192         my( $self, $conn, $authtoken, $note ) = @_;
2193         my( $reqr, $patron, $evt ) = 
2194                 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2195         return $evt if $evt;
2196         $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2197
2198         $note->creator($reqr->id);
2199         my $id = $U->storagereq(
2200                 'open-ils.storage.direct.actor.usr_note.create', $note );
2201         return $U->DB_UPDATE_FAILED($note) unless $id;
2202         return $id;
2203 }
2204
2205
2206 __PACKAGE__->register_method (
2207         method          => 'delete_user_note',
2208         api_name                => 'open-ils.actor.note.delete',
2209         signature       => q/
2210                 Deletes a note for the given user
2211                 @param authtoken The login session key
2212                 @param noteid The note id
2213         /
2214 );
2215 sub delete_user_note {
2216         my( $self, $conn, $authtoken, $noteid ) = @_;
2217
2218         my $note = $U->cstorereq(
2219                 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2220         return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2221
2222         my( $reqr, $patron, $evt ) = 
2223                 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2224         return $evt if $evt;
2225         $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2226
2227         my $stat = $U->storagereq(
2228                 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2229         return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2230         return $stat;
2231 }
2232
2233
2234 __PACKAGE__->register_method (
2235         method          => 'update_user_note',
2236         api_name                => 'open-ils.actor.note.update',
2237         signature       => q/
2238                 @param authtoken The login session key
2239                 @param note The note
2240         /
2241 );
2242
2243 sub update_user_note {
2244         my( $self, $conn, $auth, $note ) = @_;
2245         my $e = new_editor(authtoken=>$auth, xact=>1);
2246         return $e->event unless $e->checkauth;
2247         my $patron = $e->retrieve_actor_user($note->usr)
2248                 or return $e->event;
2249         return $e->event unless 
2250                 $e->allowed('UPDATE_USER', $patron->home_ou);
2251         $e->update_actor_user_note($note)
2252                 or return $e->event;
2253         $e->commit;
2254         return 1;
2255 }
2256
2257
2258
2259
2260 __PACKAGE__->register_method (
2261         method          => 'create_closed_date',
2262         api_name        => 'open-ils.actor.org_unit.closed_date.create',
2263         signature       => q/
2264                 Creates a new closing entry for the given org_unit
2265                 @param authtoken The login session key
2266                 @param note The closed_date object
2267         /
2268 );
2269 sub create_closed_date {
2270         my( $self, $conn, $authtoken, $cd ) = @_;
2271
2272         my( $user, $evt ) = $U->checkses($authtoken);
2273         return $evt if $evt;
2274
2275         $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2276         return $evt if $evt;
2277
2278         $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2279
2280         my $id = $U->storagereq(
2281                 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2282         return $U->DB_UPDATE_FAILED($cd) unless $id;
2283         return $id;
2284 }
2285
2286
2287 __PACKAGE__->register_method (
2288         method          => 'delete_closed_date',
2289         api_name        => 'open-ils.actor.org_unit.closed_date.delete',
2290         signature       => q/
2291                 Deletes a closing entry for the given org_unit
2292                 @param authtoken The login session key
2293                 @param noteid The close_date id
2294         /
2295 );
2296 sub delete_closed_date {
2297         my( $self, $conn, $authtoken, $cd ) = @_;
2298
2299         my( $user, $evt ) = $U->checkses($authtoken);
2300         return $evt if $evt;
2301
2302         my $cd_obj;
2303         ($cd_obj, $evt) = fetch_closed_date($cd);
2304         return $evt if $evt;
2305
2306         $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2307         return $evt if $evt;
2308
2309         $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2310
2311         my $stat = $U->storagereq(
2312                 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2313         return $U->DB_UPDATE_FAILED($cd) unless $stat;
2314         return $stat;
2315 }
2316
2317
2318 __PACKAGE__->register_method(
2319         method => 'usrname_exists',
2320         api_name        => 'open-ils.actor.username.exists',
2321         signature => q/
2322                 Returns 1 if the requested username exists, returns 0 otherwise
2323         /
2324 );
2325
2326 sub usrname_exists {
2327         my( $self, $conn, $auth, $usrname ) = @_;
2328         my $e = new_editor(authtoken=>$auth);
2329         return $e->event unless $e->checkauth;
2330         my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2331         return $$a[0] if $a and @$a;
2332         return 0;
2333 }
2334
2335 __PACKAGE__->register_method(
2336         method => 'barcode_exists',
2337         api_name        => 'open-ils.actor.barcode.exists',
2338         signature => q/
2339                 Returns 1 if the requested barcode exists, returns 0 otherwise
2340         /
2341 );
2342
2343 sub barcode_exists {
2344         my( $self, $conn, $auth, $barcode ) = @_;
2345         my $e = new_editor(authtoken=>$auth);
2346         return $e->event unless $e->checkauth;
2347         my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2348         return $$a[0] if $a and @$a;
2349         return 0;
2350 }
2351
2352
2353 __PACKAGE__->register_method(
2354         method => 'retrieve_net_levels',
2355         api_name        => 'open-ils.actor.net_access_level.retrieve.all',
2356 );
2357
2358 sub retrieve_net_levels {
2359         my( $self, $conn, $auth ) = @_;
2360         my $e = new_editor(authtoken=>$auth);
2361         return $e->event unless $e->checkauth;
2362         return $e->retrieve_all_config_net_access_level();
2363 }
2364
2365
2366 __PACKAGE__->register_method(
2367         method => 'fetch_org_by_shortname',
2368         api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2369 );
2370 sub fetch_org_by_shortname {
2371         my( $self, $conn, $sname ) = @_;
2372         my $e = new_editor();
2373         my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2374         return $e->event unless $org;
2375         return $org;
2376 }
2377
2378
2379 __PACKAGE__->register_method(
2380         method => 'session_home_lib',
2381         api_name => 'open-ils.actor.session.home_lib',
2382 );
2383
2384 sub session_home_lib {
2385         my( $self, $conn, $auth ) = @_;
2386         my $e = new_editor(authtoken=>$auth);
2387         return undef unless $e->checkauth;
2388         my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2389         return $org->shortname;
2390 }
2391
2392
2393
2394 __PACKAGE__->register_method(
2395         method => 'slim_tree',
2396         api_name        => "open-ils.actor.org_tree.slim_hash.retrieve",
2397 );
2398 sub slim_tree {
2399         my $tree = new_editor()->search_actor_org_unit( 
2400                 [
2401                         {"parent_ou" => undef },
2402                         {
2403                                 flesh                           => 2,
2404                                 flesh_fields    => { aou =>  ['children'] },
2405                                 order_by                        => { aou => 'name'},
2406                                 select                  => { aou => ["id","shortname", "name"]},
2407                         }
2408                 ]
2409         )->[0];
2410
2411         return trim_tree($tree);
2412 }
2413
2414
2415 sub trim_tree {
2416         my $tree = shift;
2417         return undef unless $tree;
2418         my $htree = {
2419                 code => $tree->shortname,
2420                 name => $tree->name,
2421         };
2422         if( $tree->children and @{$tree->children} ) {
2423                 $htree->{children} = [];
2424                 for my $c (@{$tree->children}) {
2425                         push( @{$htree->{children}}, trim_tree($c) );
2426                 }
2427         }
2428
2429         return $htree;
2430 }
2431
2432
2433
2434 __PACKAGE__->register_method(
2435         method  => "user_retrieve_fleshed_by_id",
2436         api_name        => "open-ils.actor.user.fleshed.retrieve",);
2437
2438 sub user_retrieve_fleshed_by_id {
2439         my( $self, $client, $auth, $user_id, $fields ) = @_;
2440         my $e = new_editor(authtoken => $auth);
2441         return $e->event unless $e->checkauth;
2442         if( $e->requestor->id != $user_id ) {
2443                 return $e->event unless $e->allowed('VIEW_USER');
2444         }
2445         $fields ||= [
2446                 "cards",
2447                 "card",
2448                 "standing_penalties",
2449                 "addresses",
2450                 "billing_address",
2451                 "mailing_address",
2452                 "stat_cat_entries" ];
2453         return new_flesh_user($user_id, $fields, $e);
2454 }
2455
2456
2457 sub new_flesh_user {
2458
2459         my $id = shift;
2460         my $fields = shift || [];
2461         my $e   = shift || new_editor(xact=>1);
2462
2463         my $user = $e->retrieve_actor_user(
2464         [
2465         $id,
2466         {
2467                 "flesh"                         => 1,
2468                 "flesh_fields" =>  { "au" => $fields }
2469         }
2470         ]
2471         ) or return $e->event;
2472
2473
2474         if( grep { $_ eq 'addresses' } @$fields ) {
2475
2476                 $user->addresses([]) unless @{$user->addresses};
2477         
2478                 if( ref $user->billing_address ) {
2479                         unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2480                                 push( @{$user->addresses}, $user->billing_address );
2481                         }
2482                 }
2483         
2484                 if( ref $user->mailing_address ) {
2485                         unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2486                                 push( @{$user->addresses}, $user->mailing_address );
2487                         }
2488                 }
2489         }
2490
2491         $e->disconnect;
2492         $user->clear_passwd();
2493         return $user;
2494 }
2495
2496
2497
2498
2499 __PACKAGE__->register_method(
2500         method  => "user_retrieve_parts",
2501         api_name        => "open-ils.actor.user.retrieve.parts",);
2502
2503 sub user_retrieve_parts {
2504         my( $self, $client, $auth, $user_id, $fields ) = @_;
2505         my $e = new_editor(authtoken => $auth);
2506         return $e->event unless $e->checkauth;
2507         if( $e->requestor->id != $user_id ) {
2508                 return $e->event unless $e->allowed('VIEW_USER');
2509         }
2510         my @resp;
2511         my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2512         push(@resp, $user->$_()) for(@$fields);
2513         return \@resp;
2514 }
2515
2516
2517
2518
2519
2520
2521 1;
2522