]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Cat.pm
7cc1244a15fc85aa325a27813d78bf7a7ef93e93
[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", $volume->id );
529
530                 my $copies = $creq->gather(1);
531
532                 $copies = [ sort { $a->barcode cmp $b->barcode } @$copies  ];
533
534                 $volume->copies($copies);
535
536                 push( @volumes, $volume );
537         }
538
539
540         $session->disconnect();
541         return \@volumes;
542
543 }
544
545
546 # -----------------------------------------------------------------
547 # Fleshed volume tree batch add/update.  This does everything a 
548 # volume tree could want, add, update, delete
549 # -----------------------------------------------------------------
550 __PACKAGE__->register_method(
551         method  => "volume_tree_fleshed_update",
552         api_name        => "open-ils.cat.asset.volume_tree.fleshed.batch.update",
553 );
554 sub volume_tree_fleshed_update {
555
556         my( $self, $client, $user_session, $volumes ) = @_;
557         return undef unless $volumes;
558
559         my $user_obj = $apputils->check_user_session($user_session);
560
561
562         my $session = $apputils->start_db_session();
563         warn "Looping on volumes in fleshed volume tree update\n";
564
565         # cycle through the volumes provided and update/create/delete where necessary
566         for my $volume (@$volumes) {
567
568                 warn "updating volume " . $volume->id . "\n";
569
570                 my $update_copy_list = $volume->copies;
571
572
573                 if( $volume->isdeleted) {
574                         my $status = _delete_volume($session, $volume, $user_obj);
575                         if(!$status) {
576                                 throw OpenSRF::EX::ERROR
577                                         ("Volume delete failed for volume " . $volume->id);
578                         }
579                         if(UNIVERSAL::isa($status, "Fieldmapper::perm_ex")) { return $status; }
580
581                 } elsif( $volume->isnew ) {
582
583                         $volume->clear_id;
584                         $volume->editor($user_obj->id);
585                         $volume->creator($user_obj->id);
586                         $volume = _add_volume($session, $volume, $user_obj);
587                         use Data::Dumper;
588                         warn Dumper $volume;
589                         if($volume and UNIVERSAL::isa($volume, "Fieldmapper::perm_ex")) { return $volume; }
590
591                 } elsif( $volume->ischanged ) {
592
593                         $volume->editor($user_obj->id);
594                         my $stat = _update_volume($session, $volume, $user_obj);
595                         if($stat and UNIVERSAL::isa($stat, "Fieldmapper::perm_ex")) { return $stat; }
596                 }
597
598
599                 if( ! $volume->isdeleted ) {
600                         for my $copy (@{$update_copy_list}) {
601         
602                                 $copy->editor($user_obj->id);
603                                 warn "updating copy for volume " . $volume->id . "\n";
604         
605                                 if( $copy->isnew ) {
606         
607                                         $copy->clear_id;
608                                         $copy->call_number($volume->id);
609                                         $copy->creator($user_obj->id);
610                                         $copy = _fleshed_copy_update($session,$copy,$user_obj);
611         
612                                 } elsif( $copy->ischanged ) {
613                                         $copy->call_number($volume->id);
614                                         $copy = _fleshed_copy_update($session, $copy, $user_obj);
615         
616                                 } elsif( $copy->isdeleted ) {
617                                         warn "Deleting copy " . $copy->id . " for volume " . $volume->id . "\n";
618                                         my $status = _fleshed_copy_update($session, $copy, $user_obj);
619                                         warn "Copy delete returned a status of $status\n";
620                                 }
621                         }
622                 }
623         }
624
625         $apputils->commit_db_session($session);
626         return scalar(@$volumes);
627 }
628
629
630 sub _delete_volume {
631         my( $session, $volume, $user_obj ) = @_;
632
633         if($apputils->check_user_perms(
634                         $user_obj->id, $user_obj->home_ou, "DELETE_VOLUME")) {
635                 return OpenILS::Perm->new("DELETE_VOLUME"); }
636
637         #$volume = _find_volume($session, $volume);
638         warn "Deleting volume " . $volume->id . "\n";
639
640         my $copies = $session->request(
641                 "open-ils.storage.direct.asset.copy.search.call_number.atomic",
642                 $volume->id )->gather(1);
643         if(@$copies) {
644                 throw OpenSRF::EX::ERROR 
645                         ("Cannot remove volume with copies attached");
646         }
647
648         my $req = $session->request(
649                 "open-ils.storage.direct.asset.call_number.delete",
650                 $volume );
651         return $req->gather(1);
652 }
653
654
655 sub _update_volume {
656         my($session, $volume, $user_obj) = @_;
657         if($apputils->check_user_perms(
658                         $user_obj->id, $user_obj->home_ou, "UPDATE_VOLUME")) {
659                 return OpenILS::Perm->new("UPDATE_VOLUME"); }
660
661         my $req = $session->request(
662                 "open-ils.storage.direct.asset.call_number.update",
663                 $volume );
664         my $status = $req->gather(1);
665 }
666
667 sub _add_volume {
668
669         my($session, $volume, $user_obj) = @_;
670
671         if($apputils->check_user_perms(
672                         $user_obj->id, $user_obj->home_ou, "CREATE_VOLUME")) {
673                 warn "User does not have priveleges to create new volumes\n";
674                 return OpenILS::Perm->new("CREATE_VOLUME"); 
675         }
676
677         my $request = $session->request( 
678                 "open-ils.storage.direct.asset.call_number.create", $volume );
679
680         my $id = $request->gather(1);
681
682         if( $id == 0 ) {
683                 OpenILS::Application::AppUtils->rollback_db_session($session);
684                 throw OpenSRF::EX::ERROR (" * -> Error creating new volume");
685         }
686
687         $volume->id($id);
688         warn "received new volume id: $id\n";
689         return $volume;
690
691 }
692
693
694
695
696 __PACKAGE__->register_method(
697         method  => "fleshed_copy_update",
698         api_name        => "open-ils.cat.asset.copy.fleshed.batch.update",
699 );
700
701 sub fleshed_copy_update {
702         my($self, $client, $user_session, $copies) = @_;
703
704         my $user_obj = $apputils->check_user_session($user_session); 
705         my $session = $apputils->start_db_session();
706
707         for my $copy (@$copies) {
708                 _fleshed_copy_update($session, $copy, $user_obj);
709         }
710
711         $apputils->commit_db_session($session);
712         return 1;
713 }
714
715
716
717 sub _delete_copy {
718         my($session, $copy, $user_obj) = @_;
719
720         if($apputils->check_user_perms(
721                         $user_obj->id, $user_obj->home_ou, "DELETE_COPY")) {
722                 return OpenILS::Perm->new("DELETE_COPY"); }
723
724         warn "Deleting copy " . $copy->id . "\n";
725         my $request = $session->request(
726                 "open-ils.storage.direct.asset.copy.delete",
727                 $copy );
728         return $request->gather(1);
729 }
730
731 sub _create_copy {
732         my($session, $copy, $user_obj) = @_;
733
734         if($apputils->check_user_perms(
735                         $user_obj->id, $user_obj->home_ou, "CREATE_COPY")) {
736                 return OpenILS::Perm->new("CREATE_COPY"); }
737
738         my $request = $session->request(
739                 "open-ils.storage.direct.asset.copy.create",
740                 $copy );
741         my $id = $request->gather(1);
742
743         if($id < 1) {
744                 throw OpenSRF::EX::ERROR
745                         ("Unable to create new copy " . Dumper($copy));
746         }
747         $copy->id($id);
748         warn "Created copy " . $copy->id . "\n";
749
750         return $copy;
751
752 }
753
754 sub _update_copy {
755         my($session, $copy, $user_obj) = @_;
756
757         if($apputils->check_user_perms(
758                         $user_obj->id, $user_obj->home_ou, "UPDATE_COPY")) {
759                 return OpenILS::Perm->new("UPDATE_COPY"); }
760
761         my $request = $session->request(
762                 "open-ils.storage.direct.asset.copy.update", $copy );
763         my $status = $request->gather(1);
764         warn "Updated copy " . $copy->id . "\n";
765         return $status;
766 }
767
768
769 # -----------------------------------------------------------------
770 # Creates/Updates/Deletes a fleshed asset.copy.  
771 # adds/deletes copy stat_cat maps where necessary
772 # -----------------------------------------------------------------
773 sub _fleshed_copy_update {
774         my($session, $copy, $editor) = @_;
775
776         my $stat_cat_entries = $copy->stat_cat_entries;
777         $copy->editor($editor->id);
778         
779         # in case we're fleshed
780         if(ref($copy->status))          {$copy->status( $copy->status->id ); }
781         if(ref($copy->location))        {$copy->location( $copy->location->id ); }
782         if(ref($copy->circ_lib))        {$copy->circ_lib( $copy->circ_lib->id ); }
783
784         warn "Updating copy " . Dumper($copy) . "\n";
785
786         if( $copy->isdeleted ) { 
787                 return _delete_copy($session, $copy, $editor);
788         } elsif( $copy->isnew ) {
789                 $copy = _create_copy($session, $copy, $editor);
790         } elsif( $copy->ischanged ) {
791                 _update_copy($session, $copy, $editor);
792         }
793
794         
795         if(!@$stat_cat_entries) { return 1; }
796
797         my $stat_maps = $session->request(
798                 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.search.owning_copy.atomic",
799                 $copy->id )->gather(1);
800
801         if(!$copy->isnew) { _delete_stale_maps($session, $stat_maps, $copy); }
802         
803         # go through the stat cat update/create process
804         for my $stat_entry (@{$stat_cat_entries}){ 
805                 _copy_update_stat_cats( $session, $copy, $stat_maps, $stat_entry, $editor );
806         }
807         
808         return 1;
809 }
810
811
812 # -----------------------------------------------------------------
813 # Deletes stat maps attached to this copy in the database that
814 # are no longer attached to the current copy
815 # -----------------------------------------------------------------
816 sub _delete_stale_maps {
817         my( $session, $stat_maps, $copy) = @_;
818
819         warn "Deleting stale stat maps for copy " . $copy->id . "\n";
820         for my $map (@$stat_maps) {
821         # if there is no stat cat entry on the copy who's id matches the
822         # current map's id, remove the map from the database
823         if(! grep { $_->id == $map->stat_cat_entry } @{$copy->stat_cat_entries} ) {
824                 my $req = $session->request(
825                         "open-ils.storage.direct.asset.stat_cat_entry_copy_map.delete", $map );
826                 $req->gather(1);
827                 }
828         }
829
830         return $stat_maps;
831 }
832
833
834 # -----------------------------------------------------------------
835 # Searches the stat maps to see if '$entry' already exists on
836 # the given copy.  If it does not, a new stat map is created
837 # for the given entry and copy
838 # -----------------------------------------------------------------
839 sub _copy_update_stat_cats {
840         my ( $session, $copy, $stat_maps, $entry, $editor ) = @_;
841
842         warn "Updating stat maps for copy " . $copy->id . "\n";
843
844         # see if this map already exists
845         for my $map (@$stat_maps) {
846                 if( $map->stat_cat_entry == $entry->id ) {return;}
847         }
848
849         warn "Creating new stat map for stat  " . 
850                 $entry->stat_cat . " and copy " . $copy->id . "\n";
851
852         # if not, create it
853         my $new_map = Fieldmapper::asset::stat_cat_entry_copy_map->new();
854
855         $new_map->stat_cat( $entry->stat_cat );
856         $new_map->stat_cat_entry( $entry->id );
857         $new_map->owning_copy( $copy->id );
858
859         warn "New map is " . Dumper($new_map) . "\n";
860
861         my $request = $session->request(
862                 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.create",
863                 $new_map );
864         my $status = $request->gather(1);
865         warn "created new map with id $status\n";
866
867 }
868
869
870
871
872 1;