]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Cat.pm
checking for deleted == false on copy searches
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Cat.pm
1 use strict; use warnings;
2 package OpenILS::Application::Cat;
3 use OpenILS::Application::AppUtils;
4 use OpenSRF::Application;
5 use OpenILS::Application::Cat::Utils;
6 use base qw/OpenSRF::Application/;
7 use Time::HiRes qw(time);
8 use OpenSRF::EX qw(:try);
9 use JSON;
10 use OpenILS::Utils::Fieldmapper;
11 use XML::LibXML;
12 use Data::Dumper;
13 use OpenILS::Utils::FlatXML;
14 use OpenILS::Perm;
15 use OpenSRF::Utils::SettingsClient;
16 use OpenSRF::Utils::Logger qw($logger);
17
18 my $apputils = "OpenILS::Application::AppUtils";
19
20 my $utils = "OpenILS::Application::Cat::Utils";
21
22 my $conf;
23
24 my %marctemplates;
25
26
27 __PACKAGE__->register_method(
28         method  => "retrieve_marc_template",
29         api_name        => "open-ils.cat.biblio.marc_template.retrieve",
30         notes           => <<"  NOTES");
31         Returns a MARC 'record tree' based on a set of pre-defined templates.
32         Templates include : book
33         NOTES
34
35 sub retrieve_marc_template {
36         my( $self, $client, $type ) = @_;
37
38         return $marctemplates{$type} if defined($marctemplates{$type});
39
40         my $xml = _load_marc_template($type);
41
42         my $nodes = OpenILS::Utils::FlatXML->new()->xml_to_nodeset( $xml ); 
43         $marctemplates{$type} = $utils->nodeset2tree( $nodes->nodeset );
44         return $marctemplates{$type};
45 }
46
47 sub _load_marc_template {
48         my $type = shift;
49
50         if(!$conf) { $conf = OpenSRF::Utils::SettingsClient->new; }
51
52         my $template = $conf->config_value(                                     
53                 "apps", "open-ils.cat","app_settings", "marctemplates", $type );
54         warn "Opening template file $template\n";
55
56         open( F, $template );
57         my @xml = <F>;
58         close(F);
59         return join('', @xml);
60
61 }
62
63
64
65 __PACKAGE__->register_method(
66         method  => "create_record_tree",
67         api_name        => "open-ils.cat.biblio.record_tree.create",
68         notes           => <<"  NOTES");
69         Inserts a new MARC 'record tree' into the system
70         NOTES
71
72 sub create_record_tree {
73         my( $self, $client, $login, $tree ) = @_;
74
75         my $user_obj = $apputils->check_user_session($login);
76
77         if($apputils->check_user_perms(
78                         $user_obj->id, $user_obj->home_ou, "CREATE_MARC")) {
79                 return OpenILS::Perm->new("CREATE_MARC"); 
80         }
81
82         warn "Creating a new record tree entry...";
83         my $meth = $self->method_lookup("open-ils.cat.biblio.record.tree.import");
84         my ($s) = $meth->run($login, $tree);
85         return $s;
86 }
87
88
89
90
91 __PACKAGE__->register_method(
92         method  => "biblio_record_tree_import",
93         api_name        => "open-ils.cat.biblio.record.tree.import",
94         notes           => <<"  NOTES");
95         Takes a record tree and imports the record into the database.  In this
96         case, the record tree is assumed to be a complete record (i.e. valid
97         MARC.  The title control number is taken from (whichever comes first)
98         tags 001, 020, 022, 010, 035 and whichever does not already exist
99         in the database.
100         user_session must have IMPORT_MARC permissions
101         NOTES
102
103
104 sub biblio_record_tree_import {
105         my( $self, $client, $user_session, $tree) = @_;
106         my $user_obj = $apputils->check_user_session($user_session);
107
108         if($apputils->check_user_perms(
109                         $user_obj->id, $user_obj->home_ou, "IMPORT_MARC")) {
110                 return OpenILS::Perm->new("IMPORT_MARC"); 
111         }
112
113         my $nodeset = $utils->tree2nodeset($tree);
114
115         # copy the doc so that we can mangle the namespace.  
116         my $marcxml = OpenILS::Utils::FlatXML->new()->nodeset_to_xml($nodeset);
117         my $copy_marcxml = XML::LibXML->new->parse_string($marcxml->toString);
118
119         $marcxml->documentElement->setNamespace( "http://www.loc.gov/MARC21/slim", "marc", 1 );
120         my $tcn;
121
122         #warn "Importing MARC Doc:\n".$marcxml->toString(1)."\n";
123         #warn "Namespace: " . $marcxml->documentElement->firstChild->namespaceURI . "\n";
124         #return 1;
125
126         warn "Starting db session in import\n";
127         my $session = $apputils->start_db_session();
128         my $source = 2; # system local source
129
130         my $xpath = '//controlfield[@tag="001"]';
131         $tcn = $marcxml->documentElement->findvalue($xpath);
132         if(_tcn_exists($session, $tcn)) {$tcn = undef;}
133         my $tcn_source = "External";
134
135
136         if(!$tcn) {
137                 $xpath = '//datafield[@tag="020"]';
138                 $tcn = $marcxml->documentElement->findvalue($xpath);
139                 $tcn_source = "ISBN";
140                 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
141         }
142
143         if(!$tcn) { 
144                 $xpath = '//datafield[@tag="022"]';
145                 $tcn = $marcxml->documentElement->findvalue($xpath);
146                 $tcn_source = "ISSN";
147                 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
148         }
149
150         if(!$tcn) {
151                 $xpath = '//datafield[@tag="010"]';
152                 $tcn = $marcxml->documentElement->findvalue($xpath);
153                 $tcn_source = "LCCN";
154                 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
155         }
156
157         if(!$tcn) {
158                 $xpath = '//datafield[@tag="035"]';
159                 $tcn = $marcxml->documentElement->findvalue($xpath);
160                 $tcn_source = "System";
161                 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
162         }
163
164         $tcn =~ s/^\s+//g;
165         $tcn =~ s/\s+$//g;
166
167         warn "Record import with tcn: $tcn and source $tcn_source\n";
168
169         my $record = Fieldmapper::biblio::record_entry->new;
170
171         $record->source($source);
172         $record->tcn_source($tcn_source);
173         $record->tcn_value($tcn);
174         $record->creator($user_obj->id);
175         $record->editor($user_obj->id);
176         $record->marc($copy_marcxml->toString);
177
178
179         my $req = $session->request(
180                 "open-ils.storage.direct.biblio.record_entry.create", $record );
181
182         my $id = $req->gather(1);
183
184         if(!$id) { throw OpenSRF::EX::ERROR ("Unable to create new record_entry from import"); }
185         warn "received id: $id from record_entry create\n";
186
187         $apputils->commit_db_session($session);
188
189         $session = OpenSRF::AppSession->create("open-ils.storage");
190
191         my $wreq = $session->request("open-ils.worm.wormize.biblio", $id)->gather(1);
192         warn "Done worming record $id\n";
193
194         if(!$wreq) { throw OpenSRF::EX::ERROR ("Unable to wormize imported record"); }
195
196         return $self->biblio_record_tree_retrieve($client, $id);
197
198 }
199
200 sub _tcn_exists {
201         my $session = shift;
202         my $tcn = shift;
203
204         if(!$tcn) {return 0;}
205
206         my $req = $session->request(      
207                 "open-ils.storage.direct.biblio.record_entry.search.tcn_value.atomic",
208                 $tcn );
209         my $recs = $req->gather(1);
210
211         if($recs and $recs->[0]) {
212                 $logger->debug("_tcn_exists is true for tcn : $tcn");
213                 return 1;
214         }
215
216         $logger->debug("_tcn_exists is false for tcn : $tcn");
217         return 0;
218 }
219
220
221
222 __PACKAGE__->register_method(
223         method  => "biblio_record_tree_retrieve",
224         api_name        => "open-ils.cat.biblio.record.tree.retrieve",
225 );
226
227 sub biblio_record_tree_retrieve {
228
229         my( $self, $client, $recordid ) = @_;
230
231         my $name = "open-ils.storage.direct.biblio.record_entry.retrieve";
232         my $session = OpenSRF::AppSession->create( "open-ils.storage" );
233         my $request = $session->request( $name, $recordid );
234         my $marcxml = $request->gather(1);
235
236         if(!$marcxml) {
237                 throw OpenSRF::EX::ERROR 
238                         ("No record in database with id $recordid");
239         }
240
241         $session->disconnect();
242         $session->kill_me();
243
244         warn "turning into nodeset\n";
245         my $nodes = OpenILS::Utils::FlatXML->new()->xml_to_nodeset( $marcxml->marc ); 
246         warn "turning nodeset into tree\n";
247         my $tree = $utils->nodeset2tree( $nodes->nodeset );
248
249         $tree->owner_doc( $marcxml->id() );
250
251         warn "returning tree\n";
252
253         return $tree;
254 }
255
256 __PACKAGE__->register_method(
257         method  => "biblio_record_tree_commit",
258         api_name        => "open-ils.cat.biblio.record.tree.commit",
259         argc            => 3, #(session_id, biblio_tree ) 
260         notes           => <<"  NOTES");
261         Walks the tree and commits any changed nodes 
262         adds any new nodes, and deletes any deleted nodes
263         The record to commit must already exist or this
264         method will fail
265         NOTES
266
267 sub biblio_record_tree_commit {
268
269         my( $self, $client, $user_session,  $tree ) = @_;
270
271         throw OpenSRF::EX::InvalidArg 
272                 ("Not enough args to to open-ils.cat.biblio.record.tree.commit")
273                 unless ( $user_session and $tree );
274
275         my $user_obj = $apputils->check_user_session($user_session); 
276
277         if($apputils->check_user_perms(
278                         $user_obj->id, $user_obj->home_ou, "UPDATE_MARC")) {
279                 return OpenILS::Perm->new("UPDATE_MARC"); 
280         }
281
282
283         # capture the doc id
284         my $docid = $tree->owner_doc();
285         my $session = OpenILS::Application::AppUtils->start_db_session();
286
287         warn "Retrieving biblio record from storage for update\n";
288
289         my $req1 = $session->request(
290                         "open-ils.storage.direct.biblio.record_entry.batch.retrieve", $docid );
291         my $biblio = $req1->gather(1);
292
293         warn "retrieved doc $docid\n";
294
295
296         # turn the tree into a nodeset
297         my $nodeset = $utils->tree2nodeset($tree);
298         $nodeset = $utils->clean_nodeset($nodeset);
299
300         if(!defined($docid)) { # be sure
301                 for my $node (@$nodeset) {
302                         $docid = $node->owner_doc();
303                         last if defined($docid);
304                 }
305         }
306
307         # turn the nodeset into a doc
308         my $marcxml = OpenILS::Utils::FlatXML->new()->nodeset_to_xml( $nodeset );
309
310         $biblio->marc( $marcxml->toString() );
311
312         warn "Starting db session\n";
313
314         my $x = _update_record_metadata( $session, { user => $user_obj, docid => $docid } );
315         OpenILS::Application::AppUtils->rollback_db_session($session) unless $x;
316
317         warn "Sending updated doc $docid to db\n";
318         my $req = $session->request( "open-ils.storage.direct.biblio.record_entry.update", $biblio );
319
320         $req->wait_complete;
321         my $status = $req->recv();
322         if( !$status || $status->isa("Error") || ! $status->content) {
323                 OpenILS::Application::AppUtils->rollback_db_session($session);
324                 if($status->isa("Error")) { throw $status ($status); }
325                 throw OpenSRF::EX::ERROR ("Error updating biblio record");
326         }
327         $req->finish();
328
329         # Send the doc to the wormer for wormizing
330         warn "Starting worm session\n";
331
332         my $success = 0;
333         my $wresp;
334
335         my $wreq = $session->request( "open-ils.worm.wormize.biblio", $docid );
336
337         try {
338                 $wreq->gather(1);
339
340         } catch Error with {
341                 my $e = shift;
342                 warn "wormizing failed, rolling back\n";
343                 OpenILS::Application::AppUtils->rollback_db_session($session);
344
345                 if($e) { throw $e ($e); }
346                 throw OpenSRF::EX::ERROR ("Wormizing Failed for $docid" );
347         };
348
349         warn "Committing db session...\n";
350         OpenILS::Application::AppUtils->commit_db_session( $session );
351
352         $nodeset = OpenILS::Utils::FlatXML->new()->xmldoc_to_nodeset($marcxml);
353         $tree = $utils->nodeset2tree($nodeset->nodeset);
354         $tree->owner_doc($docid);
355
356 #       $client->respond_complete($tree);
357
358         warn "Done wormizing\n";
359
360         #use Data::Dumper;
361         #warn "Returning tree:\n";
362         #warn Dumper $tree;
363
364         return $tree;
365
366 }
367
368
369
370 __PACKAGE__->register_method(
371         method  => "biblio_record_record_metadata",
372         api_name        => "open-ils.cat.biblio.record.metadata.retrieve",
373         argc            => 1, #(session_id, biblio_tree ) 
374         notes           => "Walks the tree and commits any changed nodes " .
375                                         "adds any new nodes, and deletes any deleted nodes",
376 );
377
378 sub biblio_record_record_metadata {
379         my( $self, $client, @ids ) = @_;
380
381         if(!@ids){return undef;}
382
383         my $session = OpenSRF::AppSession->create("open-ils.storage");
384         my $request = $session->request( 
385                         "open-ils.storage.direct.biblio.record_entry.batch.retrieve", @ids );
386
387         my $results = [];
388
389         while( my $response = $request->recv() ) {
390
391                 if(!$response) {
392                         throw OpenSRF::EX::ERROR ("No Response from Storage");
393                 }
394                 if($response->isa("Error")) {
395                         throw $response ($response->stringify);
396                 }
397
398                 my $record_entry = $response->content;
399
400                 my $creator = $record_entry->creator;
401                 my $editor      = $record_entry->editor;
402
403                 ($creator, $editor) = _get_userid_by_id($creator, $editor);
404
405                 $record_entry->creator($creator);
406                 $record_entry->editor($editor);
407
408                 push @$results, $record_entry;
409
410         }
411
412         $request->finish;
413         $session->disconnect();
414         $session->finish();
415
416         return $results;
417
418 }
419
420 __PACKAGE__->register_method(
421         method  => "biblio_record_marc_cn",
422         api_name        => "open-ils.cat.biblio.record.marc_cn.retrieve",
423         argc            => 1, #(bib id ) 
424 );
425
426 sub biblio_record_marc_cn {
427         my( $self, $client, $id ) = @_;
428
429         my $session = OpenSRF::AppSession->create("open-ils.storage");
430         my $marc = $session
431                 ->request("open-ils.storage.direct.biblio.record_entry.retrieve", $id )
432                 ->gather(1)
433                 ->marc;
434
435         my $doc = XML::LibXML->new->parse_string($marc);
436         $doc->documentElement->setNamespace( "http://www.loc.gov/MARC21/slim", "marc", 1 );
437         
438         my @res;
439         for my $tag ( qw/050 055 060 070 080 082 086 088 090 092 096 098 099/ ) {
440                 my @node = $doc->findnodes("//marc:datafield[\@tag='$tag']");
441                 for my $x (@node) {
442                         my $cn = $x->findvalue("marc:subfield[\@code='a' or \@code='b']");
443                         push @res, {$tag => $cn} if ($cn);
444                 }
445         }
446
447         return \@res
448 }
449
450 # gets the username
451 sub _get_userid_by_id {
452
453         my @ids = @_;
454         my @users;
455
456         my $session = OpenSRF::AppSession->create( "open-ils.storage" );
457         my $request = $session->request( 
458                 "open-ils.storage.direct.actor.user.batch.retrieve.atomic", @ids );
459
460         $request->wait_complete;
461         my $response = $request->recv();
462         if(!$request->complete) { return undef; }
463
464         if($response->isa("Error")){
465                 throw $response ($response);
466         }
467
468         for my $u (@{$response->content}) {
469                 next unless ref($u);
470                 push @users, $u->usrname;
471         }
472
473         $request->finish;
474         $session->disconnect;
475         $session->kill_me();
476
477         return @users;
478 }
479
480 sub _get_id_by_userid {
481
482         my @users = @_;
483         my @ids;
484
485         my $session = OpenSRF::AppSession->create( "open-ils.storage" );
486         my $request = $session->request( 
487                 "open-ils.storage.direct.actor.user.search.usrname.atomic", @users );
488
489         $request->wait_complete;
490         my $response = $request->recv();
491         if(!$request->complete) { 
492                 throw OpenSRF::EX::ERROR ("no response from storage on user retrieve");
493         }
494
495         if(UNIVERSAL::isa( $response, "Error")){
496                 throw $response ($response);
497         }
498
499         for my $u (@{$response->content}) {
500                 next unless ref($u);
501                 push @ids, $u->id();
502         }
503
504         $request->finish;
505         $session->disconnect;
506         $session->kill_me();
507
508         return @ids;
509 }
510
511
512 # commits metadata objects to the db
513 sub _update_record_metadata {
514
515         my ($session, @docs ) = @_;
516
517         for my $doc (@docs) {
518
519                 my $user_obj = $doc->{user};
520                 my $docid = $doc->{docid};
521
522                 warn "Updating metata for doc $docid\n";
523
524                 my $request = $session->request( 
525                         "open-ils.storage.direct.biblio.record_entry.retrieve", $docid );
526                 my $record = $request->gather(1);
527
528                 warn "retrieved record\n";
529                 my ($id) = _get_id_by_userid($user_obj->usrname);
530
531                 warn "got $id from _get_id_by_userid\n";
532                 $record->editor($id);
533                 
534                 warn "Grabbed the record, updating and moving on\n";
535
536                 $request = $session->request( 
537                         "open-ils.storage.direct.biblio.record_entry.update", $record );
538                 $request->gather(1);
539         }
540
541         warn "committing metarecord update\n";
542
543         return 1;
544 }
545
546
547
548 __PACKAGE__->register_method(
549         method  => "orgs_for_title",
550         api_name        => "open-ils.cat.actor.org_unit.retrieve_by_title"
551 );
552
553 sub orgs_for_title {
554         my( $self, $client, $record_id ) = @_;
555
556         my $vols = $apputils->simple_scalar_request(
557                 "open-ils.storage",
558                 "open-ils.storage.direct.asset.call_number.search.record.atomic",
559                 $record_id );
560
561         my $orgs = { map {$_->owning_lib => 1 } @$vols };
562         return [ keys %$orgs ];
563 }
564
565
566 __PACKAGE__->register_method(
567         method  => "retrieve_copies_",
568         api_name        => "open-ils.cat.asset.copy_tree.retrieve_");
569
570 sub retrieve_copies_ {
571         my( $self, $conn, $authtoken, $docid, $orgs ) = @_;
572 }
573
574
575 __PACKAGE__->register_method(
576         method  => "retrieve_copies",
577         api_name        => "open-ils.cat.asset.copy_tree.retrieve");
578
579 __PACKAGE__->register_method(
580         method  => "retrieve_copies",
581         api_name        => "open-ils.cat.asset.copy_tree.global.retrieve");
582
583 # user_session may be null/undef
584 sub retrieve_copies {
585
586         my( $self, $client, $user_session, $docid, @org_ids ) = @_;
587
588         if(ref($org_ids[0])) { @org_ids = @{$org_ids[0]}; }
589
590         $docid = "$docid";
591
592         warn " $$ retrieving copy tree for orgs @org_ids and doc $docid at " . time() . "\n";
593
594         # grabbing copy trees should be available for everyone..
595         if(!@org_ids and $user_session) {
596                 my $user_obj = 
597                         OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
598                         @org_ids = ($user_obj->home_ou);
599         }
600
601         if( $self->api_name =~ /global/ ) {
602                 warn "performing global copy_tree search for $docid\n";
603                 return _build_volume_list( { record => $docid } );
604
605         } else {
606
607                 my @all_vols;
608                 for my $orgid (@org_ids) {
609                         my $vols = _build_volume_list( 
610                                         { record => $docid, owning_lib => $orgid } );
611                         warn "Volumes built for org $orgid\n";
612                         push( @all_vols, @$vols );
613                 }
614                 
615                 warn " $$ Finished copy_tree at " . time() . "\n";
616                 return \@all_vols;
617         }
618
619         return undef;
620 }
621
622
623 sub _build_volume_list {
624         my $search_hash = shift;
625
626         my      $session = OpenSRF::AppSession->create( "open-ils.storage" );
627         
628
629         my $request = $session->request( 
630                         "open-ils.storage.direct.asset.call_number.search.atomic", $search_hash );
631
632         my $vols = $request->gather(1);
633         my @volumes;
634
635         for my $volume (@$vols) {
636
637                 warn "Grabbing copies for volume: " . $volume->id . "\n";
638                 my $creq = $session->request(
639                         "open-ils.storage.direct.asset.copy.search_where.atomic", 
640                         { call_number => $volume->id , deleted => 'f' });
641                         #"open-ils.storage.direct.asset.copy.search.call_number.atomic", $volume->id );
642
643                 my $copies = $creq->gather(1);
644
645                 $copies = [ sort { $a->barcode cmp $b->barcode } @$copies  ];
646
647                 $volume->copies($copies);
648
649                 push( @volumes, $volume );
650         }
651
652
653         $session->disconnect();
654         return \@volumes;
655
656 }
657
658
659 # -----------------------------------------------------------------
660 # Fleshed volume tree batch add/update.  This does everything a 
661 # volume tree could want, add, update, delete
662 # -----------------------------------------------------------------
663 __PACKAGE__->register_method(
664         method  => "volume_tree_fleshed_update",
665         api_name        => "open-ils.cat.asset.volume_tree.fleshed.batch.update",
666 );
667 sub volume_tree_fleshed_update {
668
669         my( $self, $client, $user_session, $volumes ) = @_;
670         return undef unless $volumes;
671
672         my $user_obj = $apputils->check_user_session($user_session);
673
674
675         my $session = $apputils->start_db_session();
676         warn "Looping on volumes in fleshed volume tree update\n";
677
678         # cycle through the volumes provided and update/create/delete where necessary
679         for my $volume (@$volumes) {
680
681                 warn "updating volume " . $volume->id . "\n";
682
683                 my $update_copy_list = $volume->copies;
684
685
686                 if( $volume->isdeleted) {
687                         my $status = _delete_volume($session, $volume, $user_obj);
688                         if(!$status) {
689                                 throw OpenSRF::EX::ERROR
690                                         ("Volume delete failed for volume " . $volume->id);
691                         }
692                         if(UNIVERSAL::isa($status, "Fieldmapper::perm_ex")) { return $status; }
693
694                 } elsif( $volume->isnew ) {
695
696                         $volume->clear_id;
697                         $volume->editor($user_obj->id);
698                         $volume->creator($user_obj->id);
699                         $volume = _add_volume($session, $volume, $user_obj);
700                         use Data::Dumper;
701                         warn Dumper $volume;
702                         if($volume and UNIVERSAL::isa($volume, "Fieldmapper::perm_ex")) { return $volume; }
703
704                 } elsif( $volume->ischanged ) {
705
706                         $volume->editor($user_obj->id);
707                         my $stat = _update_volume($session, $volume, $user_obj);
708                         if($stat and UNIVERSAL::isa($stat, "Fieldmapper::perm_ex")) { return $stat; }
709                 }
710
711
712                 if( ! $volume->isdeleted ) {
713                         for my $copy (@{$update_copy_list}) {
714         
715                                 $copy->editor($user_obj->id);
716                                 warn "updating copy for volume " . $volume->id . "\n";
717         
718                                 if( $copy->isnew ) {
719         
720                                         $copy->clear_id;
721                                         $copy->call_number($volume->id);
722                                         $copy->creator($user_obj->id);
723                                         $copy = _fleshed_copy_update($session,$copy,$user_obj);
724         
725                                 } elsif( $copy->ischanged ) {
726                                         $copy->call_number($volume->id);
727                                         $copy = _fleshed_copy_update($session, $copy, $user_obj);
728         
729                                 } elsif( $copy->isdeleted ) {
730                                         warn "Deleting copy " . $copy->id . " for volume " . $volume->id . "\n";
731                                         my $status = _fleshed_copy_update($session, $copy, $user_obj);
732                                         warn "Copy delete returned a status of $status\n";
733                                 }
734                         }
735                 }
736         }
737
738         $apputils->commit_db_session($session);
739         return scalar(@$volumes);
740 }
741
742
743 sub _delete_volume {
744         my( $session, $volume, $user_obj ) = @_;
745
746         if($apputils->check_user_perms(
747                         $user_obj->id, $user_obj->home_ou, "DELETE_VOLUME")) {
748                 return OpenILS::Perm->new("DELETE_VOLUME"); }
749
750         #$volume = _find_volume($session, $volume);
751         warn "Deleting volume " . $volume->id . "\n";
752
753         my $copies = $session->request(
754                 "open-ils.storage.direct.asset.copy.search_where.atomic", 
755                 { call_number => $volume->id, deleted => 'f' } )->gather(1);
756                 #"open-ils.storage.direct.asset.copy.search.call_number.atomic",
757
758         if(@$copies) {
759                 throw OpenSRF::EX::ERROR 
760                         ("Cannot remove volume with copies attached");
761         }
762
763         my $req = $session->request(
764                 "open-ils.storage.direct.asset.call_number.delete",
765                 $volume );
766         return $req->gather(1);
767 }
768
769
770 sub _update_volume {
771         my($session, $volume, $user_obj) = @_;
772         if($apputils->check_user_perms(
773                         $user_obj->id, $user_obj->home_ou, "UPDATE_VOLUME")) {
774                 return OpenILS::Perm->new("UPDATE_VOLUME"); }
775
776         my $req = $session->request(
777                 "open-ils.storage.direct.asset.call_number.update",
778                 $volume );
779         my $status = $req->gather(1);
780 }
781
782 sub _add_volume {
783
784         my($session, $volume, $user_obj) = @_;
785
786         if($apputils->check_user_perms(
787                         $user_obj->id, $user_obj->home_ou, "CREATE_VOLUME")) {
788                 warn "User does not have priveleges to create new volumes\n";
789                 return OpenILS::Perm->new("CREATE_VOLUME"); 
790         }
791
792         my $request = $session->request( 
793                 "open-ils.storage.direct.asset.call_number.create", $volume );
794
795         my $id = $request->gather(1);
796
797         if( $id == 0 ) {
798                 OpenILS::Application::AppUtils->rollback_db_session($session);
799                 throw OpenSRF::EX::ERROR (" * -> Error creating new volume");
800         }
801
802         $volume->id($id);
803         warn "received new volume id: $id\n";
804         return $volume;
805
806 }
807
808
809
810
811 __PACKAGE__->register_method(
812         method  => "fleshed_copy_update",
813         api_name        => "open-ils.cat.asset.copy.fleshed.batch.update",
814 );
815
816 sub fleshed_copy_update {
817         my($self, $client, $user_session, $copies) = @_;
818
819         my $user_obj = $apputils->check_user_session($user_session); 
820         my $session = $apputils->start_db_session();
821
822         for my $copy (@$copies) {
823                 _fleshed_copy_update($session, $copy, $user_obj);
824         }
825
826         $apputils->commit_db_session($session);
827         return 1;
828 }
829
830
831
832 sub _delete_copy {
833         my($session, $copy, $user_obj) = @_;
834
835         if($apputils->check_user_perms(
836                         $user_obj->id, $user_obj->home_ou, "DELETE_COPY")) {
837                 return OpenILS::Perm->new("DELETE_COPY"); }
838
839         warn "Deleting copy " . $copy->id . "\n";
840         my $request = $session->request(
841                 "open-ils.storage.direct.asset.copy.delete",
842                 $copy );
843         return $request->gather(1);
844 }
845
846 sub _create_copy {
847         my($session, $copy, $user_obj) = @_;
848
849         if($apputils->check_user_perms(
850                         $user_obj->id, $user_obj->home_ou, "CREATE_COPY")) {
851                 return OpenILS::Perm->new("CREATE_COPY"); }
852
853         my $request = $session->request(
854                 "open-ils.storage.direct.asset.copy.create",
855                 $copy );
856         my $id = $request->gather(1);
857
858         if($id < 1) {
859                 throw OpenSRF::EX::ERROR
860                         ("Unable to create new copy " . Dumper($copy));
861         }
862         $copy->id($id);
863         warn "Created copy " . $copy->id . "\n";
864
865         return $copy;
866
867 }
868
869 sub _update_copy {
870         my($session, $copy, $user_obj) = @_;
871
872         my $evt = $apputils->check_perms($user_obj->id, $copy->circ_lib, 'UPDATE_COPY');
873         return $evt if $evt; #XXX NOT YET HANDLED BY CALLER
874
875         my $status = $apputils->simplereq(      
876                 'open-ils.storage',
877                 "open-ils.storage.direct.asset.copy.update", $copy );
878         $logger->debug("Successfully updated copy " . $copy->id );
879         return $status;
880 }
881
882
883 # -----------------------------------------------------------------
884 # Creates/Updates/Deletes a fleshed asset.copy.  
885 # adds/deletes copy stat_cat maps where necessary
886 # -----------------------------------------------------------------
887 sub _fleshed_copy_update {
888         my($session, $copy, $editor) = @_;
889
890         my $stat_cat_entries = $copy->stat_cat_entries;
891         $copy->editor($editor->id);
892         
893         # in case we're fleshed
894         if(ref($copy->status))          {$copy->status( $copy->status->id ); }
895         if(ref($copy->location))        {$copy->location( $copy->location->id ); }
896         if(ref($copy->circ_lib))        {$copy->circ_lib( $copy->circ_lib->id ); }
897
898         warn "Updating copy " . Dumper($copy) . "\n";
899
900         if( $copy->isdeleted ) { 
901                 return _delete_copy($session, $copy, $editor);
902         } elsif( $copy->isnew ) {
903                 $copy = _create_copy($session, $copy, $editor);
904         } elsif( $copy->ischanged ) {
905                 _update_copy($session, $copy, $editor);
906         }
907
908         
909         return 1 unless ( $stat_cat_entries and @$stat_cat_entries );
910
911         my $stat_maps = $session->request(
912                 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.search.owning_copy.atomic",
913                 $copy->id )->gather(1);
914
915         if(!$copy->isnew) { _delete_stale_maps($session, $stat_maps, $copy); }
916         
917         # go through the stat cat update/create process
918         for my $stat_entry (@{$stat_cat_entries}){ 
919                 _copy_update_stat_cats( $session, $copy, $stat_maps, $stat_entry, $editor );
920         }
921         
922         return 1;
923 }
924
925
926 # -----------------------------------------------------------------
927 # Deletes stat maps attached to this copy in the database that
928 # are no longer attached to the current copy
929 # -----------------------------------------------------------------
930 sub _delete_stale_maps {
931         my( $session, $stat_maps, $copy) = @_;
932
933         warn "Deleting stale stat maps for copy " . $copy->id . "\n";
934         for my $map (@$stat_maps) {
935         # if there is no stat cat entry on the copy who's id matches the
936         # current map's id, remove the map from the database
937         if(! grep { $_->id == $map->stat_cat_entry } @{$copy->stat_cat_entries} ) {
938                 my $req = $session->request(
939                         "open-ils.storage.direct.asset.stat_cat_entry_copy_map.delete", $map );
940                 $req->gather(1);
941                 }
942         }
943
944         return $stat_maps;
945 }
946
947
948 # -----------------------------------------------------------------
949 # Searches the stat maps to see if '$entry' already exists on
950 # the given copy.  If it does not, a new stat map is created
951 # for the given entry and copy
952 # -----------------------------------------------------------------
953 sub _copy_update_stat_cats {
954         my ( $session, $copy, $stat_maps, $entry, $editor ) = @_;
955
956         warn "Updating stat maps for copy " . $copy->id . "\n";
957
958         # see if this map already exists
959         for my $map (@$stat_maps) {
960                 if( $map->stat_cat_entry == $entry->id ) {return;}
961         }
962
963         warn "Creating new stat map for stat  " . 
964                 $entry->stat_cat . " and copy " . $copy->id . "\n";
965
966         # if not, create it
967         my $new_map = Fieldmapper::asset::stat_cat_entry_copy_map->new();
968
969         $new_map->stat_cat( $entry->stat_cat );
970         $new_map->stat_cat_entry( $entry->id );
971         $new_map->owning_copy( $copy->id );
972
973         warn "New map is " . Dumper($new_map) . "\n";
974
975         my $request = $session->request(
976                 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.create",
977                 $new_map );
978         my $status = $request->gather(1);
979         warn "created new map with id $status\n";
980
981 }
982
983
984
985
986 1;