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