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