]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/actor.pm
added closed-date overlap check
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Storage / Publisher / actor.pm
1 package OpenILS::Application::Storage::Publisher::actor;
2 use base qw/OpenILS::Application::Storage/;
3 use OpenILS::Application::Storage::CDBI::actor;
4 use OpenSRF::Utils::Logger qw/:level/;
5 use OpenILS::Utils::Fieldmapper;
6
7 use DateTime;           
8 use DateTime::Format::ISO8601;  
9                                                 
10                                                                                                 
11 my $_dt_parser = DateTime::Format::ISO8601->new;    
12
13 my $log = 'OpenSRF::Utils::Logger';
14
15 sub org_closed_overlap {
16         my $self = shift;
17         my $client = shift;
18         my $ou = shift;
19         my $date = shift;
20         my $direction = shift || 0;
21
22         return undef unless ($date && $ou);
23
24         my $t = actor::org_unit::closed_date->table;
25         my $sql = <<"   SQL";
26                 SELECT  *
27                   FROM  $t
28                   WHERE ? between close_start and close_end
29                         AND org_unit = ?
30                   ORDER BY close_start ASC, close_end DESC
31         SQL
32
33         my $sth = actor::org_unit::closed_date->db_Main->prepare( $sql );
34         $sth->execute($date, $ou);
35         
36         my ($begin, $end);
37         while (my $closure = $sth->fetchrow_hashref) {
38                 $begin ||= $closure->{close_start};
39                 $end = $closure->{close_end};
40
41                 my $before = $_dt_parser->parse_datetime( $begin );
42                 $before->subtract( days => 1 );
43                 my $after = $_dt_parser->parse_datetime( $end );
44                 $after->add( days => 1 );
45
46                 if ( $direction <= 0 ) {
47                         while ( my $_b = org_closed_overlap($self, $client, $ou, $before->ymd, -1 ) ) {
48                                 $before = $_dt_parser->parse_datetime( $_b->{start} );
49                         }
50                 }
51
52                 if ( $direction >= 0 ) {
53                         while ( my $_a = org_closed_overlap($self, $client, $ou, $after->ymd, 1 ) ) {
54                                 $after = $_dt_parser->parse_datetime( $_a->{end} );
55                         }
56                 }
57
58                 $begin = $before->ymd;
59                 $end = $after->ymd;
60         }
61
62         if ($begin && $end) {
63                 return { start => $begin, end => $end };
64         }
65
66         return;
67 }
68 __PACKAGE__->register_method(
69         api_name        => 'open-ils.storage.actor.org_unit.closed_date.overlap',
70         api_level       => 1,
71         method          => 'org_closed_overlap',
72 );
73
74 sub user_by_barcode {
75         my $self = shift;
76         my $client = shift;
77         my @barcodes = shift;
78
79         return undef unless @barcodes;
80
81         for my $card ( actor::card->search( { barcode => @barcodes } ) ) {
82                 next unless $card;
83                 if (@barcodes == 1) {
84                         return $card->usr->to_fieldmapper;
85                 }
86                 $client->respond( $card->usr->to_fieldmapper);
87         }
88         return undef;
89 }
90 __PACKAGE__->register_method(
91         api_name        => 'open-ils.storage.direct.actor.user.search.barcode',
92         api_level       => 1,
93         method          => 'user_by_barcode',
94         stream          => 1,
95         cachable        => 1,
96 );
97
98 sub lost_barcodes {
99         my $self = shift;
100         my $client = shift;
101
102         my $c = actor::card->table;
103         my $p = actor::user->table;
104
105         my $sql = "SELECT c.barcode FROM $c c JOIN $p p ON (c.usr = p.id) WHERE p.card <> c.id";
106
107         my $list = actor::user->db_Main->selectcol_arrayref($sql);
108         for my $bc ( @$list ) {
109                 $client->respond($bc);
110         }
111         return undef;
112 }
113 __PACKAGE__->register_method(
114         api_name        => 'open-ils.storage.actor.user.lost_barcodes',
115         api_level       => 1,
116         stream          => 1,
117         method          => 'lost_barcodes',
118         signature       => <<'  NOTE',
119                 Returns an array of barcodes that belong to lost cards.
120                 @return array of barcodes
121         NOTE
122 );
123
124 sub expired_barcodes {
125         my $self = shift;
126         my $client = shift;
127
128         my $c = actor::card->table;
129         my $p = actor::user->table;
130
131         my $sql = "SELECT c.barcode FROM $c c JOIN $p p ON (c.usr = p.id) WHERE p.expire_date < CURRENT_DATE";
132
133         my $list = actor::user->db_Main->selectcol_arrayref($sql);
134         for my $bc ( @$list ) {
135                 $client->respond($bc);
136         }
137         return undef;
138 }
139 __PACKAGE__->register_method(
140         api_name        => 'open-ils.storage.actor.user.expired_barcodes',
141         api_level       => 1,
142         stream          => 1,
143         method          => 'expired_barcodes',
144         signature       => <<'  NOTE',
145                 Returns an array of barcodes that are currently expired.
146                 @return array of barcodes
147         NOTE
148 );
149
150 sub barred_barcodes {
151         my $self = shift;
152         my $client = shift;
153
154         my $c = actor::card->table;
155         my $p = actor::user->table;
156
157         my $sql = "SELECT c.barcode FROM $c c JOIN $p p ON (c.usr = p.id) WHERE p.barred IS TRUE";
158
159         my $list = actor::user->db_Main->selectcol_arrayref($sql);
160         for my $bc ( @$list ) {
161                 $client->respond($bc);
162         }
163         return undef;
164 }
165 __PACKAGE__->register_method(
166         api_name        => 'open-ils.storage.actor.user.barred_barcodes',
167         api_level       => 1,
168         stream          => 1,
169         method          => 'barred_barcodes',
170         signature       => <<'  NOTE',
171                 Returns an array of barcodes that are currently barred.
172                 @return array of barcodes
173         NOTE
174 );
175
176 sub penalized_barcodes {
177         my $self = shift;
178         my $client = shift;
179         my @ignore = @_;
180
181         my $c = actor::card->table;
182         my $p = actor::user_standing_penalty->table;
183
184         my $sql = "SELECT c.barcode FROM $c c JOIN $p p USING (usr)";
185
186         if (@ignore) {
187                 $sql .= ' WHERE penalty_type NOT IN ('. join(',', map { '?' } @ignore) . ')';
188         }
189
190         $sql .= ' GROUP BY c.barcode;';
191
192         my $list = actor::user->db_Main->selectcol_arrayref($sql, {}, @ignore);
193         for my $bc ( @$list ) {
194                 $client->respond($bc);
195         }
196         return undef;
197 }
198 __PACKAGE__->register_method(
199         api_name        => 'open-ils.storage.actor.user.penalized_barcodes',
200         api_level       => 1,
201         stream          => 1,
202         method          => 'penalized_barcodes',
203         signature       => <<'  NOTE',
204                 Returns an array of barcodes that have penalties not listed
205                 as a parameter.  Supply a list of any penalty types that should
206                 not stop a patron from checking out materials.
207
208                 @param ignore_list Penalty type to ignore
209                 @return array of barcodes
210         NOTE
211 );
212
213
214 sub patron_search {
215         my $self = shift;
216         my $client = shift;
217         my $search = shift;
218         my $limit = shift || 1000;
219         my $sort = shift;
220         $sort = ['family_name','first_given_name'] unless ($$sort[0]);
221
222         # group 0 = user
223         # group 1 = address
224         # group 2 = phone, ident
225
226         my $usr = join ' AND ', map { "LOWER($_) ~ ?" } grep { ''.$$search{$_}{group} eq '0' } keys %$search;
227         my @usrv = map { "^$$search{$_}{value}" } grep { ''.$$search{$_}{group} eq '0' } keys %$search;
228
229         my $addr = join ' AND ', map { "LOWER($_) ~ ?" } grep { ''.$$search{$_}{group} eq '1' } keys %$search;
230         my @addrv = map { "^$$search{$_}{value}" } grep { ''.$$search{$_}{group} eq '1' } keys %$search;
231
232         my $pv = $$search{phone}{value};
233         my $iv = $$search{ident}{value};
234         my $nv = $$search{name}{value};
235
236         my $phone = '';
237         my @ps;
238         my @phonev;
239         if ($pv) {
240                 for my $p ( qw/day_phone evening_phone other_phone/ ) {
241                         push @ps, "LOWER($p) ~ ?";
242                         push @phonev, "^$pv";
243                 }
244                 $phone = '(' . join(' OR ', @ps) . ')';
245         }
246
247         my $ident = '';
248         my @is;
249         my @identv;
250         if ($iv) {
251                 for my $i ( qw/ident_value ident_value2/ ) {
252                         push @is, "LOWER($i) ~ ?";
253                         push @identv, "^$iv";
254                 }
255                 $ident = '(' . join(' OR ', @is) . ')';
256         }
257
258         my $name = '';
259         my @ns;
260         my @namev;
261         if (0 && $nv) {
262                 for my $n ( qw/first_given_name second_given_name family_name/ ) {
263                         push @ns, "LOWER($i) ~ ?";
264                         push @namev, "^$nv";
265                 }
266                 $name = '(' . join(' OR ', @ns) . ')';
267         }
268
269         my $usr_where = join ' AND ', grep { $_ } ($usr,$phone,$ident,$name);
270         my $addr_where = $addr;
271
272
273         my $u_table = actor::user->table;
274         my $a_table = actor::user_address->table;
275
276         my $u_select = "SELECT id as id FROM $u_table u WHERE $usr_where";
277         my $a_select = "SELECT usr as id FROM $a_table a WHERE $addr_where";
278
279         my $select = '';
280         if ($usr_where) {
281                 if ($addr_where) {
282                         $select = "$u_select INTERSECT $a_select";
283                 } else {
284                         $select = $u_select;
285                 }
286         } elsif ($addr_where) {
287                 $select = $a_select;
288         } else {
289                 return undef;
290         }
291
292         my $order_by = join ', ', map { 'users.'. $_} @$sort;
293                 
294         $select = "SELECT users.id FROM $u_table AS users JOIN ($select) AS search USING (id) ORDER BY $order_by LIMIT $limit";
295
296         return actor::user->db_Main->selectcol_arrayref($select, {}, map {lc($_)} (@usrv,@phonev,@identv,@namev,@addrv));
297 }
298 __PACKAGE__->register_method(
299         api_name        => 'open-ils.storage.actor.user.crazy_search',
300         api_level       => 1,
301         method          => 'patron_search',
302 );
303
304 =comment not gonna use it...
305
306 sub fleshed_search {
307         my $self = shift;
308         my $client = shift;
309         my $searches = shift;
310
311         return undef unless (defined $searches);
312
313         for my $usr ( actor::user->search( $searches ) ) {
314                 next unless $usr;
315                 $client->respond( flesh_user( $usr ) );
316         }
317         return undef;
318 }
319 __PACKAGE__->register_method(
320         api_name        => 'open-ils.storage.fleshed.actor.user.search',
321         api_level       => 1,
322         method          => 'fleshed_search',
323         stream          => 1,
324         cachable        => 1,
325 );
326
327 sub fleshed_search_like {
328         my $self = shift;
329         my $client = shift;
330         my $searches = shift;
331
332         return undef unless (defined $searches);
333
334         for my $usr ( actor::user->search_like( $searches ) ) {
335                 next unless $usr;
336                 $client->respond( flesh_user( $usr ) );
337         }
338         return undef;
339 }
340 __PACKAGE__->register_method(
341         api_name        => 'open-ils.storage.fleshed.actor.user.search_like',
342         api_level       => 1,
343         method          => 'user_by_barcode',
344         stream          => 1,
345         cachable        => 1,
346 );
347
348 sub retrieve_fleshed_user {
349         my $self = shift;
350         my $client = shift;
351         my @ids = shift;
352
353         return undef unless @ids;
354
355         @ids = ($ids[0]) unless ($self->api_name =~ /batch/o); 
356
357         $client->respond( flesh_user( actor::user->retrieve( $_ ) ) ) for ( @ids );
358
359         return undef;
360 }
361 __PACKAGE__->register_method(
362         api_name        => 'open-ils.storage.fleshed.actor.user.retrieve',
363         api_level       => 1,
364         method          => 'retrieve_fleshed_user',
365         cachable        => 1,
366 );
367 __PACKAGE__->register_method(
368         api_name        => 'open-ils.storage.fleshed.actor.user.batch.retrieve',
369         api_level       => 1,
370         method          => 'retrieve_fleshed_user',
371         stream          => 1,
372         cachable        => 1,
373 );
374
375 sub flesh_user {
376         my $usr = shift;
377
378
379         my $standing = $usr->standing;
380         my $profile = $usr->profile;
381         my $ident_type = $usr->ident_type;
382                 
383         my $maddress = $usr->mailing_address;
384         my $baddress = $usr->billing_address;
385         my $card = $usr->card;
386
387         my @addresses = $usr->addresses;
388         my @cards = $usr->cards;
389
390         my $usr_fm = $usr->to_fieldmapper;
391         $usr_fm->standing( $standing->to_fieldmapper );
392         $usr_fm->profile( $profile->to_fieldmapper );
393         $usr_fm->ident_type( $ident_type->to_fieldmapper );
394
395         $usr_fm->card( $card->to_fieldmapper );
396         $usr_fm->mailing_address( $maddress->to_fieldmapper ) if ($maddress);
397         $usr_fm->billing_address( $baddress->to_fieldmapper ) if ($baddress);
398
399         $usr_fm->cards( [ map { $_->to_fieldmapper } @cards ] );
400         $usr_fm->addresses( [ map { $_->to_fieldmapper } @addresses ] );
401
402         return $usr_fm;
403 }
404
405 =cut
406
407 sub org_unit_list {
408         my $self = shift;
409         my $client = shift;
410
411         my $select =<<" SQL";
412         SELECT  *
413           FROM  actor.org_unit
414           ORDER BY CASE WHEN parent_ou IS NULL THEN 0 ELSE 1 END, name;
415         SQL
416
417         my $sth = actor::org_unit->db_Main->prepare_cached($select);
418         $sth->execute;
419
420         $client->respond( $_->to_fieldmapper ) for ( map { actor::org_unit->construct($_) } $sth->fetchall_hash );
421
422         return undef;
423 }
424 __PACKAGE__->register_method(
425         api_name        => 'open-ils.storage.direct.actor.org_unit.retrieve.all',
426         api_level       => 1,
427         stream          => 1,
428         method          => 'org_unit_list',
429 );
430
431 sub org_unit_type_list {
432         my $self = shift;
433         my $client = shift;
434
435         my $select =<<" SQL";
436         SELECT  *
437           FROM  actor.org_unit_type
438           ORDER BY depth, name;
439         SQL
440
441         my $sth = actor::org_unit_type->db_Main->prepare_cached($select);
442         $sth->execute;
443
444         $client->respond( $_->to_fieldmapper ) for ( map { actor::org_unit_type->construct($_) } $sth->fetchall_hash );
445
446         return undef;
447 }
448 __PACKAGE__->register_method(
449         api_name        => 'open-ils.storage.direct.actor.org_unit_type.retrieve.all',
450         api_level       => 1,
451         stream          => 1,
452         method          => 'org_unit_type_list',
453 );
454
455 sub org_unit_full_path {
456         my $self = shift;
457         my $client = shift;
458         my @binds = @_;
459
460         return undef unless (@binds);
461
462         my $func = 'actor.org_unit_full_path(?)';
463         $func = 'actor.org_unit_full_path(?,?)' if (@binds > 1);
464
465         my $sth = actor::org_unit->db_Main->prepare_cached("SELECT * FROM $func");
466         $sth->execute(@binds);
467
468         $client->respond( $_->to_fieldmapper ) for ( map { actor::org_unit->construct($_) } $sth->fetchall_hash );
469
470         return undef;
471 }
472 __PACKAGE__->register_method(
473         api_name        => 'open-ils.storage.actor.org_unit.full_path',
474         api_level       => 1,
475         stream          => 1,
476         method          => 'org_unit_full_path',
477 );
478
479 sub org_unit_ancestors {
480         my $self = shift;
481         my $client = shift;
482         my $id = shift;
483
484         return undef unless ($id);
485
486         my $func = 'actor.org_unit_ancestors(?)';
487
488         my $sth = actor::org_unit->db_Main->prepare_cached(<<"  SQL");
489                 SELECT  f.*
490                   FROM  $func f
491                         JOIN actor.org_unit_type t ON (f.ou_type = t.id)
492                   ORDER BY t.depth, f.name;
493         SQL
494         $sth->execute(''.$id);
495
496         $client->respond( $_->to_fieldmapper ) for ( map { actor::org_unit->construct($_) } $sth->fetchall_hash );
497
498         return undef;
499 }
500 __PACKAGE__->register_method(
501         api_name        => 'open-ils.storage.actor.org_unit.ancestors',
502         api_level       => 1,
503         stream          => 1,
504         method          => 'org_unit_ancestors',
505 );
506
507 sub org_unit_descendants {
508         my $self = shift;
509         my $client = shift;
510         my $id = shift;
511         my $depth = shift;
512
513         return undef unless ($id);
514
515         my $func = 'actor.org_unit_descendants(?)';
516         if (defined $depth) {
517                 $func = 'actor.org_unit_descendants(?,?)';
518         }
519
520         my $sth = actor::org_unit->db_Main->prepare_cached("SELECT * FROM $func");
521         $sth->execute(''.$id, ''.$depth) if (defined $depth);
522         $sth->execute(''.$id) unless (defined $depth);
523
524         $client->respond( $_->to_fieldmapper ) for ( map { actor::org_unit->construct($_) } $sth->fetchall_hash );
525
526         return undef;
527 }
528 __PACKAGE__->register_method(
529         api_name        => 'open-ils.storage.actor.org_unit.descendants',
530         api_level       => 1,
531         stream          => 1,
532         method          => 'org_unit_descendants',
533 );
534
535 sub profile_all {
536         my $self = shift;
537         my $client = shift;
538
539         for my $rec ( actor::profile->retrieve_all ) {
540                 $client->respond( $rec->to_fieldmapper );
541         }
542
543         return undef;
544 }
545 __PACKAGE__->register_method(
546         method          => 'profile_all',
547         api_name        => 'open-ils.storage.direct.actor.profile.retrieve.all',
548         argc            => 0,
549         stream          => 1,
550 );
551
552 sub fleshed_actor_stat_cat {
553         my $self = shift;
554         my $client = shift;
555         my @list = @_;
556         
557         @list = ($list[0]) unless ($self->api_name =~ /batch/o);
558
559         for my $sc (@list) {
560                 my $cat = actor::stat_cat->retrieve($sc);
561                 next unless ($cat);
562
563                 my $sc_fm = $cat->to_fieldmapper;
564                 $sc_fm->entries( [ map { $_->to_fieldmapper } $cat->entries ] );
565
566                 $client->respond( $sc_fm );
567
568         }
569
570         return undef;
571 }
572 __PACKAGE__->register_method(
573         api_name        => 'open-ils.storage.fleshed.actor.stat_cat.retrieve',
574         api_level       => 1,
575         argc            => 1,
576         method          => 'fleshed_actor_stat_cat',
577 );
578
579 __PACKAGE__->register_method(
580         api_name        => 'open-ils.storage.fleshed.actor.stat_cat.retrieve.batch',
581         api_level       => 1,
582         argc            => 1,
583         stream          => 1,
584         method          => 'fleshed_actor_stat_cat',
585 );
586
587 #XXX Fix stored proc calls
588 sub ranged_actor_stat_cat_all {
589         my $self = shift;
590         my $client = shift;
591         my $ou = ''.shift();
592         
593         return undef unless ($ou);
594         my $s_table = actor::stat_cat->table;
595
596         my $select = <<"        SQL";
597                 SELECT  s.*
598                   FROM  $s_table s
599                         JOIN actor.org_unit_full_path(?) p ON (p.id = s.owner)
600                   ORDER BY name
601         SQL
602
603         $fleshed = 0;
604         $fleshed = 1 if ($self->api_name =~ /fleshed/o);
605
606         my $sth = actor::stat_cat->db_Main->prepare_cached($select);
607         $sth->execute($ou);
608
609         for my $sc ( map { actor::stat_cat->construct($_) } $sth->fetchall_hash ) {
610                 my $sc_fm = $sc->to_fieldmapper;
611                 $sc_fm->entries(
612                         [ $self->method_lookup( 'open-ils.storage.ranged.actor.stat_cat_entry.search.stat_cat' )->run($ou,$sc->id) ]
613                 ) if ($fleshed);
614                 $client->respond( $sc_fm );
615         }
616
617         return undef;
618 }
619 __PACKAGE__->register_method(
620         api_name        => 'open-ils.storage.ranged.fleshed.actor.stat_cat.all',
621         api_level       => 1,
622         argc            => 1,
623         stream          => 1,
624         method          => 'ranged_actor_stat_cat_all',
625 );
626
627 __PACKAGE__->register_method(
628         api_name        => 'open-ils.storage.ranged.actor.stat_cat.all',
629         api_level       => 1,
630         argc            => 1,
631         stream          => 1,
632         method          => 'ranged_actor_stat_cat_all',
633 );
634
635 #XXX Fix stored proc calls
636 sub ranged_actor_stat_cat_entry {
637         my $self = shift;
638         my $client = shift;
639         my $ou = ''.shift();
640         my $sc = ''.shift();
641         
642         return undef unless ($ou);
643         my $s_table = actor::stat_cat_entry->table;
644
645         my $select = <<"        SQL";
646                 SELECT  s.*
647                   FROM  $s_table s
648                         JOIN actor.org_unit_full_path(?) p ON (p.id = s.owner)
649                   WHERE stat_cat = ?
650                   ORDER BY name
651         SQL
652
653         my $sth = actor::stat_cat->db_Main->prepare_cached($select);
654         $sth->execute($ou,$sc);
655
656         for my $sce ( map { actor::stat_cat_entry->construct($_) } $sth->fetchall_hash ) {
657                 $client->respond( $sce->to_fieldmapper );
658         }
659
660         return undef;
661 }
662 __PACKAGE__->register_method(
663         api_name        => 'open-ils.storage.ranged.actor.stat_cat_entry.search.stat_cat',
664         api_level       => 1,
665         stream          => 1,
666         method          => 'ranged_actor_stat_cat_entry',
667 );
668
669
670 1;