]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Actor.pm
moved to cancel_time on hold as opposed to deleting it
[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_ident_types",
907         api_name        => "open-ils.actor.user.ident_types.retrieve",
908 );
909 my $ident_types;
910 sub get_user_ident_types {
911         return $ident_types if $ident_types;
912         return $ident_types = 
913                 new_editor()->retrieve_all_config_identification_type();
914 }
915
916
917
918
919 __PACKAGE__->register_method(
920         method  => "get_org_unit",
921         api_name        => "open-ils.actor.org_unit.retrieve",
922 );
923
924 sub get_org_unit {
925         my( $self, $client, $user_session, $org_id ) = @_;
926         my $e = new_editor(authtoken => $user_session);
927         if(!$org_id) {
928                 return $e->event unless $e->checkauth;
929                 $org_id = $e->requestor->ws_ou;
930         }
931         my $o = $e->retrieve_actor_org_unit($org_id)
932                 or return $e->event;
933         return $o;
934 }
935
936 __PACKAGE__->register_method(
937         method  => "search_org_unit",
938         api_name        => "open-ils.actor.org_unit_list.search",
939 );
940
941 sub search_org_unit {
942
943         my( $self, $client, $field, $value ) = @_;
944
945         my $list = OpenILS::Application::AppUtils->simple_scalar_request(
946                 "open-ils.cstore",
947                 "open-ils.cstore.direct.actor.org_unit.search.atomic", 
948                 { $field => $value } );
949
950         return $list;
951 }
952
953
954 # build the org tree
955
956 __PACKAGE__->register_method(
957         method  => "get_org_tree",
958         api_name        => "open-ils.actor.org_tree.retrieve",
959         argc            => 0, 
960         note            => "Returns the entire org tree structure",
961 );
962
963 sub get_org_tree {
964         my( $self, $client) = @_;
965
966         $cache  = OpenSRF::Utils::Cache->new("global", 0) unless $cache;
967         my $tree = $cache->get_cache('orgtree');
968         return $tree if $tree;
969
970         $tree = new_editor()->search_actor_org_unit( 
971                 [
972                         {"parent_ou" => undef },
973                         {
974                                 flesh                           => 2,
975                                 flesh_fields    => { aou =>  ['children'] },
976                                 order_by                        => { aou => 'name'}
977                         }
978                 ]
979         )->[0];
980
981         $cache->put_cache('orgtree', $tree);
982         return $tree;
983 }
984
985
986 # turns an org list into an org tree
987 sub build_org_tree {
988
989         my( $self, $orglist) = @_;
990
991         return $orglist unless ( 
992                         ref($orglist) and @$orglist > 1 );
993
994         my @list = sort { 
995                 $a->ou_type <=> $b->ou_type ||
996                 $a->name cmp $b->name } @$orglist;
997
998         for my $org (@list) {
999
1000                 next unless ($org and defined($org->parent_ou));
1001                 my ($parent) = grep { $_->id == $org->parent_ou } @list;
1002                 next unless $parent;
1003
1004                 $parent->children([]) unless defined($parent->children); 
1005                 push( @{$parent->children}, $org );
1006         }
1007
1008         return $list[0];
1009
1010 }
1011
1012
1013 __PACKAGE__->register_method(
1014         method  => "get_org_descendants",
1015         api_name        => "open-ils.actor.org_tree.descendants.retrieve"
1016 );
1017
1018 # depth is optional.  org_unit is the id
1019 sub get_org_descendants {
1020         my( $self, $client, $org_unit, $depth ) = @_;
1021         my $orglist = $apputils->simple_scalar_request(
1022                         "open-ils.storage", 
1023                         "open-ils.storage.actor.org_unit.descendants.atomic",
1024                         $org_unit, $depth );
1025         return $self->build_org_tree($orglist);
1026 }
1027
1028
1029 __PACKAGE__->register_method(
1030         method  => "get_org_ancestors",
1031         api_name        => "open-ils.actor.org_tree.ancestors.retrieve"
1032 );
1033
1034 # depth is optional.  org_unit is the id
1035 sub get_org_ancestors {
1036         my( $self, $client, $org_unit, $depth ) = @_;
1037         my $orglist = $apputils->simple_scalar_request(
1038                         "open-ils.storage", 
1039                         "open-ils.storage.actor.org_unit.ancestors.atomic",
1040                         $org_unit, $depth );
1041         return $self->build_org_tree($orglist);
1042 }
1043
1044
1045 __PACKAGE__->register_method(
1046         method  => "get_standings",
1047         api_name        => "open-ils.actor.standings.retrieve"
1048 );
1049
1050 my $user_standings;
1051 sub get_standings {
1052         return $user_standings if $user_standings;
1053         return $user_standings = 
1054                 $apputils->simple_scalar_request(
1055                         "open-ils.cstore",
1056                         "open-ils.cstore.direct.config.standing.search.atomic",
1057                         { id => { "!=" => undef } }
1058                 );
1059 }
1060
1061
1062
1063 __PACKAGE__->register_method(
1064         method  => "get_my_org_path",
1065         api_name        => "open-ils.actor.org_unit.full_path.retrieve"
1066 );
1067
1068 sub get_my_org_path {
1069         my( $self, $client, $user_session, $org_id ) = @_;
1070         my $user_obj = $apputils->check_user_session($user_session); 
1071         if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
1072
1073         return $apputils->simple_scalar_request(
1074                 "open-ils.storage",
1075                 "open-ils.storage.actor.org_unit.full_path.atomic",
1076                 $org_id );
1077 }
1078
1079
1080 __PACKAGE__->register_method(
1081         method  => "patron_adv_search",
1082         api_name        => "open-ils.actor.patron.search.advanced" );
1083 sub patron_adv_search {
1084         my( $self, $client, $auth, $search_hash, $search_limit, $search_sort, $include_inactive ) = @_;
1085         my $e = new_editor(authtoken=>$auth);
1086         return $e->event unless $e->checkauth;
1087         return $e->event unless $e->allowed('VIEW_USER');
1088         return $U->storagereq(
1089                 "open-ils.storage.actor.user.crazy_search", 
1090                 $search_hash, $search_limit, $search_sort, $include_inactive);
1091 }
1092
1093
1094
1095 sub _verify_password {
1096         my($user_session, $password) = @_;
1097         my $user_obj = $apputils->check_user_session($user_session); 
1098
1099         #grab the user with password
1100         $user_obj = $apputils->simple_scalar_request(
1101                 "open-ils.cstore", 
1102                 "open-ils.cstore.direct.actor.user.retrieve",
1103                 $user_obj->id );
1104
1105         if($user_obj->passwd eq $password) {
1106                 return 1;
1107         }
1108
1109         return 0;
1110 }
1111
1112
1113 __PACKAGE__->register_method(
1114         method  => "update_password",
1115         api_name        => "open-ils.actor.user.password.update");
1116
1117 __PACKAGE__->register_method(
1118         method  => "update_password",
1119         api_name        => "open-ils.actor.user.username.update");
1120
1121 __PACKAGE__->register_method(
1122         method  => "update_password",
1123         api_name        => "open-ils.actor.user.email.update");
1124
1125 sub update_password {
1126         my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1127
1128         my $evt;
1129
1130         my $user_obj = $apputils->check_user_session($user_session); 
1131
1132         if($self->api_name =~ /password/o) {
1133
1134                 #make sure they know the current password
1135                 if(!_verify_password($user_session, md5_hex($current_password))) {
1136                         return OpenILS::Event->new('INCORRECT_PASSWORD');
1137                 }
1138
1139                 $logger->debug("update_password setting new password $new_value");
1140                 $user_obj->passwd($new_value);
1141
1142         } elsif($self->api_name =~ /username/o) {
1143                 my $users = search_username(undef, undef, $new_value); 
1144                 if( $users and $users->[0] ) {
1145                         return OpenILS::Event->new('USERNAME_EXISTS');
1146                 }
1147                 $user_obj->usrname($new_value);
1148
1149         } elsif($self->api_name =~ /email/o) {
1150                 #warn "Updating email to $new_value\n";
1151                 $user_obj->email($new_value);
1152         }
1153
1154         my $session = $apputils->start_db_session();
1155
1156         ( $user_obj, $evt ) = _update_patron($session, $user_obj, $user_obj, 1);
1157         return $evt if $evt;
1158
1159         $apputils->commit_db_session($session);
1160
1161         if($user_obj) { return 1; }
1162         return undef;
1163 }
1164
1165
1166 __PACKAGE__->register_method(
1167         method  => "check_user_perms",
1168         api_name        => "open-ils.actor.user.perm.check",
1169         notes           => <<"  NOTES");
1170         Takes a login session, user id, an org id, and an array of perm type strings.  For each
1171         perm type, if the user does *not* have the given permission it is added
1172         to a list which is returned from the method.  If all permissions
1173         are allowed, an empty list is returned
1174         if the logged in user does not match 'user_id', then the logged in user must
1175         have VIEW_PERMISSION priveleges.
1176         NOTES
1177
1178 sub check_user_perms {
1179         my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1180
1181         my( $staff, $evt ) = $apputils->checkses($login_session);
1182         return $evt if $evt;
1183
1184         if($staff->id ne $user_id) {
1185                 if( $evt = $apputils->check_perms(
1186                         $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1187                         return $evt;
1188                 }
1189         }
1190
1191         my @not_allowed;
1192         for my $perm (@$perm_types) {
1193                 if($apputils->check_perms($user_id, $org_id, $perm)) {
1194                         push @not_allowed, $perm;
1195                 }
1196         }
1197
1198         return \@not_allowed
1199 }
1200
1201 __PACKAGE__->register_method(
1202         method  => "check_user_perms2",
1203         api_name        => "open-ils.actor.user.perm.check.multi_org",
1204         notes           => q/
1205                 Checks the permissions on a list of perms and orgs for a user
1206                 @param authtoken The login session key
1207                 @param user_id The id of the user to check
1208                 @param orgs The array of org ids
1209                 @param perms The array of permission names
1210                 @return An array of  [ orgId, permissionName ] arrays that FAILED the check
1211                 if the logged in user does not match 'user_id', then the logged in user must
1212                 have VIEW_PERMISSION priveleges.
1213         /);
1214
1215 sub check_user_perms2 {
1216         my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1217
1218         my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1219                 $authtoken, $user_id, 'VIEW_PERMISSION' );
1220         return $evt if $evt;
1221
1222         my @not_allowed;
1223         for my $org (@$orgs) {
1224                 for my $perm (@$perms) {
1225                         if($apputils->check_perms($user_id, $org, $perm)) {
1226                                 push @not_allowed, [ $org, $perm ];
1227                         }
1228                 }
1229         }
1230
1231         return \@not_allowed
1232 }
1233
1234
1235 __PACKAGE__->register_method(
1236         method => 'check_user_perms3',
1237         api_name        => 'open-ils.actor.user.perm.highest_org',
1238         notes           => q/
1239                 Returns the highest org unit id at which a user has a given permission
1240                 If the requestor does not match the target user, the requestor must have
1241                 'VIEW_PERMISSION' rights at the home org unit of the target user
1242                 @param authtoken The login session key
1243                 @param userid The id of the user in question
1244                 @param perm The permission to check
1245                 @return The org unit highest in the org tree within which the user has
1246                 the requested permission
1247         /);
1248
1249 sub check_user_perms3 {
1250         my( $self, $client, $authtoken, $userid, $perm ) = @_;
1251
1252         my( $staff, $target, $org, $evt );
1253
1254         ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1255                 $authtoken, $userid, 'VIEW_PERMISSION' );
1256         return $evt if $evt;
1257
1258         my $tree = $self->get_org_tree();
1259         return _find_highest_perm_org( $perm, $userid, $target->ws_ou, $tree );
1260 }
1261
1262
1263 sub _find_highest_perm_org {
1264         my ( $perm, $userid, $start_org, $org_tree ) = @_;
1265         my $org = $apputils->find_org($org_tree, $start_org );
1266
1267         my $lastid = undef;
1268         while( $org ) {
1269                 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1270                 $lastid = $org->id;
1271                 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1272         }
1273
1274         return $lastid;
1275 }
1276
1277 __PACKAGE__->register_method(
1278         method => 'check_user_perms4',
1279         api_name        => 'open-ils.actor.user.perm.highest_org.batch',
1280         notes           => q/
1281                 Returns the highest org unit id at which a user has a given permission
1282                 If the requestor does not match the target user, the requestor must have
1283                 'VIEW_PERMISSION' rights at the home org unit of the target user
1284                 @param authtoken The login session key
1285                 @param userid The id of the user in question
1286                 @param perms An array of perm names to check 
1287                 @return An array of orgId's  representing the org unit 
1288                 highest in the org tree within which the user has the requested permission
1289                 The arrah of orgId's has matches the order of the perms array
1290         /);
1291
1292 sub check_user_perms4 {
1293         my( $self, $client, $authtoken, $userid, $perms ) = @_;
1294         
1295         my( $staff, $target, $org, $evt );
1296
1297         ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1298                 $authtoken, $userid, 'VIEW_PERMISSION' );
1299         return $evt if $evt;
1300
1301         my @arr;
1302         return [] unless ref($perms);
1303         my $tree = $self->get_org_tree();
1304
1305         for my $p (@$perms) {
1306                 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1307         }
1308         return \@arr;
1309 }
1310
1311
1312
1313
1314 __PACKAGE__->register_method(
1315         method  => "user_fines_summary",
1316         api_name        => "open-ils.actor.user.fines.summary",
1317         notes           => <<"  NOTES");
1318         Returns a short summary of the users total open fines, excluding voided fines
1319         Params are login_session, user_id
1320         Returns a 'mous' object.
1321         NOTES
1322
1323 sub user_fines_summary {
1324         my( $self, $client, $login_session, $user_id ) = @_;
1325
1326         my $user_obj = $apputils->check_user_session($login_session); 
1327         if($user_obj->id ne $user_id) {
1328                 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1329                         return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY"); 
1330                 }
1331         }
1332
1333         return $apputils->simple_scalar_request( 
1334                 "open-ils.cstore",
1335                 "open-ils.cstore.direct.money.open_user_summary.search",
1336                 { usr => $user_id } );
1337
1338 }
1339
1340
1341
1342
1343 __PACKAGE__->register_method(
1344         method  => "user_transactions",
1345         api_name        => "open-ils.actor.user.transactions",
1346         notes           => <<"  NOTES");
1347         Returns a list of open user transactions (mbts objects);
1348         Params are login_session, user_id
1349         Optional third parameter is the transactions type.  defaults to all
1350         NOTES
1351
1352 __PACKAGE__->register_method(
1353         method  => "user_transactions",
1354         api_name        => "open-ils.actor.user.transactions.have_charge",
1355         notes           => <<"  NOTES");
1356         Returns a list of all open user transactions (mbts objects) that have an initial charge
1357         Params are login_session, user_id
1358         Optional third parameter is the transactions type.  defaults to all
1359         NOTES
1360
1361 __PACKAGE__->register_method(
1362         method  => "user_transactions",
1363         api_name        => "open-ils.actor.user.transactions.have_balance",
1364         notes           => <<"  NOTES");
1365         Returns a list of all open user transactions (mbts objects) that have a balance
1366         Params are login_session, user_id
1367         Optional third parameter is the transactions type.  defaults to all
1368         NOTES
1369
1370 __PACKAGE__->register_method(
1371         method  => "user_transactions",
1372         api_name        => "open-ils.actor.user.transactions.fleshed",
1373         notes           => <<"  NOTES");
1374         Returns an object/hash of transaction, circ, title where transaction = an open 
1375         user transactions (mbts objects), circ is the attached circluation, and title
1376         is the title the circ points to
1377         Params are login_session, user_id
1378         Optional third parameter is the transactions type.  defaults to all
1379         NOTES
1380
1381 __PACKAGE__->register_method(
1382         method  => "user_transactions",
1383         api_name        => "open-ils.actor.user.transactions.have_charge.fleshed",
1384         notes           => <<"  NOTES");
1385         Returns an object/hash of transaction, circ, title where transaction = an open 
1386         user transactions that has an initial charge (mbts objects), circ is the 
1387         attached circluation, and title is the title the circ points to
1388         Params are login_session, user_id
1389         Optional third parameter is the transactions type.  defaults to all
1390         NOTES
1391
1392 __PACKAGE__->register_method(
1393         method  => "user_transactions",
1394         api_name        => "open-ils.actor.user.transactions.have_balance.fleshed",
1395         notes           => <<"  NOTES");
1396         Returns an object/hash of transaction, circ, title where transaction = an open 
1397         user transaction that has a balance (mbts objects), circ is the attached 
1398         circluation, and title is the title the circ points to
1399         Params are login_session, user_id
1400         Optional third parameter is the transaction type.  defaults to all
1401         NOTES
1402
1403 __PACKAGE__->register_method(
1404         method  => "user_transactions",
1405         api_name        => "open-ils.actor.user.transactions.count",
1406         notes           => <<"  NOTES");
1407         Returns an object/hash of transaction, circ, title where transaction = an open 
1408         user transactions (mbts objects), circ is the attached circluation, and title
1409         is the title the circ points to
1410         Params are login_session, user_id
1411         Optional third parameter is the transactions type.  defaults to all
1412         NOTES
1413
1414 __PACKAGE__->register_method(
1415         method  => "user_transactions",
1416         api_name        => "open-ils.actor.user.transactions.have_charge.count",
1417         notes           => <<"  NOTES");
1418         Returns an object/hash of transaction, circ, title where transaction = an open 
1419         user transactions that has an initial charge (mbts objects), circ is the 
1420         attached circluation, and title is the title the circ points to
1421         Params are login_session, user_id
1422         Optional third parameter is the transactions type.  defaults to all
1423         NOTES
1424
1425 __PACKAGE__->register_method(
1426         method  => "user_transactions",
1427         api_name        => "open-ils.actor.user.transactions.have_balance.count",
1428         notes           => <<"  NOTES");
1429         Returns an object/hash of transaction, circ, title where transaction = an open 
1430         user transaction that has a balance (mbts objects), circ is the attached 
1431         circluation, and title is the title the circ points to
1432         Params are login_session, user_id
1433         Optional third parameter is the transaction type.  defaults to all
1434         NOTES
1435
1436 __PACKAGE__->register_method(
1437         method  => "user_transactions",
1438         api_name        => "open-ils.actor.user.transactions.have_balance.total",
1439         notes           => <<"  NOTES");
1440         Returns an object/hash of transaction, circ, title where transaction = an open 
1441         user transaction that has a balance (mbts objects), circ is the attached 
1442         circluation, and title is the title the circ points to
1443         Params are login_session, user_id
1444         Optional third parameter is the transaction type.  defaults to all
1445         NOTES
1446
1447
1448
1449 sub user_transactions {
1450         my( $self, $client, $login_session, $user_id, $type ) = @_;
1451
1452         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1453                 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1454         return $evt if $evt;
1455         
1456         my $api = $self->api_name();
1457         my $trans;
1458         my @xact;
1459
1460         if(defined($type)) { @xact = (xact_type =>  $type); 
1461
1462         } else { @xact = (); }
1463
1464         if($api =~ /have_charge/o) {
1465
1466                 $trans = $apputils->simple_scalar_request( 
1467                         "open-ils.cstore",
1468                         "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1469                         { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1470
1471         } elsif($api =~ /have_balance/o) {
1472
1473                 $trans =  $apputils->simple_scalar_request( 
1474                         "open-ils.cstore",
1475                         "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1476                         { usr => $user_id, balance_owed => { "<>" => 0 }, @xact });
1477
1478         } else {
1479
1480                 $trans =  $apputils->simple_scalar_request( 
1481                         "open-ils.cstore",
1482                         "open-ils.cstore.direct.money.open_billable_transaction_summary.search.atomic",
1483                         { usr => $user_id, @xact });
1484         }
1485
1486         if($api =~ /total/o) { 
1487                 my $total = 0.0;
1488                 for my $t (@$trans) {
1489                         $total += $t->balance_owed;
1490                 }
1491
1492                 $logger->debug("Total balance owed by user $user_id: $total");
1493                 return $total;
1494         }
1495
1496         if($api =~ /count/o) { return scalar @$trans; }
1497         if($api !~ /fleshed/o) { return $trans; }
1498
1499         my @resp;
1500         for my $t (@$trans) {
1501                         
1502                 if( $t->xact_type ne 'circulation' ) {
1503                         push @resp, {transaction => $t};
1504                         next;
1505                 }
1506
1507                 my $circ = $apputils->simple_scalar_request(
1508                                 "open-ils.cstore",
1509                                 "open-ils.cstore.direct.action.circulation.retrieve",
1510                                 $t->id );
1511
1512                 next unless $circ;
1513
1514                 my $title = $apputils->simple_scalar_request(
1515                         "open-ils.storage", 
1516                         "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1517                         $circ->target_copy );
1518
1519                 next unless $title;
1520
1521                 my $u = OpenILS::Utils::ModsParser->new();
1522                 $u->start_mods_batch($title->marc());
1523                 my $mods = $u->finish_mods_batch();
1524                 $mods->doc_id($title->id) if $mods;
1525
1526                 push @resp, {transaction => $t, circ => $circ, record => $mods };
1527
1528         }
1529
1530         return \@resp; 
1531
1532
1533
1534 __PACKAGE__->register_method(
1535         method  => "user_transaction_retrieve",
1536         api_name        => "open-ils.actor.user.transaction.fleshed.retrieve",
1537         argc            => 1,
1538         notes           => <<"  NOTES");
1539         Returns a fleshedtransaction record
1540         NOTES
1541 __PACKAGE__->register_method(
1542         method  => "user_transaction_retrieve",
1543         api_name        => "open-ils.actor.user.transaction.retrieve",
1544         argc            => 1,
1545         notes           => <<"  NOTES");
1546         Returns a transaction record
1547         NOTES
1548 sub user_transaction_retrieve {
1549         my( $self, $client, $login_session, $bill_id ) = @_;
1550
1551         my $trans = $apputils->simple_scalar_request( 
1552                 "open-ils.cstore",
1553                 "open-ils.cstore.direct.money.billable_transaction_summary.retrieve",
1554                 $bill_id
1555         );
1556
1557         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1558                 $login_session, $trans->usr, 'VIEW_USER_TRANSACTIONS' );
1559         return $evt if $evt;
1560         
1561         my $api = $self->api_name();
1562         if($api !~ /fleshed/o) { return $trans; }
1563
1564         if( $trans->xact_type ne 'circulation' ) {
1565                 $logger->debug("Returning non-circ transaction");
1566                 return {transaction => $trans};
1567         }
1568
1569         my $circ = $apputils->simple_scalar_request(
1570                         "open-ils.cstore",
1571                         "open-ils..direct.action.circulation.retrieve",
1572                         $trans->id );
1573
1574         return {transaction => $trans} unless $circ;
1575         $logger->debug("Found the circ transaction");
1576
1577         my $title = $apputils->simple_scalar_request(
1578                 "open-ils.storage", 
1579                 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1580                 $circ->target_copy );
1581
1582         return {transaction => $trans, circ => $circ } unless $title;
1583         $logger->debug("Found the circ title");
1584
1585         my $mods;
1586         try {
1587                 my $u = OpenILS::Utils::ModsParser->new();
1588                 $u->start_mods_batch($title->marc());
1589                 $mods = $u->finish_mods_batch();
1590         } otherwise {
1591                 if ($title->id == -1) {
1592                         my $copy = $apputils->simple_scalar_request(
1593                                 "open-ils.cstore",
1594                                 "open-ils.cstore.direct.asset.copy.retrieve",
1595                                 $circ->target_copy );
1596
1597                         $mods = new Fieldmapper::metabib::virtual_record;
1598                         $mods->doc_id(-1);
1599                         $mods->title($copy->dummy_title);
1600                         $mods->author($copy->dummy_author);
1601                 }
1602         };
1603
1604         $logger->debug("MODSized the circ title");
1605
1606         return {transaction => $trans, circ => $circ, record => $mods };
1607 }
1608
1609
1610 __PACKAGE__->register_method(
1611         method  => "hold_request_count",
1612         api_name        => "open-ils.actor.user.hold_requests.count",
1613         argc            => 1,
1614         notes           => <<"  NOTES");
1615         Returns hold ready/total counts
1616         NOTES
1617 sub hold_request_count {
1618         my( $self, $client, $login_session, $userid ) = @_;
1619
1620         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1621                 $login_session, $userid, 'VIEW_HOLD' );
1622         return $evt if $evt;
1623         
1624
1625         my $holds = $apputils->simple_scalar_request(
1626                         "open-ils.cstore",
1627                         "open-ils.cstore.direct.action.hold_request.search.atomic",
1628                         { 
1629                                 usr => $userid,
1630                                 fulfillment_time => {"=" => undef },
1631                                 cancel_time => undef,
1632                         }
1633         );
1634
1635         my @ready;
1636         for my $h (@$holds) {
1637                 next unless $h->capture_time and $h->current_copy;
1638
1639                 my $copy = $apputils->simple_scalar_request(
1640                         "open-ils.cstore",
1641                         "open-ils.cstore.direct.asset.copy.retrieve",
1642                         $h->current_copy
1643                 );
1644
1645                 if ($copy and $copy->status == 8) {
1646                         push @ready, $h;
1647                 }
1648         }
1649
1650         return { total => scalar(@$holds), ready => scalar(@ready) };
1651 }
1652
1653
1654 __PACKAGE__->register_method(
1655         method  => "checkedout_count",
1656         api_name        => "open-ils.actor.user.checked_out.count__",
1657         argc            => 1,
1658         notes           => <<"  NOTES");
1659         Returns a transaction record
1660         NOTES
1661
1662 # XXX Deprecate Me
1663 sub checkedout_count {
1664         my( $self, $client, $login_session, $userid ) = @_;
1665
1666         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1667                 $login_session, $userid, 'VIEW_CIRCULATIONS' );
1668         return $evt if $evt;
1669         
1670         my $circs = $apputils->simple_scalar_request(
1671                         "open-ils.cstore",
1672                         "open-ils.cstore.direct.action.circulation.search.atomic",
1673                         { usr => $userid, stop_fines => undef }
1674                         #{ usr => $userid, checkin_time => {"=" => undef } }
1675         );
1676
1677         my $parser = DateTime::Format::ISO8601->new;
1678
1679         my (@out,@overdue);
1680         for my $c (@$circs) {
1681                 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1682                 my $due = $due_dt->epoch;
1683
1684                 if ($due < DateTime->today->epoch) {
1685                         push @overdue, $c;
1686                 }
1687         }
1688
1689         return { total => scalar(@$circs), overdue => scalar(@overdue) };
1690 }
1691
1692
1693 __PACKAGE__->register_method(
1694         method          => "checked_out",
1695         api_name                => "open-ils.actor.user.checked_out",
1696         argc                    => 2,
1697         signature       => q/
1698                 Returns a structure of circulations objects sorted by
1699                 out, overdue, lost, claims_returned, long_overdue.
1700                 A list of IDs are returned of each type.
1701                 lost, long_overdue, and claims_returned circ will not
1702                 be "finished" (there is an outstanding balance or some 
1703                 other pending action on the circ). 
1704
1705                 The .count method also includes a 'total' field which 
1706                 sums all "open" circs
1707         /
1708 );
1709
1710 __PACKAGE__->register_method(
1711         method          => "checked_out",
1712         api_name                => "open-ils.actor.user.checked_out.count",
1713         argc                    => 2,
1714         signature       => q/@see open-ils.actor.user.checked_out/
1715 );
1716
1717 sub checked_out {
1718         my( $self, $conn, $auth, $userid ) = @_;
1719
1720         my $e = new_editor(authtoken=>$auth);
1721         return $e->event unless $e->checkauth;
1722
1723         if( $userid ne $e->requestor->id ) {
1724                 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1725         }
1726
1727         my $count = $self->api_name =~ /count/;
1728         return _checked_out( $count, $e, $userid );
1729 }
1730
1731 sub _checked_out {
1732         my( $iscount, $e, $userid ) = @_;
1733
1734         my $circs = $e->search_action_circulation( 
1735                 { usr => $userid, stop_fines => undef });
1736
1737         my $parser = DateTime::Format::ISO8601->new;
1738
1739         # split the circs up into overdue and not-overdue circs
1740         my (@out,@overdue);
1741         for my $c (@$circs) {
1742                 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1743                 my $due = $due_dt->epoch;
1744                 if ($due < DateTime->today->epoch) {
1745                         push @overdue, $c->id;
1746                 } else {
1747                         push @out, $c->id;
1748                 }
1749         }
1750
1751         # grab all of the lost, claims-returned, and longoverdue circs
1752         #my $open = $e->search_action_circulation(
1753         #       {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1754
1755
1756         # these items have stop_fines, but no xact_finish, so money
1757         # is owed on them and they have not been checked in
1758         my $open = $e->search_action_circulation(
1759                 {
1760                         usr                             => $userid, 
1761                         stop_fines              => { '!=' => undef }, 
1762                         xact_finish             => undef,
1763                         checkin_time    => undef,
1764                 }
1765         );
1766
1767
1768         my( @lost, @cr, @lo );
1769         for my $c (@$open) {
1770                 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1771                 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1772                 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1773         }
1774
1775
1776         if( $iscount ) {
1777                 return {
1778                         total           => @$circs + @lost + @cr + @lo,
1779                         out             => scalar(@out),
1780                         overdue => scalar(@overdue),
1781                         lost            => scalar(@lost),
1782                         claims_returned => scalar(@cr),
1783                         long_overdue            => scalar(@lo)
1784                 };
1785         }
1786
1787         return {
1788                 out             => \@out,
1789                 overdue => \@overdue,
1790                 lost            => \@lost,
1791                 claims_returned => \@cr,
1792                 long_overdue            => \@lo
1793         };
1794 }
1795
1796
1797
1798 __PACKAGE__->register_method(
1799         method          => "checked_in_with_fines",
1800         api_name                => "open-ils.actor.user.checked_in_with_fines",
1801         argc                    => 2,
1802         signature       => q/@see open-ils.actor.user.checked_out/
1803 );
1804 sub checked_in_with_fines {
1805         my( $self, $conn, $auth, $userid ) = @_;
1806
1807         my $e = new_editor(authtoken=>$auth);
1808         return $e->event unless $e->checkauth;
1809
1810         if( $userid ne $e->requestor->id ) {
1811                 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1812         }
1813
1814         # money is owed on these items and they are checked in
1815         my $open = $e->search_action_circulation(
1816                 {
1817                         usr                             => $userid, 
1818                         xact_finish             => undef,
1819                         checkin_time    => { "!=" => undef },
1820                 }
1821         );
1822
1823
1824         my( @lost, @cr, @lo );
1825         for my $c (@$open) {
1826                 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1827                 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1828                 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1829         }
1830
1831         return {
1832                 lost            => \@lost,
1833                 claims_returned => \@cr,
1834                 long_overdue            => \@lo
1835         };
1836 }
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846 __PACKAGE__->register_method(
1847         method  => "user_transaction_history",
1848         api_name        => "open-ils.actor.user.transactions.history",
1849         argc            => 1,
1850         notes           => <<"  NOTES");
1851         Returns a list of billable transaction ids for a user, optionally by type
1852         NOTES
1853 __PACKAGE__->register_method(
1854         method  => "user_transaction_history",
1855         api_name        => "open-ils.actor.user.transactions.history.have_charge",
1856         argc            => 1,
1857         notes           => <<"  NOTES");
1858         Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1859         NOTES
1860 __PACKAGE__->register_method(
1861         method  => "user_transaction_history",
1862         api_name        => "open-ils.actor.user.transactions.history.have_balance",
1863         argc            => 1,
1864         notes           => <<"  NOTES");
1865         Returns a list of billable transaction ids for a user that have a balance, optionally by type
1866         NOTES
1867
1868 =head old
1869 sub _user_transaction_history {
1870         my( $self, $client, $login_session, $user_id, $type ) = @_;
1871
1872         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1873                 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1874         return $evt if $evt;
1875
1876         my $api = $self->api_name();
1877         my @xact;
1878         my @charge;
1879         my @balance;
1880
1881         @xact = (xact_type =>  $type) if(defined($type));
1882         @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1883         @charge  = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1884
1885         $logger->debug("searching for transaction history: @xact : @balance, @charge");
1886
1887         my $trans = $apputils->simple_scalar_request( 
1888                 "open-ils.cstore",
1889                 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1890                 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1891
1892         return [ map { $_->id } @$trans ];
1893 }
1894 =cut
1895
1896
1897 sub user_transaction_history {
1898         my( $self, $conn, $auth, $userid, $type ) = @_;
1899         my $e = new_editor(authtoken=>$auth);
1900         return $e->event unless $e->checkauth;
1901         return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1902
1903         my $api = $self->api_name;
1904         my @xact = (xact_type =>  $type) if(defined($type));
1905         my @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1906         my @charge  = (last_billing_ts => { "!=" => undef }) if $api =~ /have_charge/;
1907
1908         return $e->search_money_billable_transaction_summary(
1909                 [
1910                         { usr => $userid, @xact, @charge, @balance }, 
1911                         { order_by => 'xact_start DESC' }
1912                 ], {idlist => 1});
1913 }
1914
1915
1916
1917 __PACKAGE__->register_method(
1918         method  => "user_perms",
1919         api_name        => "open-ils.actor.permissions.user_perms.retrieve",
1920         argc            => 1,
1921         notes           => <<"  NOTES");
1922         Returns a list of permissions
1923         NOTES
1924 sub user_perms {
1925         my( $self, $client, $authtoken, $user ) = @_;
1926
1927         my( $staff, $evt ) = $apputils->checkses($authtoken);
1928         return $evt if $evt;
1929
1930         $user ||= $staff->id;
1931
1932         if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1933                 return $evt;
1934         }
1935
1936         return $apputils->simple_scalar_request(
1937                 "open-ils.storage",
1938                 "open-ils.storage.permission.user_perms.atomic",
1939                 $user);
1940 }
1941
1942 __PACKAGE__->register_method(
1943         method  => "retrieve_perms",
1944         api_name        => "open-ils.actor.permissions.retrieve",
1945         notes           => <<"  NOTES");
1946         Returns a list of permissions
1947         NOTES
1948 sub retrieve_perms {
1949         my( $self, $client ) = @_;
1950         return $apputils->simple_scalar_request(
1951                 "open-ils.cstore",
1952                 "open-ils.cstore.direct.permission.perm_list.search.atomic",
1953                 { id => { '!=' => undef } }
1954         );
1955 }
1956
1957 __PACKAGE__->register_method(
1958         method  => "retrieve_groups",
1959         api_name        => "open-ils.actor.groups.retrieve",
1960         notes           => <<"  NOTES");
1961         Returns a list of user groupss
1962         NOTES
1963 sub retrieve_groups {
1964         my( $self, $client ) = @_;
1965         return new_editor()->retrieve_all_permission_grp_tree();
1966 }
1967
1968 __PACKAGE__->register_method(
1969         method  => "retrieve_org_address",
1970         api_name        => "open-ils.actor.org_unit.address.retrieve",
1971         notes           => <<'  NOTES');
1972         Returns an org_unit address by ID
1973         @param An org_address ID
1974         NOTES
1975 sub retrieve_org_address {
1976         my( $self, $client, $id ) = @_;
1977         return $apputils->simple_scalar_request(
1978                 "open-ils.cstore",
1979                 "open-ils.cstore.direct.actor.org_address.retrieve",
1980                 $id
1981         );
1982 }
1983
1984 __PACKAGE__->register_method(
1985         method  => "retrieve_groups_tree",
1986         api_name        => "open-ils.actor.groups.tree.retrieve",
1987         notes           => <<"  NOTES");
1988         Returns a list of user groups
1989         NOTES
1990 sub retrieve_groups_tree {
1991         my( $self, $client ) = @_;
1992         return new_editor()->search_permission_grp_tree(
1993                 [
1994                         { parent => undef},
1995                         {       
1996                                 flesh                           => 10, 
1997                                 flesh_fields    => { pgt => ["children"] }, 
1998                                 order_by                        => { pgt => 'name'}
1999                         }
2000                 ]
2001         )->[0];
2002 }
2003
2004
2005 # turns an org list into an org tree
2006 =head old code
2007 sub build_group_tree {
2008
2009         my( $self, $grplist) = @_;
2010
2011         return $grplist unless ( 
2012                         ref($grplist) and @$grplist > 1 );
2013
2014         my @list = sort { $a->name cmp $b->name } @$grplist;
2015
2016         my $root;
2017         for my $grp (@list) {
2018
2019                 if ($grp and !defined($grp->parent)) {
2020                         $root = $grp;
2021                         next;
2022                 }
2023                 my ($parent) = grep { $_->id == $grp->parent} @list;
2024
2025                 $parent->children([]) unless defined($parent->children); 
2026                 push( @{$parent->children}, $grp );
2027         }
2028
2029         return $root;
2030 }
2031 =cut
2032
2033
2034 __PACKAGE__->register_method(
2035         method  => "add_user_to_groups",
2036         api_name        => "open-ils.actor.user.set_groups",
2037         notes           => <<"  NOTES");
2038         Adds a user to one or more permission groups
2039         NOTES
2040
2041 sub add_user_to_groups {
2042         my( $self, $client, $authtoken, $userid, $groups ) = @_;
2043
2044         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2045                 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2046         return $evt if $evt;
2047
2048         ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2049                 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2050         return $evt if $evt;
2051
2052         $apputils->simplereq(
2053                 'open-ils.storage',
2054                 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2055                 
2056         for my $group (@$groups) {
2057                 my $link = Fieldmapper::permission::usr_grp_map->new;
2058                 $link->grp($group);
2059                 $link->usr($userid);
2060
2061                 my $id = $apputils->simplereq(
2062                         'open-ils.storage',
2063                         'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2064         }
2065
2066         return 1;
2067 }
2068
2069 __PACKAGE__->register_method(
2070         method  => "get_user_perm_groups",
2071         api_name        => "open-ils.actor.user.get_groups",
2072         notes           => <<"  NOTES");
2073         Retrieve a user's permission groups.
2074         NOTES
2075
2076
2077 sub get_user_perm_groups {
2078         my( $self, $client, $authtoken, $userid ) = @_;
2079
2080         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2081                 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2082         return $evt if $evt;
2083
2084         return $apputils->simplereq(
2085                 'open-ils.cstore',
2086                 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2087 }       
2088
2089
2090
2091 __PACKAGE__->register_method (
2092         method          => 'register_workstation',
2093         api_name                => 'open-ils.actor.workstation.register.override',
2094         signature       => q/@see open-ils.actor.workstation.register/);
2095
2096 __PACKAGE__->register_method (
2097         method          => 'register_workstation',
2098         api_name                => 'open-ils.actor.workstation.register',
2099         signature       => q/
2100                 Registers a new workstion in the system
2101                 @param authtoken The login session key
2102                 @param name The name of the workstation id
2103                 @param owner The org unit that owns this workstation
2104                 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2105                 if the name is already in use.
2106         /);
2107
2108 sub _register_workstation {
2109         my( $self, $connection, $authtoken, $name, $owner ) = @_;
2110         my( $requestor, $evt ) = $U->checkses($authtoken);
2111         return $evt if $evt;
2112         $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2113         return $evt if $evt;
2114
2115         my $ws = $U->cstorereq(
2116                 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2117         return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2118
2119         $ws = Fieldmapper::actor::workstation->new;
2120         $ws->owning_lib($owner);
2121         $ws->name($name);
2122
2123         my $id = $U->storagereq(
2124                 'open-ils.storage.direct.actor.workstation.create', $ws );
2125         return $U->DB_UPDATE_FAILED($ws) unless $id;
2126
2127         $ws->id($id);
2128         return $ws->id();
2129 }
2130
2131 sub register_workstation {
2132         my( $self, $conn, $authtoken, $name, $owner ) = @_;
2133
2134         my $e = new_editor(authtoken=>$authtoken, xact=>1);
2135         return $e->event unless $e->checkauth;
2136         return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2137         my $existing = $e->search_actor_workstation({name => $name});
2138
2139         if( @$existing ) {
2140                 if( $self->api_name =~ /override/o ) {
2141                         return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2142                         return $e->event unless $e->delete_actor_workstation($$existing[0]);
2143                 } else {
2144                         return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2145                 }
2146         }
2147
2148         my $ws = Fieldmapper::actor::workstation->new;
2149         $ws->owning_lib($owner);
2150         $ws->name($name);
2151         $e->create_actor_workstation($ws) or return $e->event;
2152         $e->commit;
2153         return $ws->id; # note: editor sets the id on the new object for us
2154 }
2155
2156
2157 __PACKAGE__->register_method (
2158         method          => 'fetch_patron_note',
2159         api_name                => 'open-ils.actor.note.retrieve.all',
2160         signature       => q/
2161                 Returns a list of notes for a given user
2162                 Requestor must have VIEW_USER permission if pub==false and
2163                 @param authtoken The login session key
2164                 @param args Hash of params including
2165                         patronid : the patron's id
2166                         pub : true if retrieving only public notes
2167         /
2168 );
2169
2170 sub fetch_patron_note {
2171         my( $self, $conn, $authtoken, $args ) = @_;
2172         my $patronid = $$args{patronid};
2173
2174         my($reqr, $evt) = $U->checkses($authtoken);
2175
2176         my $patron;
2177         ($patron, $evt) = $U->fetch_user($patronid);
2178         return $evt if $evt;
2179
2180         if($$args{pub}) {
2181                 if( $patronid ne $reqr->id ) {
2182                         $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2183                         return $evt if $evt;
2184                 }
2185                 return $U->cstorereq(
2186                         'open-ils.cstore.direct.actor.usr_note.search.atomic', 
2187                         { usr => $patronid, pub => 't' } );
2188         }
2189
2190         $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2191         return $evt if $evt;
2192
2193         return $U->cstorereq(
2194                 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2195 }
2196
2197 __PACKAGE__->register_method (
2198         method          => 'create_user_note',
2199         api_name                => 'open-ils.actor.note.create',
2200         signature       => q/
2201                 Creates a new note for the given user
2202                 @param authtoken The login session key
2203                 @param note The note object
2204         /
2205 );
2206 sub create_user_note {
2207         my( $self, $conn, $authtoken, $note ) = @_;
2208         my( $reqr, $patron, $evt ) = 
2209                 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2210         return $evt if $evt;
2211         $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2212
2213         $note->creator($reqr->id);
2214         my $id = $U->storagereq(
2215                 'open-ils.storage.direct.actor.usr_note.create', $note );
2216         return $U->DB_UPDATE_FAILED($note) unless $id;
2217         return $id;
2218 }
2219
2220
2221 __PACKAGE__->register_method (
2222         method          => 'delete_user_note',
2223         api_name                => 'open-ils.actor.note.delete',
2224         signature       => q/
2225                 Deletes a note for the given user
2226                 @param authtoken The login session key
2227                 @param noteid The note id
2228         /
2229 );
2230 sub delete_user_note {
2231         my( $self, $conn, $authtoken, $noteid ) = @_;
2232
2233         my $note = $U->cstorereq(
2234                 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2235         return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2236
2237         my( $reqr, $patron, $evt ) = 
2238                 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2239         return $evt if $evt;
2240         $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2241
2242         my $stat = $U->storagereq(
2243                 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2244         return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2245         return $stat;
2246 }
2247
2248
2249 __PACKAGE__->register_method (
2250         method          => 'update_user_note',
2251         api_name                => 'open-ils.actor.note.update',
2252         signature       => q/
2253                 @param authtoken The login session key
2254                 @param note The note
2255         /
2256 );
2257
2258 sub update_user_note {
2259         my( $self, $conn, $auth, $note ) = @_;
2260         my $e = new_editor(authtoken=>$auth, xact=>1);
2261         return $e->event unless $e->checkauth;
2262         my $patron = $e->retrieve_actor_user($note->usr)
2263                 or return $e->event;
2264         return $e->event unless 
2265                 $e->allowed('UPDATE_USER', $patron->home_ou);
2266         $e->update_actor_user_note($note)
2267                 or return $e->event;
2268         $e->commit;
2269         return 1;
2270 }
2271
2272
2273
2274
2275 __PACKAGE__->register_method (
2276         method          => 'create_closed_date',
2277         api_name        => 'open-ils.actor.org_unit.closed_date.create',
2278         signature       => q/
2279                 Creates a new closing entry for the given org_unit
2280                 @param authtoken The login session key
2281                 @param note The closed_date object
2282         /
2283 );
2284 sub create_closed_date {
2285         my( $self, $conn, $authtoken, $cd ) = @_;
2286
2287         my( $user, $evt ) = $U->checkses($authtoken);
2288         return $evt if $evt;
2289
2290         $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2291         return $evt if $evt;
2292
2293         $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2294
2295         my $id = $U->storagereq(
2296                 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2297         return $U->DB_UPDATE_FAILED($cd) unless $id;
2298         return $id;
2299 }
2300
2301
2302 __PACKAGE__->register_method (
2303         method          => 'delete_closed_date',
2304         api_name        => 'open-ils.actor.org_unit.closed_date.delete',
2305         signature       => q/
2306                 Deletes a closing entry for the given org_unit
2307                 @param authtoken The login session key
2308                 @param noteid The close_date id
2309         /
2310 );
2311 sub delete_closed_date {
2312         my( $self, $conn, $authtoken, $cd ) = @_;
2313
2314         my( $user, $evt ) = $U->checkses($authtoken);
2315         return $evt if $evt;
2316
2317         my $cd_obj;
2318         ($cd_obj, $evt) = fetch_closed_date($cd);
2319         return $evt if $evt;
2320
2321         $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2322         return $evt if $evt;
2323
2324         $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2325
2326         my $stat = $U->storagereq(
2327                 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2328         return $U->DB_UPDATE_FAILED($cd) unless $stat;
2329         return $stat;
2330 }
2331
2332
2333 __PACKAGE__->register_method(
2334         method => 'usrname_exists',
2335         api_name        => 'open-ils.actor.username.exists',
2336         signature => q/
2337                 Returns 1 if the requested username exists, returns 0 otherwise
2338         /
2339 );
2340
2341 sub usrname_exists {
2342         my( $self, $conn, $auth, $usrname ) = @_;
2343         my $e = new_editor(authtoken=>$auth);
2344         return $e->event unless $e->checkauth;
2345         my $a = $e->search_actor_user({usrname => $usrname}, {idlist=>1});
2346         return $$a[0] if $a and @$a;
2347         return 0;
2348 }
2349
2350 __PACKAGE__->register_method(
2351         method => 'barcode_exists',
2352         api_name        => 'open-ils.actor.barcode.exists',
2353         signature => q/
2354                 Returns 1 if the requested barcode exists, returns 0 otherwise
2355         /
2356 );
2357
2358 sub barcode_exists {
2359         my( $self, $conn, $auth, $barcode ) = @_;
2360         my $e = new_editor(authtoken=>$auth);
2361         return $e->event unless $e->checkauth;
2362         my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2363         return $$a[0] if $a and @$a;
2364         return 0;
2365 }
2366
2367
2368 __PACKAGE__->register_method(
2369         method => 'retrieve_net_levels',
2370         api_name        => 'open-ils.actor.net_access_level.retrieve.all',
2371 );
2372
2373 sub retrieve_net_levels {
2374         my( $self, $conn, $auth ) = @_;
2375         my $e = new_editor(authtoken=>$auth);
2376         return $e->event unless $e->checkauth;
2377         return $e->retrieve_all_config_net_access_level();
2378 }
2379
2380
2381
2382 1;
2383