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