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