]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Cat.pm
marc template retrieval but fixes
[working/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
17 my $apputils = "OpenILS::Application::AppUtils";
18
19 my $utils = "OpenILS::Application::Cat::Utils";
20
21 my $conf;
22
23 my %marctemplates;
24
25
26 __PACKAGE__->register_method(
27         method  => "retrieve_marc_template",
28         api_name        => "open-ils.cat.biblio.marc_template.retrieve",
29         notes           => <<"  NOTES");
30         Returns a MARC 'record tree' based on a set of pre-defined templates.
31         Templates include : book
32         NOTES
33
34 sub retrieve_marc_template {
35         my( $self, $client, $type ) = @_;
36         my $xml = _load_marc_template($type);
37         my $nodes = OpenILS::Utils::FlatXML->new()->xml_to_nodeset( $xml ); 
38         return $utils->nodeset2tree( $nodes->nodeset );
39 }
40
41 sub _load_marc_template {
42         my $type = shift;
43
44         if(!defined( $marctemplates{$type} )) {
45                 if(!$conf) { $conf = OpenSRF::Utils::SettingsClient->new; }
46
47                 my $template = $conf->config_value(                                     
48                         "apps", "open-ils.cat","app_settings", "marctemplates", $type );
49                 warn "Opening template file $template\n";
50
51                 open( F, $template );
52                 my @xml = <F>;
53                 close(F);
54                 $marctemplates{$type} = join('', @xml);
55
56         }
57
58         warn "Loaded MARC template XML:\n" . $marctemplates{$type} . "\n";
59
60         return $marctemplates{$type};
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", $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", $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         if($apputils->check_user_perms(
828                         $user_obj->id, $user_obj->home_ou, "UPDATE_COPY")) {
829                 return OpenILS::Perm->new("UPDATE_COPY"); }
830
831         my $request = $session->request(
832                 "open-ils.storage.direct.asset.copy.update", $copy );
833         my $status = $request->gather(1);
834         warn "Updated copy " . $copy->id . "\n";
835         return $status;
836 }
837
838
839 # -----------------------------------------------------------------
840 # Creates/Updates/Deletes a fleshed asset.copy.  
841 # adds/deletes copy stat_cat maps where necessary
842 # -----------------------------------------------------------------
843 sub _fleshed_copy_update {
844         my($session, $copy, $editor) = @_;
845
846         my $stat_cat_entries = $copy->stat_cat_entries;
847         $copy->editor($editor->id);
848         
849         # in case we're fleshed
850         if(ref($copy->status))          {$copy->status( $copy->status->id ); }
851         if(ref($copy->location))        {$copy->location( $copy->location->id ); }
852         if(ref($copy->circ_lib))        {$copy->circ_lib( $copy->circ_lib->id ); }
853
854         warn "Updating copy " . Dumper($copy) . "\n";
855
856         if( $copy->isdeleted ) { 
857                 return _delete_copy($session, $copy, $editor);
858         } elsif( $copy->isnew ) {
859                 $copy = _create_copy($session, $copy, $editor);
860         } elsif( $copy->ischanged ) {
861                 _update_copy($session, $copy, $editor);
862         }
863
864         
865         if(!@$stat_cat_entries) { return 1; }
866
867         my $stat_maps = $session->request(
868                 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.search.owning_copy.atomic",
869                 $copy->id )->gather(1);
870
871         if(!$copy->isnew) { _delete_stale_maps($session, $stat_maps, $copy); }
872         
873         # go through the stat cat update/create process
874         for my $stat_entry (@{$stat_cat_entries}){ 
875                 _copy_update_stat_cats( $session, $copy, $stat_maps, $stat_entry, $editor );
876         }
877         
878         return 1;
879 }
880
881
882 # -----------------------------------------------------------------
883 # Deletes stat maps attached to this copy in the database that
884 # are no longer attached to the current copy
885 # -----------------------------------------------------------------
886 sub _delete_stale_maps {
887         my( $session, $stat_maps, $copy) = @_;
888
889         warn "Deleting stale stat maps for copy " . $copy->id . "\n";
890         for my $map (@$stat_maps) {
891         # if there is no stat cat entry on the copy who's id matches the
892         # current map's id, remove the map from the database
893         if(! grep { $_->id == $map->stat_cat_entry } @{$copy->stat_cat_entries} ) {
894                 my $req = $session->request(
895                         "open-ils.storage.direct.asset.stat_cat_entry_copy_map.delete", $map );
896                 $req->gather(1);
897                 }
898         }
899
900         return $stat_maps;
901 }
902
903
904 # -----------------------------------------------------------------
905 # Searches the stat maps to see if '$entry' already exists on
906 # the given copy.  If it does not, a new stat map is created
907 # for the given entry and copy
908 # -----------------------------------------------------------------
909 sub _copy_update_stat_cats {
910         my ( $session, $copy, $stat_maps, $entry, $editor ) = @_;
911
912         warn "Updating stat maps for copy " . $copy->id . "\n";
913
914         # see if this map already exists
915         for my $map (@$stat_maps) {
916                 if( $map->stat_cat_entry == $entry->id ) {return;}
917         }
918
919         warn "Creating new stat map for stat  " . 
920                 $entry->stat_cat . " and copy " . $copy->id . "\n";
921
922         # if not, create it
923         my $new_map = Fieldmapper::asset::stat_cat_entry_copy_map->new();
924
925         $new_map->stat_cat( $entry->stat_cat );
926         $new_map->stat_cat_entry( $entry->id );
927         $new_map->owning_copy( $copy->id );
928
929         warn "New map is " . Dumper($new_map) . "\n";
930
931         my $request = $session->request(
932                 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.create",
933                 $new_map );
934         my $status = $request->gather(1);
935         warn "created new map with id $status\n";
936
937 }
938
939
940
941
942 1;