]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/asset.pm
safer holdings import
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Storage / Publisher / asset.pm
1 package OpenILS::Application::Storage::Publisher::asset;
2 use base qw/OpenILS::Application::Storage/;
3 #use OpenILS::Application::Storage::CDBI::asset;
4 #use OpenILS::Utils::Fieldmapper;
5 use OpenSRF::Utils::Logger qw/:level/;
6 use OpenSRF::EX qw/:try/;
7
8 #
9
10 my $log = 'OpenSRF::Utils::Logger';
11
12 use MARC::Record;
13 use MARC::File::XML;
14
15 #our $_default_subfield_map = {
16 #        call_number     => $cn,
17 #        barcode         => $bc,
18 #        owning_lib      => $ol,
19 #        circulating_lib => $cl,
20 #        copy_location   => $sl,
21 #        copy_number     => $num,
22 #        price           => $pr,
23 #        status          => $loc,
24 #        create_date     => $date,
25 #
26 #        legacy_item_type        => $it,
27 #        legacy_item_cat_1       => $ic1,
28 #        legacy_item_cat_2       => $ic2,
29 #};
30
31 sub import_xml_holdings {
32         my $self = shift;
33         my $client = shift;
34         my $editor = shift;
35         my $record = shift;
36         my $xml = shift;
37         my $tag = shift;
38         my $map = shift;
39         my $date_format = shift || 'mm/dd/yyyy';
40
41         my $r = MARC::Record->new_from_xml($xml);
42
43         for my $f ( $r->fields( $tag ) ) {
44                 next unless ($f->subfield( $map->{owning_lib} ));
45
46                 my ($ol,$cl);
47
48                 try {
49                         $ol = actor::org_unit->search( shortname => $f->subfield( $map->{owning_lib} ) )->next->id;
50                 } otherwise {
51                         $log->debug('Could not find library with shortname ['.$f->subfield( $map->{owning_lib} ).'] : '. shift(), ERROR);
52                 };
53                 
54                 try {
55                         $cl = actor::org_unit->search( shortname => $f->subfield( $map->{circulating_lib} ) )->next->id;
56                 } otherwise {
57                         $log->debug('Could not find library with shortname ['.$f->subfield( $map->{circulating_lib} ).'] : '. shift(), ERROR);
58                 };
59
60                 next unless ($ol && $cl);
61
62                 my $cn;
63                 try {
64                         $cn = asset::call_number->find_or_create(
65                                 { label         => $f->subfield( $map->{call_number} ),
66                                   owning_lib    => $ol,
67                                   record        => $record,
68                                   creator       => $editor,
69                                   editor        => $editor,
70                                 }
71                         );
72                 } otherwise {
73                         $log->debug('Could not find or create callnumber ['.$f->subfield( $map->{call_number} ).'] : '. shift(), ERROR);
74                 };
75
76                 next unless ($cn);
77
78                 my $create_date =  $f->subfield( $map->{create_date} );
79
80                 my ($m,$d,$y);
81                 if ($date_format eq 'mm/dd/yyyy') {
82                         ($m,$d,$y) = split '/', $create_date;
83
84                 } elsif ($date_format eq 'dd/mm/yyyy') {
85                         ($d,$m,$y) = split '/', $create_date;
86
87                 } elsif ($date_format eq 'mm-dd-yyyy') {
88                         ($m,$d,$y) = split '-', $create_date;
89
90                 } elsif ($date_format eq 'dd-mm-yyyy') {
91                         ($d,$m,$y) = split '-', $create_date;
92
93                 } elsif ($date_format eq 'yyyy-mm-dd') {
94                         ($y,$m,$d) = split '-', $create_date;
95
96                 } elsif ($date_format eq 'yyyy/mm/dd') {
97                         ($y,$m,$d) = split '/', $create_date;
98                 }
99
100                 if ($y == 0) {
101                         (undef,undef,undef,$d,$m,$y) = localtime;
102                         $m++;
103                         $y+=1900;
104                 }
105
106                 my $price = $f->subfield( $map->{price} );
107                 $price =~ s/[^0-9\.]+//gso;
108                 $price ||= '0.00';
109
110                 try {
111                         $cn->add_to_copies(
112                                 { circ_lib      => actor::org_unit->search( shortname => $f->subfield( $map->{circulating_lib} ) )->next->id,
113                                   copy_number   => $f->subfield( $map->{copy_number} ),
114                                   price         => $price,
115                                   barcode       => $f->subfield( $map->{barcode} ),
116                                   loan_duration => 2,
117                                   fine_level    => 2,
118                                   creator       => $editor,
119                                   editor        => $editor,
120                                   create_date   => sprintf('%04d-%02d-%02d',$y,$m,$d),
121                                 }
122                         );
123                 } otherwise {
124                         $log->debug('Could not create copy ['.$f->subfield( $map->{barcode} ).'] : '. shift(), ERROR);
125                 };
126         }
127
128         return 1;
129 }
130 __PACKAGE__->register_method(
131         method          => 'import_xml_holdings',
132         api_name        => 'open-ils.storage.asset.holdings.import.xml',
133         argc            => 5,
134         stream          => 0,
135 );
136
137 # XXX
138 # see /home/miker/cn_browse-test.sql for page up and down sql ...
139 # XXX
140
141 sub cn_browse_pagedown {
142         my $self = shift;
143         my $client = shift;
144
145         my %args = @_;
146
147         my $cn = uc($args{label});
148         my $org = $args{org_unit};
149         my $depth = $args{depth};
150         my $boundry_id = $args{boundry_id};
151         my $size = $args{page_size} || 20;
152         $size = int($size);
153
154         my $table = asset::call_number->table;
155
156         my $descendants = "actor.org_unit_descendants($org)";
157         if (defined $depth) {
158                 $descendants = "actor.org_unit_descendants($org,$depth)";
159         }
160
161         my $sql = <<"   SQL";
162                 select
163                         cn.label,
164                         cn.owning_lib,
165                         cn.record,
166                         cn.id
167                 from
168                         $table cn
169                         join $descendants d
170                                 on (d.id = cn.owning_lib)
171                 where
172                         upper(label) > ?
173                         or ( cn.id > ? and upper(label) = ? )
174                 order by upper(label), 4, 2
175                 limit $size;
176         SQL
177
178         my $sth = asset::call_number->db_Main->prepare($sql);
179         $sth->execute($cn, $boundry_id, $cn);
180         while ( my @row = $sth->fetchrow_array ) {
181                 $client->respond([@row]);
182         }
183         $sth->finish;
184
185         return undef;
186 }
187 __PACKAGE__->register_method(
188         method          => 'cn_browse_pagedown',
189         api_name        => 'open-ils.storage.asset.call_number.browse.page_down',
190         argc            => 4,
191         stream          => 1,
192 );
193
194 sub cn_browse_pageup {
195         my $self = shift;
196         my $client = shift;
197
198         my %args = @_;
199
200         my $cn = uc($args{label});
201         my $org = $args{org_unit};
202         my $depth = $args{depth};
203         my $boundry_id = $args{boundry_id};
204         my $size = $args{page_size} || 20;
205         $size = int($size);
206
207         my $table = asset::call_number->table;
208
209         my $descendants = "actor.org_unit_descendants($org)";
210         if (defined $depth) {
211                 $descendants = "actor.org_unit_descendants($org,$depth)";
212         }
213
214         my $sql = <<"   SQL";
215                 select * from (
216                         select
217                                 cn.label,
218                                 cn.owning_lib,
219                                 cn.record,
220                                 cn.id
221                         from
222                                 $table cn
223                                 join $descendants d
224                                         on (d.id = cn.owning_lib)
225                         where
226                                 upper(label) < ?
227                                 or ( cn.id < ? and upper(label) = ? )
228                         order by upper(label) desc, 4 desc, 2 desc
229                         limit $size
230                 ) as bar
231                 order by 1,4,2;
232         SQL
233
234         my $sth = asset::call_number->db_Main->prepare($sql);
235         $sth->execute($cn, $boundry_id, $cn);
236         while ( my @row = $sth->fetchrow_array ) {
237                 $client->respond([@row]);
238         }
239         $sth->finish;
240
241         return undef;
242 }
243 __PACKAGE__->register_method(
244         method          => 'cn_browse_pageup',
245         api_name        => 'open-ils.storage.asset.call_number.browse.page_up',
246         argc            => 4,
247         stream          => 1,
248 );
249
250 sub cn_browse_target {
251         my $self = shift;
252         my $client = shift;
253
254         my %args = @_;
255
256         my $cn = uc($args{label});
257         my $org = $args{org_unit};
258         my $depth = $args{depth};
259         my $size = $args{page_size} || 20;
260         my $topsize = $size / 2;
261         $topsize = int($topsize);
262         $bottomsize = $size - $topsize;
263
264         my $table = asset::call_number->table;
265
266         my $descendants = "actor.org_unit_descendants($org)";
267         if (defined $depth) {
268                 $descendants = "actor.org_unit_descendants($org,$depth)";
269         }
270
271         my $top_sql = <<"       SQL";
272                 select * from (
273                         select
274                                 cn.label,
275                                 cn.owning_lib,
276                                 cn.record,
277                                 cn.id
278                         from
279                                 $table cn
280                                 join $descendants d
281                                         on (d.id = cn.owning_lib)
282                         where
283                                 upper(label) < ?
284                         order by upper(label) desc, 4 desc, 2 desc
285                         limit $topsize
286                 ) as bar
287                 order by 1,4,2;
288         SQL
289
290         my $bottom_sql = <<"    SQL";
291                 select
292                         cn.label,
293                         cn.owning_lib,
294                         cn.record,
295                         cn.id
296                 from
297                         $table cn
298                         join $descendants d
299                                 on (d.id = cn.owning_lib)
300                 where
301                         upper(label) >= ?
302                 order by upper(label),4,2
303                 limit $bottomsize;
304         SQL
305
306         my $sth = asset::call_number->db_Main->prepare($top_sql);
307         $sth->execute($cn);
308         while ( my @row = $sth->fetchrow_array ) {
309                 $client->respond([@row]);
310         }
311         $sth->finish;
312
313         $sth = asset::call_number->db_Main->prepare($bottom_sql);
314         $sth->execute($cn);
315         while ( my @row = $sth->fetchrow_array ) {
316                 $client->respond([@row]);
317         }
318         $sth->finish;
319
320         return undef;
321 }
322 __PACKAGE__->register_method(
323         method          => 'cn_browse_target',
324         api_name        => 'open-ils.storage.asset.call_number.browse.target',
325         argc            => 4,
326         stream          => 1,
327 );
328
329
330 sub copy_proximity {
331         my $self = shift;
332         my $client = shift;
333
334         my $cp = shift;
335         my $org = shift;
336
337         return unless ($cp && $org);
338
339         $cp = $cp->id if (ref $cp);
340         $cp = asset::copy->retrieve($cp);
341         return unless $copy;
342         my $ol = $cp->call_number->owning_lib;
343
344         return asset::copy->db_Main->selectcol_arrayref('SELECT actor.org_unit_proximity(?,?)',{},"$ol","$org")->[0];
345 }
346 __PACKAGE__->register_method(
347         method          => 'copy_proximity',
348         api_name        => 'open-ils.storage.asset.copy.proximity',
349         argc            => 2,
350         stream          => 1,
351 );
352
353 sub asset_copy_location_all {
354         my $self = shift;
355         my $client = shift;
356
357         for my $rec ( asset::copy_location->retrieve_all ) {
358                 $client->respond( $rec->to_fieldmapper );
359         }
360
361         return undef;
362 }
363 __PACKAGE__->register_method(
364         method          => 'asset_copy_location_all',
365         api_name        => 'open-ils.storage.direct.asset.copy_location.retrieve.all',
366         argc            => 0,
367         stream          => 1,
368 );
369
370 # XXX arg, with the descendancy SPs...
371 sub ranged_asset_copy_location {
372         my $self = shift;
373         my $client = shift;
374         my @binds = @_;
375         
376         my $ctable = asset::copy_location->table;
377         
378         my $descendants = defined($binds[1]) ?
379                 "actor.org_unit_full_path(?, ?)" :
380                 "actor.org_unit_full_path(?)" ;
381
382         
383         my $sql = <<"   SQL";
384                 SELECT  DISTINCT c.*
385                   FROM  $ctable c
386                         JOIN $descendants d
387                                 ON (d.id = c.owning_lib)
388         SQL
389         
390         my $sth = asset::copy_location->db_Main->prepare($sql);
391         $sth->execute(@binds);
392         
393         while ( my $rec = $sth->fetchrow_hashref ) {
394         
395                 my $cnct = new Fieldmapper::asset::copy_location;
396                 map {$cnct->$_($$rec{$_})} keys %$rec;
397                 $client->respond( $cnct );
398         }
399
400         return undef;
401 }
402 __PACKAGE__->register_method(
403         method          => 'ranged_asset_copy_location',
404         api_name        => 'open-ils.storage.ranged.asset.copy_location.retrieve',
405         argc            => 1,
406         stream          => 1,
407 );
408
409
410 sub fleshed_copy {
411         my $self = shift;
412         my $client = shift;
413         my @ids = @_;
414
415         return undef unless (@ids);
416
417         @ids = ($ids[0]) unless ($self->api_name =~ /batch/o);
418
419         for my $id ( @ids ) {
420                 next unless $id;
421                 my $cp = asset::copy->retrieve($id);
422                 next unless $cp;
423
424                 my $cp_fm = $cp->to_fieldmapper;
425                 $cp_fm->circ_lib( $cp->circ_lib->to_fieldmapper );
426                 $cp_fm->location( $cp->location->to_fieldmapper );
427                 $cp_fm->status( $cp->status->to_fieldmapper );
428                 $cp_fm->stat_cat_entries( [ map { $_->to_fieldmapper } $cp->stat_cat_entries ] );
429
430                 $client->respond( $cp_fm );
431         }
432
433         return undef;
434 }
435 __PACKAGE__->register_method(
436         api_name        => 'open-ils.storage.fleshed.asset.copy.batch.retrieve',
437         method          => 'fleshed_copy',
438         argc            => 1,
439         stream          => 1,
440 );
441 __PACKAGE__->register_method(
442         api_name        => 'open-ils.storage.fleshed.asset.copy.retrieve',
443         method          => 'fleshed_copy',
444         argc            => 1,
445 );
446
447 sub fleshed_copy_by_barcode {
448         my $self = shift;
449         my $client = shift;
450         my $bc = ''.shift;
451
452         my ($cp) = asset::copy->search( { barcode => $bc } );
453
454         return undef unless ($cp);
455
456         my $cp_fm = $cp->to_fieldmapper;
457         $cp_fm->circ_lib( $cp->circ_lib->to_fieldmapper );
458         $cp_fm->location( $cp->location->to_fieldmapper );
459         $cp_fm->status( $cp->status->to_fieldmapper );
460
461         return $cp_fm;
462 }       
463 __PACKAGE__->register_method(
464         api_name        => 'open-ils.storage.fleshed.asset.copy.search.barcode',
465         method          => 'fleshed_copy_by_barcode',
466         argc            => 1,
467         stream          => 1,
468 );
469
470
471 #XXX Fix stored proc calls
472 sub fleshed_asset_stat_cat {
473         my $self = shift;
474         my $client = shift;
475         my @list = @_;
476
477         @list = ($list[0]) unless ($self->api_name =~ /batch/o);
478         for my $sc (@list) {
479                 my $cat = asset::stat_cat->retrieve($sc);
480                 
481                 next unless ($cat);
482
483                 my $sc_fm = $cat->to_fieldmapper;
484                 $sc_fm->entries( [ map { $_->to_fieldmapper } $cat->entries ] );
485                 $client->respond( $sc_fm );
486         }
487
488         return undef;
489 }
490 __PACKAGE__->register_method(
491         api_name        => 'open-ils.storage.fleshed.asset.stat_cat.retrieve',
492         api_level       => 1,
493         method          => 'fleshed_asset_stat_cat',
494 );
495
496 __PACKAGE__->register_method(
497         api_name        => 'open-ils.storage.fleshed.asset.stat_cat.retrieve.batch',
498         api_level       => 1,
499         stream          => 1,
500         method          => 'fleshed_asset_stat_cat',
501 );
502
503
504 #XXX Fix stored proc calls
505 sub ranged_asset_stat_cat {
506         my $self = shift;
507         my $client = shift;
508         my $ou = ''.shift();
509
510         return undef unless ($ou);
511         my $s_table = asset::stat_cat->table;
512
513         my $select = <<"        SQL";
514                 SELECT  s.*
515                   FROM  $s_table s
516                         JOIN actor.org_unit_full_path(?) p ON (p.id = s.owner)
517                   ORDER BY name
518         SQL
519
520         $fleshed = 0;
521         $fleshed = 1 if ($self->api_name =~ /fleshed/o);
522
523         my $sth = asset::stat_cat->db_Main->prepare_cached($select);
524         $sth->execute($ou);
525
526         for my $sc ( map { asset::stat_cat->construct($_) } $sth->fetchall_hash ) {
527                 my $sc_fm = $sc->to_fieldmapper;
528                 $sc_fm->entries(
529                         [ $self->method_lookup( 'open-ils.storage.ranged.asset.stat_cat_entry.search.stat_cat' )->run($ou,$sc->id) ]
530                 ) if ($fleshed);
531                 $client->respond( $sc_fm );
532         }
533
534         return undef;
535 }
536 __PACKAGE__->register_method(
537         api_name        => 'open-ils.storage.ranged.fleshed.asset.stat_cat.all',
538         api_level       => 1,
539         stream          => 1,
540         method          => 'ranged_asset_stat_cat',
541 );
542
543 __PACKAGE__->register_method(
544         api_name        => 'open-ils.storage.ranged.asset.stat_cat.all',
545         api_level       => 1,
546         stream          => 1,
547         method          => 'ranged_asset_stat_cat',
548 );
549
550
551 #XXX Fix stored proc calls
552 sub multiranged_asset_stat_cat {
553         my $self = shift;
554         my $client = shift;
555         my $ous = shift;
556
557         return undef unless (defined($ous) and @$ous);
558         my $s_table = asset::stat_cat->table;
559
560         my $select = <<"        SQL";
561                 SELECT  s.*
562                   FROM  $s_table s
563                   WHERE s.owner IN ( XXX )
564                   ORDER BY name
565         SQL
566
567         my $collector = ' INTERSECT ';
568         my $entry_method = 'open-ils.storage.multiranged.intersect.asset.stat_cat_entry.search.stat_cat';
569         if ($self->api_name =~ /union/o) {
570                 $collector = ' UNION ';
571                 $entry_method = 'open-ils.storage.multiranged.union.asset.stat_cat_entry.search.stat_cat';
572         }
573
574         my $binds = join($collector, map { 'SELECT id FROM actor.org_unit_full_path(?)' } grep {defined} @$ous);
575         $select =~ s/XXX/$binds/so;
576         
577         $fleshed = 0;
578         $fleshed = 1 if ($self->api_name =~ /fleshed/o);
579
580         my $sth = asset::stat_cat->db_Main->prepare_cached($select);
581         $sth->execute(map { "$_" } grep {defined} @$ous);
582
583         for my $sc ( map { asset::stat_cat->construct($_) } $sth->fetchall_hash ) {
584                 my $sc_fm = $sc->to_fieldmapper;
585                 $sc_fm->entries(
586                         [ $self->method_lookup( $entry_method )->run($ous, $sc->id) ]
587                 ) if ($fleshed);
588                 $client->respond( $sc_fm );
589         }
590
591         return undef;
592 }
593 __PACKAGE__->register_method(
594         api_name        => 'open-ils.storage.multiranged.intersect.fleshed.asset.stat_cat.all',
595         api_level       => 1,
596         stream          => 1,
597         method          => 'multiranged_asset_stat_cat',
598 );
599 __PACKAGE__->register_method(
600         api_name        => 'open-ils.storage.multiranged.union.fleshed.asset.stat_cat.all',
601         api_level       => 1,
602         stream          => 1,
603         method          => 'multiranged_asset_stat_cat',
604 );
605
606 #XXX Fix stored proc calls
607 sub ranged_asset_stat_cat_entry {
608         my $self = shift;
609         my $client = shift;
610         my $ou = ''.shift();
611         my $sc = ''.shift();
612
613         return undef unless ($ou);
614         my $s_table = asset::stat_cat_entry->table;
615
616         my $select = <<"        SQL";
617                 SELECT  s.*
618                   FROM  $s_table s
619                         JOIN actor.org_unit_full_path(?) p ON (p.id = s.owner)
620                   WHERE stat_cat = ?
621                   ORDER BY name
622         SQL
623
624         my $sth = asset::stat_cat->db_Main->prepare_cached($select);
625         $sth->execute($ou,$sc);
626
627         for my $sce ( map { asset::stat_cat_entry->construct($_) } $sth->fetchall_hash ) {
628                 $client->respond( $sce->to_fieldmapper );
629         }
630
631         return undef;
632 }
633 __PACKAGE__->register_method(
634         api_name        => 'open-ils.storage.ranged.asset.stat_cat_entry.search.stat_cat',
635         api_level       => 1,
636         stream          => 1,
637         method          => 'ranged_asset_stat_cat_entry',
638 );
639
640 #XXX Fix stored proc calls
641 sub multiranged_asset_stat_cat_entry {
642         my $self = shift;
643         my $client = shift;
644         my $ous = shift;
645         my $sc = ''.shift();
646
647         return undef unless (defined($ous) and @$ous);
648         my $s_table = asset::stat_cat_entry->table;
649
650         my $collector = ' INTERSECT ';
651         $collector = ' UNION ' if ($self->api_name =~ /union/o);
652
653         my $select = <<"        SQL";
654                 SELECT  s.*
655                   FROM  $s_table s
656                   WHERE s.owner IN ( XXX ) and s.stat_cat = ?
657                   ORDER BY value
658         SQL
659
660         my $binds = join($collector, map { 'SELECT id FROM actor.org_unit_full_path(?)' } grep {defined} @$ous);
661         $select =~ s/XXX/$binds/so;
662         
663         my $sth = asset::stat_cat->db_Main->prepare_cached($select);
664         $sth->execute(map {"$_"} @$ous,$sc);
665
666         for my $sce ( map { asset::stat_cat_entry->construct($_) } $sth->fetchall_hash ) {
667                 $client->respond( $sce->to_fieldmapper );
668         }
669
670         return undef;
671 }
672 __PACKAGE__->register_method(
673         api_name        => 'open-ils.storage.multiranged.intersect.asset.stat_cat_entry.search.stat_cat',
674         api_level       => 1,
675         stream          => 1,
676         method          => 'multiranged_asset_stat_cat_entry',
677 );
678 __PACKAGE__->register_method(
679         api_name        => 'open-ils.storage.multiranged.union.asset.stat_cat_entry.search.stat_cat',
680         api_level       => 1,
681         stream          => 1,
682         method          => 'multiranged_asset_stat_cat_entry',
683 );
684
685
686 sub cn_ranged_tree {
687         my $self = shift;
688         my $client = shift;
689         my $cn = shift;
690         my $ou = shift;
691         my $depth = shift || 0;
692
693         my $ou_list =
694                 actor::org_unit
695                         ->db_Main
696                         ->selectcol_arrayref(
697                                 'SELECT id FROM actor.org_unit_descendants(?,?)',
698                                 {},
699                                 $ou,
700                                 $depth
701                         );
702
703         return undef unless ($ou_list and @$ou_list);
704
705         $cn = asset::call_number->retrieve( $cn );
706         return undef unless ($cn);
707
708         my $call_number = $cn->to_fieldmapper;
709         $call_number->copies([]);
710
711         $call_number->record( $cn->record->to_fieldmapper );
712         $call_number->record->fixed_fields( $cn->record->record_descriptor->next->to_fieldmapper );
713
714         for my $cp ( $cn->copies(circ_lib => $ou_list) ) {
715                 my $copy = $cp->to_fieldmapper;
716                 $copy->status( $cp->status->to_fieldmapper );
717                 $copy->location( $cp->status->to_fieldmapper );
718
719                 push @{ $call_number->copies }, $copy;
720         }
721
722         return $call_number;
723 }
724 __PACKAGE__->register_method(
725         api_name        => 'open-ils.storage.asset.call_number.ranged_tree',
726         method          => 'cn_ranged_tree',
727         argc            => 1,
728         api_level       => 1,
729 );
730
731
732 1;