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