]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Cat.pm
911122ac35a7efa92cb260eb5c6bab32ead4bca8
[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         warn "importing new record " . Dumper($tree) . "\n";
114
115         my $nodeset = $utils->tree2nodeset($tree);
116         warn "turned into nodeset " . Dumper($nodeset) . "\n";
117
118         # copy the doc so that we can mangle the namespace.  
119         my $marcxml = OpenILS::Utils::FlatXML->new()->nodeset_to_xml($nodeset);
120         my $copy_marcxml = XML::LibXML->new->parse_string($marcxml->toString);
121
122         $marcxml->documentElement->setNamespace( "http://www.loc.gov/MARC21/slim", "marc", 1 );
123         my $tcn;
124
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                 return 1;
213         }
214         return 0;
215 }
216
217
218
219 __PACKAGE__->register_method(
220         method  => "biblio_record_tree_retrieve",
221         api_name        => "open-ils.cat.biblio.record.tree.retrieve",
222 );
223
224 sub biblio_record_tree_retrieve {
225
226         my( $self, $client, $recordid ) = @_;
227
228         my $name = "open-ils.storage.direct.biblio.record_entry.retrieve";
229         my $session = OpenSRF::AppSession->create( "open-ils.storage" );
230         my $request = $session->request( $name, $recordid );
231         my $marcxml = $request->gather(1);
232
233         if(!$marcxml) {
234                 throw OpenSRF::EX::ERROR 
235                         ("No record in database with id $recordid");
236         }
237
238         $session->disconnect();
239         $session->kill_me();
240
241         warn "turning into nodeset\n";
242         my $nodes = OpenILS::Utils::FlatXML->new()->xml_to_nodeset( $marcxml->marc ); 
243         warn "turning nodeset into tree\n";
244         my $tree = $utils->nodeset2tree( $nodes->nodeset );
245
246         $tree->owner_doc( $marcxml->id() );
247
248         warn "returning tree\n";
249
250         return $tree;
251 }
252
253 __PACKAGE__->register_method(
254         method  => "biblio_record_tree_commit",
255         api_name        => "open-ils.cat.biblio.record.tree.commit",
256         argc            => 3, #(session_id, biblio_tree ) 
257         notes           => <<"  NOTES");
258         Walks the tree and commits any changed nodes 
259         adds any new nodes, and deletes any deleted nodes
260         The record to commit must already exist or this
261         method will fail
262         NOTES
263
264 sub biblio_record_tree_commit {
265
266         my( $self, $client, $user_session,  $tree ) = @_;
267
268         throw OpenSRF::EX::InvalidArg 
269                 ("Not enough args to to open-ils.cat.biblio.record.tree.commit")
270                 unless ( $user_session and $tree );
271
272         my $user_obj = $apputils->check_user_session($user_session); 
273
274         if($apputils->check_user_perms(
275                         $user_obj->id, $user_obj->home_ou, "UPDATE_MARC")) {
276                 return OpenILS::Perm->new("UPDATE_MARC"); 
277         }
278
279
280         # capture the doc id
281         my $docid = $tree->owner_doc();
282         my $session = OpenILS::Application::AppUtils->start_db_session();
283
284         warn "Retrieving biblio record from storage for update\n";
285
286         my $req1 = $session->request(
287                         "open-ils.storage.direct.biblio.record_entry.batch.retrieve", $docid );
288         my $biblio = $req1->gather(1);
289
290         warn "retrieved doc $docid\n";
291
292
293         # turn the tree into a nodeset
294         my $nodeset = $utils->tree2nodeset($tree);
295         $nodeset = $utils->clean_nodeset($nodeset);
296
297         if(!defined($docid)) { # be sure
298                 for my $node (@$nodeset) {
299                         $docid = $node->owner_doc();
300                         last if defined($docid);
301                 }
302         }
303
304         # turn the nodeset into a doc
305         my $marcxml = OpenILS::Utils::FlatXML->new()->nodeset_to_xml( $nodeset );
306
307         $biblio->marc( $marcxml->toString() );
308
309         warn "Starting db session\n";
310
311         my $x = _update_record_metadata( $session, { user => $user_obj, docid => $docid } );
312         OpenILS::Application::AppUtils->rollback_db_session($session) unless $x;
313
314         warn "Sending updated doc $docid to db\n";
315         my $req = $session->request( "open-ils.storage.direct.biblio.record_entry.update", $biblio );
316
317         $req->wait_complete;
318         my $status = $req->recv();
319         if( !$status || $status->isa("Error") || ! $status->content) {
320                 OpenILS::Application::AppUtils->rollback_db_session($session);
321                 if($status->isa("Error")) { throw $status ($status); }
322                 throw OpenSRF::EX::ERROR ("Error updating biblio record");
323         }
324         $req->finish();
325
326         # Send the doc to the wormer for wormizing
327         warn "Starting worm session\n";
328
329         my $success = 0;
330         my $wresp;
331
332         my $wreq = $session->request( "open-ils.worm.wormize.biblio", $docid );
333
334         try {
335                 $wreq->gather(1);
336
337         } catch Error with {
338                 my $e = shift;
339                 warn "wormizing failed, rolling back\n";
340                 OpenILS::Application::AppUtils->rollback_db_session($session);
341
342                 if($e) { throw $e ($e); }
343                 throw OpenSRF::EX::ERROR ("Wormizing Failed for $docid" );
344         };
345
346         warn "Committing db session...\n";
347         OpenILS::Application::AppUtils->commit_db_session( $session );
348
349         $nodeset = OpenILS::Utils::FlatXML->new()->xmldoc_to_nodeset($marcxml);
350         $tree = $utils->nodeset2tree($nodeset->nodeset);
351         $tree->owner_doc($docid);
352
353 #       $client->respond_complete($tree);
354
355         warn "Done wormizing\n";
356
357         #use Data::Dumper;
358         #warn "Returning tree:\n";
359         #warn Dumper $tree;
360
361         return $tree;
362
363 }
364
365
366
367 __PACKAGE__->register_method(
368         method  => "biblio_record_record_metadata",
369         api_name        => "open-ils.cat.biblio.record.metadata.retrieve",
370         argc            => 1, #(session_id, biblio_tree ) 
371         notes           => "Walks the tree and commits any changed nodes " .
372                                         "adds any new nodes, and deletes any deleted nodes",
373 );
374
375 sub biblio_record_record_metadata {
376         my( $self, $client, @ids ) = @_;
377
378         if(!@ids){return undef;}
379
380         my $session = OpenSRF::AppSession->create("open-ils.storage");
381         my $request = $session->request( 
382                         "open-ils.storage.direct.biblio.record_entry.batch.retrieve", @ids );
383
384         my $results = [];
385
386         while( my $response = $request->recv() ) {
387
388                 if(!$response) {
389                         throw OpenSRF::EX::ERROR ("No Response from Storage");
390                 }
391                 if($response->isa("Error")) {
392                         throw $response ($response->stringify);
393                 }
394
395                 my $record_entry = $response->content;
396
397                 my $creator = $record_entry->creator;
398                 my $editor      = $record_entry->editor;
399
400                 ($creator, $editor) = _get_userid_by_id($creator, $editor);
401
402                 $record_entry->creator($creator);
403                 $record_entry->editor($editor);
404
405                 push @$results, $record_entry;
406
407         }
408
409         $request->finish;
410         $session->disconnect();
411         $session->finish();
412
413         return $results;
414
415 }
416
417 # gets the username
418 sub _get_userid_by_id {
419
420         my @ids = @_;
421         my @users;
422
423         my $session = OpenSRF::AppSession->create( "open-ils.storage" );
424         my $request = $session->request( 
425                 "open-ils.storage.direct.actor.user.batch.retrieve.atomic", @ids );
426
427         $request->wait_complete;
428         my $response = $request->recv();
429         if(!$request->complete) { return undef; }
430
431         if($response->isa("Error")){
432                 throw $response ($response);
433         }
434
435         for my $u (@{$response->content}) {
436                 next unless ref($u);
437                 push @users, $u->usrname;
438         }
439
440         $request->finish;
441         $session->disconnect;
442         $session->kill_me();
443
444         return @users;
445 }
446
447 sub _get_id_by_userid {
448
449         my @users = @_;
450         my @ids;
451
452         my $session = OpenSRF::AppSession->create( "open-ils.storage" );
453         my $request = $session->request( 
454                 "open-ils.storage.direct.actor.user.search.usrname.atomic", @users );
455
456         $request->wait_complete;
457         my $response = $request->recv();
458         if(!$request->complete) { 
459                 throw OpenSRF::EX::ERROR ("no response from storage on user retrieve");
460         }
461
462         if(UNIVERSAL::isa( $response, "Error")){
463                 throw $response ($response);
464         }
465
466         for my $u (@{$response->content}) {
467                 next unless ref($u);
468                 push @ids, $u->id();
469         }
470
471         $request->finish;
472         $session->disconnect;
473         $session->kill_me();
474
475         return @ids;
476 }
477
478
479 # commits metadata objects to the db
480 sub _update_record_metadata {
481
482         my ($session, @docs ) = @_;
483
484         for my $doc (@docs) {
485
486                 my $user_obj = $doc->{user};
487                 my $docid = $doc->{docid};
488
489                 warn "Updating metata for doc $docid\n";
490
491                 my $request = $session->request( 
492                         "open-ils.storage.direct.biblio.record_entry.retrieve", $docid );
493                 my $record = $request->gather(1);
494
495                 warn "retrieved record\n";
496                 my ($id) = _get_id_by_userid($user_obj->usrname);
497
498                 warn "got $id from _get_id_by_userid\n";
499                 $record->editor($id);
500                 
501                 warn "Grabbed the record, updating and moving on\n";
502
503                 $request = $session->request( 
504                         "open-ils.storage.direct.biblio.record_entry.update", $record );
505                 $request->gather(1);
506         }
507
508         warn "committing metarecord update\n";
509
510         return 1;
511 }
512
513
514
515 __PACKAGE__->register_method(
516         method  => "orgs_for_title",
517         api_name        => "open-ils.cat.actor.org_unit.retrieve_by_title"
518 );
519
520 sub orgs_for_title {
521         my( $self, $client, $record_id ) = @_;
522
523         my $vols = $apputils->simple_scalar_request(
524                 "open-ils.storage",
525                 "open-ils.storage.direct.asset.call_number.search.record.atomic",
526                 $record_id );
527
528         my $orgs = { map {$_->owning_lib => 1 } @$vols };
529         return [ keys %$orgs ];
530 }
531
532
533
534 __PACKAGE__->register_method(
535         method  => "retrieve_copies",
536         api_name        => "open-ils.cat.asset.copy_tree.retrieve");
537
538 __PACKAGE__->register_method(
539         method  => "retrieve_copies",
540         api_name        => "open-ils.cat.asset.copy_tree.global.retrieve");
541
542 # user_session may be null/undef
543 sub retrieve_copies {
544
545         my( $self, $client, $user_session, $docid, @org_ids ) = @_;
546
547         if(ref($org_ids[0])) { @org_ids = @{$org_ids[0]}; }
548
549         $docid = "$docid";
550
551         warn " $$ retrieving copy tree for orgs @org_ids and doc $docid at " . time() . "\n";
552
553         # grabbing copy trees should be available for everyone..
554         if(!@org_ids and $user_session) {
555                 my $user_obj = 
556                         OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
557                         @org_ids = ($user_obj->home_ou);
558         }
559
560         if( $self->api_name =~ /global/ ) {
561                 warn "performing global copy_tree search for $docid\n";
562                 return _build_volume_list( { record => $docid } );
563
564         } else {
565
566                 my @all_vols;
567                 for my $orgid (@org_ids) {
568                         my $vols = _build_volume_list( 
569                                         { record => $docid, owning_lib => $orgid } );
570                         warn "Volumes built for org $orgid\n";
571                         push( @all_vols, @$vols );
572                 }
573                 
574                 warn " $$ Finished copy_tree at " . time() . "\n";
575                 return \@all_vols;
576         }
577
578         return undef;
579 }
580
581
582 sub _build_volume_list {
583         my $search_hash = shift;
584
585         my      $session = OpenSRF::AppSession->create( "open-ils.storage" );
586         
587
588         my $request = $session->request( 
589                         "open-ils.storage.direct.asset.call_number.search.atomic", $search_hash );
590
591         my $vols = $request->gather(1);
592         my @volumes;
593
594         for my $volume (@$vols) {
595
596                 warn "Grabbing copies for volume: " . $volume->id . "\n";
597                 my $creq = $session->request(
598                         "open-ils.storage.direct.asset.copy.search.call_number.atomic", $volume->id );
599
600                 my $copies = $creq->gather(1);
601
602                 $copies = [ sort { $a->barcode cmp $b->barcode } @$copies  ];
603
604                 $volume->copies($copies);
605
606                 push( @volumes, $volume );
607         }
608
609
610         $session->disconnect();
611         return \@volumes;
612
613 }
614
615
616 # -----------------------------------------------------------------
617 # Fleshed volume tree batch add/update.  This does everything a 
618 # volume tree could want, add, update, delete
619 # -----------------------------------------------------------------
620 __PACKAGE__->register_method(
621         method  => "volume_tree_fleshed_update",
622         api_name        => "open-ils.cat.asset.volume_tree.fleshed.batch.update",
623 );
624 sub volume_tree_fleshed_update {
625
626         my( $self, $client, $user_session, $volumes ) = @_;
627         return undef unless $volumes;
628
629         my $user_obj = $apputils->check_user_session($user_session);
630
631
632         my $session = $apputils->start_db_session();
633         warn "Looping on volumes in fleshed volume tree update\n";
634
635         # cycle through the volumes provided and update/create/delete where necessary
636         for my $volume (@$volumes) {
637
638                 warn "updating volume " . $volume->id . "\n";
639
640                 my $update_copy_list = $volume->copies;
641
642
643                 if( $volume->isdeleted) {
644                         my $status = _delete_volume($session, $volume, $user_obj);
645                         if(!$status) {
646                                 throw OpenSRF::EX::ERROR
647                                         ("Volume delete failed for volume " . $volume->id);
648                         }
649                         if(UNIVERSAL::isa($status, "Fieldmapper::perm_ex")) { return $status; }
650
651                 } elsif( $volume->isnew ) {
652
653                         $volume->clear_id;
654                         $volume->editor($user_obj->id);
655                         $volume->creator($user_obj->id);
656                         $volume = _add_volume($session, $volume, $user_obj);
657                         use Data::Dumper;
658                         warn Dumper $volume;
659                         if($volume and UNIVERSAL::isa($volume, "Fieldmapper::perm_ex")) { return $volume; }
660
661                 } elsif( $volume->ischanged ) {
662
663                         $volume->editor($user_obj->id);
664                         my $stat = _update_volume($session, $volume, $user_obj);
665                         if($stat and UNIVERSAL::isa($stat, "Fieldmapper::perm_ex")) { return $stat; }
666                 }
667
668
669                 if( ! $volume->isdeleted ) {
670                         for my $copy (@{$update_copy_list}) {
671         
672                                 $copy->editor($user_obj->id);
673                                 warn "updating copy for volume " . $volume->id . "\n";
674         
675                                 if( $copy->isnew ) {
676         
677                                         $copy->clear_id;
678                                         $copy->call_number($volume->id);
679                                         $copy->creator($user_obj->id);
680                                         $copy = _fleshed_copy_update($session,$copy,$user_obj);
681         
682                                 } elsif( $copy->ischanged ) {
683                                         $copy->call_number($volume->id);
684                                         $copy = _fleshed_copy_update($session, $copy, $user_obj);
685         
686                                 } elsif( $copy->isdeleted ) {
687                                         warn "Deleting copy " . $copy->id . " for volume " . $volume->id . "\n";
688                                         my $status = _fleshed_copy_update($session, $copy, $user_obj);
689                                         warn "Copy delete returned a status of $status\n";
690                                 }
691                         }
692                 }
693         }
694
695         $apputils->commit_db_session($session);
696         return scalar(@$volumes);
697 }
698
699
700 sub _delete_volume {
701         my( $session, $volume, $user_obj ) = @_;
702
703         if($apputils->check_user_perms(
704                         $user_obj->id, $user_obj->home_ou, "DELETE_VOLUME")) {
705                 return OpenILS::Perm->new("DELETE_VOLUME"); }
706
707         #$volume = _find_volume($session, $volume);
708         warn "Deleting volume " . $volume->id . "\n";
709
710         my $copies = $session->request(
711                 "open-ils.storage.direct.asset.copy.search.call_number.atomic",
712                 $volume->id )->gather(1);
713         if(@$copies) {
714                 throw OpenSRF::EX::ERROR 
715                         ("Cannot remove volume with copies attached");
716         }
717
718         my $req = $session->request(
719                 "open-ils.storage.direct.asset.call_number.delete",
720                 $volume );
721         return $req->gather(1);
722 }
723
724
725 sub _update_volume {
726         my($session, $volume, $user_obj) = @_;
727         if($apputils->check_user_perms(
728                         $user_obj->id, $user_obj->home_ou, "UPDATE_VOLUME")) {
729                 return OpenILS::Perm->new("UPDATE_VOLUME"); }
730
731         my $req = $session->request(
732                 "open-ils.storage.direct.asset.call_number.update",
733                 $volume );
734         my $status = $req->gather(1);
735 }
736
737 sub _add_volume {
738
739         my($session, $volume, $user_obj) = @_;
740
741         if($apputils->check_user_perms(
742                         $user_obj->id, $user_obj->home_ou, "CREATE_VOLUME")) {
743                 warn "User does not have priveleges to create new volumes\n";
744                 return OpenILS::Perm->new("CREATE_VOLUME"); 
745         }
746
747         my $request = $session->request( 
748                 "open-ils.storage.direct.asset.call_number.create", $volume );
749
750         my $id = $request->gather(1);
751
752         if( $id == 0 ) {
753                 OpenILS::Application::AppUtils->rollback_db_session($session);
754                 throw OpenSRF::EX::ERROR (" * -> Error creating new volume");
755         }
756
757         $volume->id($id);
758         warn "received new volume id: $id\n";
759         return $volume;
760
761 }
762
763
764
765
766 __PACKAGE__->register_method(
767         method  => "fleshed_copy_update",
768         api_name        => "open-ils.cat.asset.copy.fleshed.batch.update",
769 );
770
771 sub fleshed_copy_update {
772         my($self, $client, $user_session, $copies) = @_;
773
774         my $user_obj = $apputils->check_user_session($user_session); 
775         my $session = $apputils->start_db_session();
776
777         for my $copy (@$copies) {
778                 _fleshed_copy_update($session, $copy, $user_obj);
779         }
780
781         $apputils->commit_db_session($session);
782         return 1;
783 }
784
785
786
787 sub _delete_copy {
788         my($session, $copy, $user_obj) = @_;
789
790         if($apputils->check_user_perms(
791                         $user_obj->id, $user_obj->home_ou, "DELETE_COPY")) {
792                 return OpenILS::Perm->new("DELETE_COPY"); }
793
794         warn "Deleting copy " . $copy->id . "\n";
795         my $request = $session->request(
796                 "open-ils.storage.direct.asset.copy.delete",
797                 $copy );
798         return $request->gather(1);
799 }
800
801 sub _create_copy {
802         my($session, $copy, $user_obj) = @_;
803
804         if($apputils->check_user_perms(
805                         $user_obj->id, $user_obj->home_ou, "CREATE_COPY")) {
806                 return OpenILS::Perm->new("CREATE_COPY"); }
807
808         my $request = $session->request(
809                 "open-ils.storage.direct.asset.copy.create",
810                 $copy );
811         my $id = $request->gather(1);
812
813         if($id < 1) {
814                 throw OpenSRF::EX::ERROR
815                         ("Unable to create new copy " . Dumper($copy));
816         }
817         $copy->id($id);
818         warn "Created copy " . $copy->id . "\n";
819
820         return $copy;
821
822 }
823
824 sub _update_copy {
825         my($session, $copy, $user_obj) = @_;
826
827         my $evt = $apputils->check_perms($user_obj->id, $copy->circ_lib, 'UPDATE_COPY');
828         return $evt if $evt; #XXX NOT YET HANDLED BY CALLER
829
830         my $status = $apputils->simplereq(      
831                 'open-ils.storage',
832                 "open-ils.storage.direct.asset.copy.update", $copy );
833         $logger->debug("Successfully updated copy " . $copy->id );
834         return $status;
835 }
836
837
838 # -----------------------------------------------------------------
839 # Creates/Updates/Deletes a fleshed asset.copy.  
840 # adds/deletes copy stat_cat maps where necessary
841 # -----------------------------------------------------------------
842 sub _fleshed_copy_update {
843         my($session, $copy, $editor) = @_;
844
845         my $stat_cat_entries = $copy->stat_cat_entries;
846         $copy->editor($editor->id);
847         
848         # in case we're fleshed
849         if(ref($copy->status))          {$copy->status( $copy->status->id ); }
850         if(ref($copy->location))        {$copy->location( $copy->location->id ); }
851         if(ref($copy->circ_lib))        {$copy->circ_lib( $copy->circ_lib->id ); }
852
853         warn "Updating copy " . Dumper($copy) . "\n";
854
855         if( $copy->isdeleted ) { 
856                 return _delete_copy($session, $copy, $editor);
857         } elsif( $copy->isnew ) {
858                 $copy = _create_copy($session, $copy, $editor);
859         } elsif( $copy->ischanged ) {
860                 _update_copy($session, $copy, $editor);
861         }
862
863         
864         return 1 unless ( $stat_cat_entries and @$stat_cat_entries );
865
866         my $stat_maps = $session->request(
867                 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.search.owning_copy.atomic",
868                 $copy->id )->gather(1);
869
870         if(!$copy->isnew) { _delete_stale_maps($session, $stat_maps, $copy); }
871         
872         # go through the stat cat update/create process
873         for my $stat_entry (@{$stat_cat_entries}){ 
874                 _copy_update_stat_cats( $session, $copy, $stat_maps, $stat_entry, $editor );
875         }
876         
877         return 1;
878 }
879
880
881 # -----------------------------------------------------------------
882 # Deletes stat maps attached to this copy in the database that
883 # are no longer attached to the current copy
884 # -----------------------------------------------------------------
885 sub _delete_stale_maps {
886         my( $session, $stat_maps, $copy) = @_;
887
888         warn "Deleting stale stat maps for copy " . $copy->id . "\n";
889         for my $map (@$stat_maps) {
890         # if there is no stat cat entry on the copy who's id matches the
891         # current map's id, remove the map from the database
892         if(! grep { $_->id == $map->stat_cat_entry } @{$copy->stat_cat_entries} ) {
893                 my $req = $session->request(
894                         "open-ils.storage.direct.asset.stat_cat_entry_copy_map.delete", $map );
895                 $req->gather(1);
896                 }
897         }
898
899         return $stat_maps;
900 }
901
902
903 # -----------------------------------------------------------------
904 # Searches the stat maps to see if '$entry' already exists on
905 # the given copy.  If it does not, a new stat map is created
906 # for the given entry and copy
907 # -----------------------------------------------------------------
908 sub _copy_update_stat_cats {
909         my ( $session, $copy, $stat_maps, $entry, $editor ) = @_;
910
911         warn "Updating stat maps for copy " . $copy->id . "\n";
912
913         # see if this map already exists
914         for my $map (@$stat_maps) {
915                 if( $map->stat_cat_entry == $entry->id ) {return;}
916         }
917
918         warn "Creating new stat map for stat  " . 
919                 $entry->stat_cat . " and copy " . $copy->id . "\n";
920
921         # if not, create it
922         my $new_map = Fieldmapper::asset::stat_cat_entry_copy_map->new();
923
924         $new_map->stat_cat( $entry->stat_cat );
925         $new_map->stat_cat_entry( $entry->id );
926         $new_map->owning_copy( $copy->id );
927
928         warn "New map is " . Dumper($new_map) . "\n";
929
930         my $request = $session->request(
931                 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.create",
932                 $new_map );
933         my $status = $request->gather(1);
934         warn "created new map with id $status\n";
935
936 }
937
938
939
940
941 1;