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