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