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