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