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