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