]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Actor.pm
moved constants out to their own module, updating code
[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                 my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) );
1744                 my $due = $due_dt->epoch;
1745                 if ($due < DateTime->today->epoch) {
1746                         push @overdue, $c->id;
1747                 } else {
1748                         push @out, $c->id;
1749                 }
1750         }
1751
1752         # grab all of the lost, claims-returned, and longoverdue circs
1753         #my $open = $e->search_action_circulation(
1754         #       {usr => $userid, stop_fines => { '!=' => undef }, xact_finish => undef });
1755
1756
1757         # these items have stop_fines, but no xact_finish, so money
1758         # is owed on them and they have not been checked in
1759         my $open = $e->search_action_circulation(
1760                 {
1761                         usr                             => $userid, 
1762                         stop_fines              => { '!=' => undef }, 
1763                         xact_finish             => undef,
1764                         checkin_time    => undef,
1765                 }
1766         );
1767
1768
1769         my( @lost, @cr, @lo );
1770         for my $c (@$open) {
1771                 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1772                 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1773                 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1774         }
1775
1776
1777         if( $iscount ) {
1778                 return {
1779                         total           => @$circs + @lost + @cr + @lo,
1780                         out             => scalar(@out),
1781                         overdue => scalar(@overdue),
1782                         lost            => scalar(@lost),
1783                         claims_returned => scalar(@cr),
1784                         long_overdue            => scalar(@lo)
1785                 };
1786         }
1787
1788         return {
1789                 out             => \@out,
1790                 overdue => \@overdue,
1791                 lost            => \@lost,
1792                 claims_returned => \@cr,
1793                 long_overdue            => \@lo
1794         };
1795 }
1796
1797
1798
1799 __PACKAGE__->register_method(
1800         method          => "checked_in_with_fines",
1801         api_name                => "open-ils.actor.user.checked_in_with_fines",
1802         argc                    => 2,
1803         signature       => q/@see open-ils.actor.user.checked_out/
1804 );
1805 sub checked_in_with_fines {
1806         my( $self, $conn, $auth, $userid ) = @_;
1807
1808         my $e = new_editor(authtoken=>$auth);
1809         return $e->event unless $e->checkauth;
1810
1811         if( $userid ne $e->requestor->id ) {
1812                 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1813         }
1814
1815         # money is owed on these items and they are checked in
1816         my $open = $e->search_action_circulation(
1817                 {
1818                         usr                             => $userid, 
1819                         xact_finish             => undef,
1820                         checkin_time    => { "!=" => undef },
1821                 }
1822         );
1823
1824
1825         my( @lost, @cr, @lo );
1826         for my $c (@$open) {
1827                 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1828                 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1829                 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1830         }
1831
1832         return {
1833                 lost            => \@lost,
1834                 claims_returned => \@cr,
1835                 long_overdue            => \@lo
1836         };
1837 }
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847 __PACKAGE__->register_method(
1848         method  => "user_transaction_history",
1849         api_name        => "open-ils.actor.user.transactions.history",
1850         argc            => 1,
1851         notes           => <<"  NOTES");
1852         Returns a list of billable transaction ids for a user, optionally by type
1853         NOTES
1854 __PACKAGE__->register_method(
1855         method  => "user_transaction_history",
1856         api_name        => "open-ils.actor.user.transactions.history.have_charge",
1857         argc            => 1,
1858         notes           => <<"  NOTES");
1859         Returns a list of billable transaction ids for a user that have an initial charge, optionally by type
1860         NOTES
1861 __PACKAGE__->register_method(
1862         method  => "user_transaction_history",
1863         api_name        => "open-ils.actor.user.transactions.history.have_balance",
1864         argc            => 1,
1865         notes           => <<"  NOTES");
1866         Returns a list of billable transaction ids for a user that have a balance, optionally by type
1867         NOTES
1868
1869 =head old
1870 sub _user_transaction_history {
1871         my( $self, $client, $login_session, $user_id, $type ) = @_;
1872
1873         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1874                 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1875         return $evt if $evt;
1876
1877         my $api = $self->api_name();
1878         my @xact;
1879         my @charge;
1880         my @balance;
1881
1882         @xact = (xact_type =>  $type) if(defined($type));
1883         @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1884         @charge  = (last_billing_ts => { "<>" => undef }) if $api =~ /have_charge/;
1885
1886         $logger->debug("searching for transaction history: @xact : @balance, @charge");
1887
1888         my $trans = $apputils->simple_scalar_request( 
1889                 "open-ils.cstore",
1890                 "open-ils.cstore.direct.money.billable_transaction_summary.search.atomic",
1891                 { usr => $user_id, @xact, @charge, @balance }, { order_by => { mbts => 'xact_start DESC' } });
1892
1893         return [ map { $_->id } @$trans ];
1894 }
1895 =cut
1896
1897
1898 sub user_transaction_history {
1899         my( $self, $conn, $auth, $userid, $type ) = @_;
1900         my $e = new_editor(authtoken=>$auth);
1901         return $e->event unless $e->checkauth;
1902         return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS');
1903
1904         my $api = $self->api_name;
1905         my @xact = (xact_type =>  $type) if(defined($type));
1906         my @balance = (balance_owed => { "!=" => 0}) if($api =~ /have_balance/);
1907         my @charge  = (last_billing_ts => { "!=" => undef }) if $api =~ /have_charge/;
1908
1909         return $e->search_money_billable_transaction_summary(
1910                 [
1911                         { usr => $userid, @xact, @charge, @balance }, 
1912                         { order_by => 'xact_start DESC' }
1913                 ], {idlist => 1});
1914 }
1915
1916
1917
1918 __PACKAGE__->register_method(
1919         method  => "user_perms",
1920         api_name        => "open-ils.actor.permissions.user_perms.retrieve",
1921         argc            => 1,
1922         notes           => <<"  NOTES");
1923         Returns a list of permissions
1924         NOTES
1925 sub user_perms {
1926         my( $self, $client, $authtoken, $user ) = @_;
1927
1928         my( $staff, $evt ) = $apputils->checkses($authtoken);
1929         return $evt if $evt;
1930
1931         $user ||= $staff->id;
1932
1933         if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1934                 return $evt;
1935         }
1936
1937         return $apputils->simple_scalar_request(
1938                 "open-ils.storage",
1939                 "open-ils.storage.permission.user_perms.atomic",
1940                 $user);
1941 }
1942
1943 __PACKAGE__->register_method(
1944         method  => "retrieve_perms",
1945         api_name        => "open-ils.actor.permissions.retrieve",
1946         notes           => <<"  NOTES");
1947         Returns a list of permissions
1948         NOTES
1949 sub retrieve_perms {
1950         my( $self, $client ) = @_;
1951         return $apputils->simple_scalar_request(
1952                 "open-ils.cstore",
1953                 "open-ils.cstore.direct.permission.perm_list.search.atomic",
1954                 { id => { '!=' => undef } }
1955         );
1956 }
1957
1958 __PACKAGE__->register_method(
1959         method  => "retrieve_groups",
1960         api_name        => "open-ils.actor.groups.retrieve",
1961         notes           => <<"  NOTES");
1962         Returns a list of user groupss
1963         NOTES
1964 sub retrieve_groups {
1965         my( $self, $client ) = @_;
1966         return new_editor()->retrieve_all_permission_grp_tree();
1967 }
1968
1969 __PACKAGE__->register_method(
1970         method  => "retrieve_org_address",
1971         api_name        => "open-ils.actor.org_unit.address.retrieve",
1972         notes           => <<'  NOTES');
1973         Returns an org_unit address by ID
1974         @param An org_address ID
1975         NOTES
1976 sub retrieve_org_address {
1977         my( $self, $client, $id ) = @_;
1978         return $apputils->simple_scalar_request(
1979                 "open-ils.cstore",
1980                 "open-ils.cstore.direct.actor.org_address.retrieve",
1981                 $id
1982         );
1983 }
1984
1985 __PACKAGE__->register_method(
1986         method  => "retrieve_groups_tree",
1987         api_name        => "open-ils.actor.groups.tree.retrieve",
1988         notes           => <<"  NOTES");
1989         Returns a list of user groups
1990         NOTES
1991 sub retrieve_groups_tree {
1992         my( $self, $client ) = @_;
1993         return new_editor()->search_permission_grp_tree(
1994                 [
1995                         { parent => undef},
1996                         {       
1997                                 flesh                           => 10, 
1998                                 flesh_fields    => { pgt => ["children"] }, 
1999                                 order_by                        => { pgt => 'name'}
2000                         }
2001                 ]
2002         )->[0];
2003 }
2004
2005
2006 # turns an org list into an org tree
2007 =head old code
2008 sub build_group_tree {
2009
2010         my( $self, $grplist) = @_;
2011
2012         return $grplist unless ( 
2013                         ref($grplist) and @$grplist > 1 );
2014
2015         my @list = sort { $a->name cmp $b->name } @$grplist;
2016
2017         my $root;
2018         for my $grp (@list) {
2019
2020                 if ($grp and !defined($grp->parent)) {
2021                         $root = $grp;
2022                         next;
2023                 }
2024                 my ($parent) = grep { $_->id == $grp->parent} @list;
2025
2026                 $parent->children([]) unless defined($parent->children); 
2027                 push( @{$parent->children}, $grp );
2028         }
2029
2030         return $root;
2031 }
2032 =cut
2033
2034
2035 __PACKAGE__->register_method(
2036         method  => "add_user_to_groups",
2037         api_name        => "open-ils.actor.user.set_groups",
2038         notes           => <<"  NOTES");
2039         Adds a user to one or more permission groups
2040         NOTES
2041
2042 sub add_user_to_groups {
2043         my( $self, $client, $authtoken, $userid, $groups ) = @_;
2044
2045         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2046                 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2047         return $evt if $evt;
2048
2049         ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2050                 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2051         return $evt if $evt;
2052
2053         $apputils->simplereq(
2054                 'open-ils.storage',
2055                 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2056                 
2057         for my $group (@$groups) {
2058                 my $link = Fieldmapper::permission::usr_grp_map->new;
2059                 $link->grp($group);
2060                 $link->usr($userid);
2061
2062                 my $id = $apputils->simplereq(
2063                         'open-ils.storage',
2064                         'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2065         }
2066
2067         return 1;
2068 }
2069
2070 __PACKAGE__->register_method(
2071         method  => "get_user_perm_groups",
2072         api_name        => "open-ils.actor.user.get_groups",
2073         notes           => <<"  NOTES");
2074         Retrieve a user's permission groups.
2075         NOTES
2076
2077
2078 sub get_user_perm_groups {
2079         my( $self, $client, $authtoken, $userid ) = @_;
2080
2081         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2082                 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2083         return $evt if $evt;
2084
2085         return $apputils->simplereq(
2086                 'open-ils.cstore',
2087                 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2088 }       
2089
2090
2091
2092 __PACKAGE__->register_method (
2093         method          => 'register_workstation',
2094         api_name                => 'open-ils.actor.workstation.register.override',
2095         signature       => q/@see open-ils.actor.workstation.register/);
2096
2097 __PACKAGE__->register_method (
2098         method          => 'register_workstation',
2099         api_name                => 'open-ils.actor.workstation.register',
2100         signature       => q/
2101                 Registers a new workstion in the system
2102                 @param authtoken The login session key
2103                 @param name The name of the workstation id
2104                 @param owner The org unit that owns this workstation
2105                 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2106                 if the name is already in use.
2107         /);
2108
2109 sub _register_workstation {
2110         my( $self, $connection, $authtoken, $name, $owner ) = @_;
2111         my( $requestor, $evt ) = $U->checkses($authtoken);
2112         return $evt if $evt;
2113         $evt = $U->check_perms($requestor->id, $owner, 'REGISTER_WORKSTATION');
2114         return $evt if $evt;
2115
2116         my $ws = $U->cstorereq(
2117                 'open-ils.cstore.direct.actor.workstation.search', { name => $name } );
2118         return OpenILS::Event->new('WORKSTATION_NAME_EXISTS') if $ws;
2119
2120         $ws = Fieldmapper::actor::workstation->new;
2121         $ws->owning_lib($owner);
2122         $ws->name($name);
2123
2124         my $id = $U->storagereq(
2125                 'open-ils.storage.direct.actor.workstation.create', $ws );
2126         return $U->DB_UPDATE_FAILED($ws) unless $id;
2127
2128         $ws->id($id);
2129         return $ws->id();
2130 }
2131
2132 sub register_workstation {
2133         my( $self, $conn, $authtoken, $name, $owner ) = @_;
2134
2135         my $e = new_editor(authtoken=>$authtoken, xact=>1);
2136         return $e->event unless $e->checkauth;
2137         return $e->event unless $e->allowed('REGISTER_WORKSTATION'); # XXX rely on editor perms
2138         my $existing = $e->search_actor_workstation({name => $name});
2139
2140         if( @$existing ) {
2141                 if( $self->api_name =~ /override/o ) {
2142                         return $e->event unless $e->allowed('DELETE_WORKSTATION'); # XXX rely on editor perms
2143                         return $e->event unless $e->delete_actor_workstation($$existing[0]);
2144                 } else {
2145                         return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2146                 }
2147         }
2148
2149         my $ws = Fieldmapper::actor::workstation->new;
2150         $ws->owning_lib($owner);
2151         $ws->name($name);
2152         $e->create_actor_workstation($ws) or return $e->event;
2153         $e->commit;
2154         return $ws->id; # note: editor sets the id on the new object for us
2155 }
2156
2157
2158 __PACKAGE__->register_method (
2159         method          => 'fetch_patron_note',
2160         api_name                => 'open-ils.actor.note.retrieve.all',
2161         signature       => q/
2162                 Returns a list of notes for a given user
2163                 Requestor must have VIEW_USER permission if pub==false and
2164                 @param authtoken The login session key
2165                 @param args Hash of params including
2166                         patronid : the patron's id
2167                         pub : true if retrieving only public notes
2168         /
2169 );
2170
2171 sub fetch_patron_note {
2172         my( $self, $conn, $authtoken, $args ) = @_;
2173         my $patronid = $$args{patronid};
2174
2175         my($reqr, $evt) = $U->checkses($authtoken);
2176
2177         my $patron;
2178         ($patron, $evt) = $U->fetch_user($patronid);
2179         return $evt if $evt;
2180
2181         if($$args{pub}) {
2182                 if( $patronid ne $reqr->id ) {
2183                         $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2184                         return $evt if $evt;
2185                 }
2186                 return $U->cstorereq(
2187                         'open-ils.cstore.direct.actor.usr_note.search.atomic', 
2188                         { usr => $patronid, pub => 't' } );
2189         }
2190
2191         $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2192         return $evt if $evt;
2193
2194         return $U->cstorereq(
2195                 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2196 }
2197
2198 __PACKAGE__->register_method (
2199         method          => 'create_user_note',
2200         api_name                => 'open-ils.actor.note.create',
2201         signature       => q/
2202                 Creates a new note for the given user
2203                 @param authtoken The login session key
2204                 @param note The note object
2205         /
2206 );
2207 sub create_user_note {
2208         my( $self, $conn, $authtoken, $note ) = @_;
2209         my( $reqr, $patron, $evt ) = 
2210                 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2211         return $evt if $evt;
2212         $logger->activity("user ".$reqr->id." creating note for user ".$note->usr);
2213
2214         $note->creator($reqr->id);
2215         my $id = $U->storagereq(
2216                 'open-ils.storage.direct.actor.usr_note.create', $note );
2217         return $U->DB_UPDATE_FAILED($note) unless $id;
2218         return $id;
2219 }
2220
2221
2222 __PACKAGE__->register_method (
2223         method          => 'delete_user_note',
2224         api_name                => 'open-ils.actor.note.delete',
2225         signature       => q/
2226                 Deletes a note for the given user
2227                 @param authtoken The login session key
2228                 @param noteid The note id
2229         /
2230 );
2231 sub delete_user_note {
2232         my( $self, $conn, $authtoken, $noteid ) = @_;
2233
2234         my $note = $U->cstorereq(
2235                 'open-ils.cstore.direct.actor.usr_note.retrieve', $noteid);
2236         return OpenILS::Event->new('ACTOR_USER_NOTE_NOT_FOUND') unless $note;
2237
2238         my( $reqr, $patron, $evt ) = 
2239                 $U->checkses_requestor($authtoken, $note->usr, 'UPDATE_USER');
2240         return $evt if $evt;
2241         $logger->activity("user ".$reqr->id." deleting note [$noteid] for user ".$note->usr);
2242
2243         my $stat = $U->storagereq(
2244                 'open-ils.storage.direct.actor.usr_note.delete', $noteid );
2245         return $U->DB_UPDATE_FAILED($note) unless defined $stat;
2246         return $stat;
2247 }
2248
2249
2250 __PACKAGE__->register_method (
2251         method          => 'update_user_note',
2252         api_name                => 'open-ils.actor.note.update',
2253         signature       => q/
2254                 @param authtoken The login session key
2255                 @param note The note
2256         /
2257 );
2258
2259 sub update_user_note {
2260         my( $self, $conn, $auth, $note ) = @_;
2261         my $e = new_editor(authtoken=>$auth, xact=>1);
2262         return $e->event unless $e->checkauth;
2263         my $patron = $e->retrieve_actor_user($note->usr)
2264                 or return $e->event;
2265         return $e->event unless 
2266                 $e->allowed('UPDATE_USER', $patron->home_ou);
2267         $e->update_actor_user_note($note)
2268                 or return $e->event;
2269         $e->commit;
2270         return 1;
2271 }
2272
2273
2274
2275
2276 __PACKAGE__->register_method (
2277         method          => 'create_closed_date',
2278         api_name        => 'open-ils.actor.org_unit.closed_date.create',
2279         signature       => q/
2280                 Creates a new closing entry for the given org_unit
2281                 @param authtoken The login session key
2282                 @param note The closed_date object
2283         /
2284 );
2285 sub create_closed_date {
2286         my( $self, $conn, $authtoken, $cd ) = @_;
2287
2288         my( $user, $evt ) = $U->checkses($authtoken);
2289         return $evt if $evt;
2290
2291         $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2292         return $evt if $evt;
2293
2294         $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2295
2296         my $id = $U->storagereq(
2297                 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2298         return $U->DB_UPDATE_FAILED($cd) unless $id;
2299         return $id;
2300 }
2301
2302
2303 __PACKAGE__->register_method (
2304         method          => 'delete_closed_date',
2305         api_name        => 'open-ils.actor.org_unit.closed_date.delete',
2306         signature       => q/
2307                 Deletes a closing entry for the given org_unit
2308                 @param authtoken The login session key
2309                 @param noteid The close_date id
2310         /
2311 );
2312 sub delete_closed_date {
2313         my( $self, $conn, $authtoken, $cd ) = @_;
2314
2315         my( $user, $evt ) = $U->checkses($authtoken);
2316         return $evt if $evt;
2317
2318         my $cd_obj;
2319         ($cd_obj, $evt) = fetch_closed_date($cd);
2320         return $evt if $evt;
2321
2322         $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2323         return $evt if $evt;
2324
2325         $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2326
2327         my $stat = $U->storagereq(
2328                 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2329         return $U->DB_UPDATE_FAILED($cd) unless $stat;
2330         return $stat;
2331 }
2332
2333
2334 __PACKAGE__->register_method(
2335         method => 'usrname_exists',
2336         api_name        => 'open-ils.actor.username.exists',
2337         signature => q/
2338                 Returns 1 if the requested username exists, returns 0 otherwise
2339         /
2340 );
2341
2342 sub usrname_exists {
2343         my( $self, $conn, $auth, $usrname ) = @_;
2344         my $e = new_editor(authtoken=>$auth);
2345         return $e->event unless $e->checkauth;
2346         my $a = $e->search_actor_user({usrname => $usrname}, {idlist=>1});
2347         return $$a[0] if $a and @$a;
2348         return 0;
2349 }
2350
2351 __PACKAGE__->register_method(
2352         method => 'barcode_exists',
2353         api_name        => 'open-ils.actor.barcode.exists',
2354         signature => q/
2355                 Returns 1 if the requested barcode exists, returns 0 otherwise
2356         /
2357 );
2358
2359 sub barcode_exists {
2360         my( $self, $conn, $auth, $barcode ) = @_;
2361         my $e = new_editor(authtoken=>$auth);
2362         return $e->event unless $e->checkauth;
2363         my $a = $e->search_actor_card({barcode => $barcode}, {idlist=>1});
2364         return $$a[0] if $a and @$a;
2365         return 0;
2366 }
2367
2368
2369 __PACKAGE__->register_method(
2370         method => 'retrieve_net_levels',
2371         api_name        => 'open-ils.actor.net_access_level.retrieve.all',
2372 );
2373
2374 sub retrieve_net_levels {
2375         my( $self, $conn, $auth ) = @_;
2376         my $e = new_editor(authtoken=>$auth);
2377         return $e->event unless $e->checkauth;
2378         return $e->retrieve_all_config_net_access_level();
2379 }
2380
2381
2382
2383 1;
2384