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);
10 use OpenILS::Utils::Fieldmapper;
13 use OpenILS::Utils::FlatXML;
15 use OpenSRF::Utils::SettingsClient;
17 my $apputils = "OpenILS::Application::AppUtils";
19 my $utils = "OpenILS::Application::Cat::Utils";
26 __PACKAGE__->register_method(
27 method => "retrieve_marc_template",
28 api_name => "open-ils.cat.biblio.marc_template.retrieve",
30 Returns a MARC 'record tree' based on a set of pre-defined templates.
31 Templates include : book
34 sub retrieve_marc_template {
35 my( $self, $client, $type ) = @_;
36 my $xml = _load_marc_template($type);
37 my $nodes = OpenILS::Utils::FlatXML->new()->xml_to_nodeset( $xml );
38 return $utils->nodeset2tree( $nodes->nodeset );
41 sub _load_marc_template {
44 if(!defined( $marctemplates{$type} )) {
45 if(!$conf) { $conf = OpenSRF::Utils::SettingsClient->new; }
46 my $template = $conf->config_value(
47 "apps", "open-ils.cat","app_settings", "marctemplates", $type );
50 $marctemplates{$type} = join('', @xml);
53 warn "Loaded MARC template XML:\n" . $marctemplates{$type} . "\n";
55 return $marctemplates{$type};
60 __PACKAGE__->register_method(
61 method => "create_record_tree",
62 api_name => "open-ils.cat.biblio.record_tree.create",
64 Inserts a new MARC 'record tree' into the system
67 sub create_record_tree {
68 my( $self, $client, $login, $tree ) = @_;
70 my $user_obj = $apputils->check_user_session($login);
72 if($apputils->check_user_perms(
73 $user_obj->id, $user_obj->home_ou, "CREATE_MARC")) {
74 return OpenILS::Perm->new("CREATE_MARC");
77 warn "Creating a new record tree entry...";
78 my $meth = $self->method_lookup("open-ils.cat.biblio.record.tree.import");
79 my ($s) = $meth->run($login, $tree);
86 __PACKAGE__->register_method(
87 method => "biblio_record_tree_import",
88 api_name => "open-ils.cat.biblio.record.tree.import",
90 Takes a record tree and imports the record into the database. In this
91 case, the record tree is assumed to be a complete record (i.e. valid
92 MARC. The title control number is taken from (whichever comes first)
93 tags 001, 020, 022, 010, 035 and whichever does not already exist
95 user_session must have IMPORT_MARC permissions
99 sub biblio_record_tree_import {
100 my( $self, $client, $user_session, $tree) = @_;
101 my $user_obj = $apputils->check_user_session($user_session);
103 if($apputils->check_user_perms(
104 $user_obj->id, $user_obj->home_ou, "IMPORT_MARC")) {
105 return OpenILS::Perm->new("IMPORT_MARC");
108 warn "importing new record " . Dumper($tree) . "\n";
110 my $nodeset = $utils->tree2nodeset($tree);
111 warn "turned into nodeset " . Dumper($nodeset) . "\n";
113 # copy the doc so that we can mangle the namespace.
114 my $marcxml = OpenILS::Utils::FlatXML->new()->nodeset_to_xml($nodeset);
115 my $copy_marcxml = XML::LibXML->new->parse_string($marcxml->toString);
117 $marcxml->documentElement->setNamespace( "http://www.loc.gov/MARC21/slim", "marc", 1 );
121 warn "Starting db session in import\n";
122 my $session = $apputils->start_db_session();
123 my $source = 2; # system local source
125 my $xpath = '//controlfield[@tag="001"]';
126 $tcn = $marcxml->documentElement->findvalue($xpath);
127 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
128 my $tcn_source = "External";
132 $xpath = '//datafield[@tag="020"]';
133 $tcn = $marcxml->documentElement->findvalue($xpath);
134 $tcn_source = "ISBN";
135 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
139 $xpath = '//datafield[@tag="022"]';
140 $tcn = $marcxml->documentElement->findvalue($xpath);
141 $tcn_source = "ISSN";
142 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
146 $xpath = '//datafield[@tag="010"]';
147 $tcn = $marcxml->documentElement->findvalue($xpath);
148 $tcn_source = "LCCN";
149 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
153 $xpath = '//datafield[@tag="035"]';
154 $tcn = $marcxml->documentElement->findvalue($xpath);
155 $tcn_source = "System";
156 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
162 warn "Record import with tcn: $tcn and source $tcn_source\n";
164 my $record = Fieldmapper::biblio::record_entry->new;
166 $record->source($source);
167 $record->tcn_source($tcn_source);
168 $record->tcn_value($tcn);
169 $record->creator($user_obj->id);
170 $record->editor($user_obj->id);
171 $record->marc($copy_marcxml->toString);
174 my $req = $session->request(
175 "open-ils.storage.direct.biblio.record_entry.create", $record );
177 my $id = $req->gather(1);
179 if(!$id) { throw OpenSRF::EX::ERROR ("Unable to create new record_entry from import"); }
180 warn "received id: $id from record_entry create\n";
182 $apputils->commit_db_session($session);
184 $session = OpenSRF::AppSession->create("open-ils.storage");
186 my $wreq = $session->request("open-ils.worm.wormize", $id)->gather(1);
187 warn "Done worming record $id\n";
189 if(!$wreq) { throw OpenSRF::EX::ERROR ("Unable to wormize imported record"); }
191 return $self->biblio_record_tree_retrieve($client, $id);
199 if(!$tcn) {return 0;}
201 my $req = $session->request(
202 "open-ils.storage.direct.biblio.record_entry.search.tcn_value.atomic",
204 my $recs = $req->gather(1);
206 if($recs and $recs->[0]) {
214 __PACKAGE__->register_method(
215 method => "biblio_record_tree_retrieve",
216 api_name => "open-ils.cat.biblio.record.tree.retrieve",
219 sub biblio_record_tree_retrieve {
221 my( $self, $client, $recordid ) = @_;
223 my $name = "open-ils.storage.direct.biblio.record_entry.retrieve";
224 my $session = OpenSRF::AppSession->create( "open-ils.storage" );
225 my $request = $session->request( $name, $recordid );
226 my $marcxml = $request->gather(1);
229 throw OpenSRF::EX::ERROR
230 ("No record in database with id $recordid");
233 $session->disconnect();
236 warn "turning into nodeset\n";
237 my $nodes = OpenILS::Utils::FlatXML->new()->xml_to_nodeset( $marcxml->marc );
238 warn "turning nodeset into tree\n";
239 my $tree = $utils->nodeset2tree( $nodes->nodeset );
241 $tree->owner_doc( $marcxml->id() );
243 warn "returning tree\n";
248 __PACKAGE__->register_method(
249 method => "biblio_record_tree_commit",
250 api_name => "open-ils.cat.biblio.record.tree.commit",
251 argc => 3, #(session_id, biblio_tree )
252 notes => <<" NOTES");
253 Walks the tree and commits any changed nodes
254 adds any new nodes, and deletes any deleted nodes
255 The record to commit must already exist or this
259 sub biblio_record_tree_commit {
261 my( $self, $client, $user_session, $tree ) = @_;
263 throw OpenSRF::EX::InvalidArg
264 ("Not enough args to to open-ils.cat.biblio.record.tree.commit")
265 unless ( $user_session and $tree );
267 my $user_obj = $apputils->check_user_session($user_session);
269 if($apputils->check_user_perms(
270 $user_obj->id, $user_obj->home_ou, "UPDATE_MARC")) {
271 return OpenILS::Perm->new("UPDATE_MARC");
276 my $docid = $tree->owner_doc();
277 my $session = OpenILS::Application::AppUtils->start_db_session();
279 warn "Retrieving biblio record from storage for update\n";
281 my $req1 = $session->request(
282 "open-ils.storage.direct.biblio.record_entry.batch.retrieve", $docid );
283 my $biblio = $req1->gather(1);
285 warn "retrieved doc $docid\n";
288 # turn the tree into a nodeset
289 my $nodeset = $utils->tree2nodeset($tree);
290 $nodeset = $utils->clean_nodeset($nodeset);
292 if(!defined($docid)) { # be sure
293 for my $node (@$nodeset) {
294 $docid = $node->owner_doc();
295 last if defined($docid);
299 # turn the nodeset into a doc
300 my $marcxml = OpenILS::Utils::FlatXML->new()->nodeset_to_xml( $nodeset );
302 $biblio->marc( $marcxml->toString() );
304 warn "Starting db session\n";
306 my $x = _update_record_metadata( $session, { user => $user_obj, docid => $docid } );
307 OpenILS::Application::AppUtils->rollback_db_session($session) unless $x;
309 warn "Sending updated doc $docid to db\n";
310 my $req = $session->request( "open-ils.storage.direct.biblio.record_entry.update", $biblio );
313 my $status = $req->recv();
314 if( !$status || $status->isa("Error") || ! $status->content) {
315 OpenILS::Application::AppUtils->rollback_db_session($session);
316 if($status->isa("Error")) { throw $status ($status); }
317 throw OpenSRF::EX::ERROR ("Error updating biblio record");
321 # Send the doc to the wormer for wormizing
322 warn "Starting worm session\n";
327 my $wreq = $session->request( "open-ils.worm.wormize", $docid );
334 warn "wormizing failed, rolling back\n";
335 OpenILS::Application::AppUtils->rollback_db_session($session);
337 if($e) { throw $e ($e); }
338 throw OpenSRF::EX::ERROR ("Wormizing Failed for $docid" );
341 warn "Committing db session...\n";
342 OpenILS::Application::AppUtils->commit_db_session( $session );
344 $nodeset = OpenILS::Utils::FlatXML->new()->xmldoc_to_nodeset($marcxml);
345 $tree = $utils->nodeset2tree($nodeset->nodeset);
346 $tree->owner_doc($docid);
348 # $client->respond_complete($tree);
350 warn "Done wormizing\n";
353 #warn "Returning tree:\n";
362 __PACKAGE__->register_method(
363 method => "biblio_record_record_metadata",
364 api_name => "open-ils.cat.biblio.record.metadata.retrieve",
365 argc => 1, #(session_id, biblio_tree )
366 notes => "Walks the tree and commits any changed nodes " .
367 "adds any new nodes, and deletes any deleted nodes",
370 sub biblio_record_record_metadata {
371 my( $self, $client, @ids ) = @_;
373 if(!@ids){return undef;}
375 my $session = OpenSRF::AppSession->create("open-ils.storage");
376 my $request = $session->request(
377 "open-ils.storage.direct.biblio.record_entry.batch.retrieve", @ids );
381 while( my $response = $request->recv() ) {
384 throw OpenSRF::EX::ERROR ("No Response from Storage");
386 if($response->isa("Error")) {
387 throw $response ($response->stringify);
390 my $record_entry = $response->content;
392 my $creator = $record_entry->creator;
393 my $editor = $record_entry->editor;
395 ($creator, $editor) = _get_userid_by_id($creator, $editor);
397 $record_entry->creator($creator);
398 $record_entry->editor($editor);
400 push @$results, $record_entry;
405 $session->disconnect();
413 sub _get_userid_by_id {
418 my $session = OpenSRF::AppSession->create( "open-ils.storage" );
419 my $request = $session->request(
420 "open-ils.storage.direct.actor.user.batch.retrieve.atomic", @ids );
422 $request->wait_complete;
423 my $response = $request->recv();
424 if(!$request->complete) { return undef; }
426 if($response->isa("Error")){
427 throw $response ($response);
430 for my $u (@{$response->content}) {
432 push @users, $u->usrname;
436 $session->disconnect;
442 sub _get_id_by_userid {
447 my $session = OpenSRF::AppSession->create( "open-ils.storage" );
448 my $request = $session->request(
449 "open-ils.storage.direct.actor.user.search.usrname.atomic", @users );
451 $request->wait_complete;
452 my $response = $request->recv();
453 if(!$request->complete) {
454 throw OpenSRF::EX::ERROR ("no response from storage on user retrieve");
457 if(UNIVERSAL::isa( $response, "Error")){
458 throw $response ($response);
461 for my $u (@{$response->content}) {
467 $session->disconnect;
474 # commits metadata objects to the db
475 sub _update_record_metadata {
477 my ($session, @docs ) = @_;
479 for my $doc (@docs) {
481 my $user_obj = $doc->{user};
482 my $docid = $doc->{docid};
484 warn "Updating metata for doc $docid\n";
486 my $request = $session->request(
487 "open-ils.storage.direct.biblio.record_entry.retrieve", $docid );
488 my $record = $request->gather(1);
490 warn "retrieved record\n";
491 my ($id) = _get_id_by_userid($user_obj->usrname);
493 warn "got $id from _get_id_by_userid\n";
494 $record->editor($id);
496 warn "Grabbed the record, updating and moving on\n";
498 $request = $session->request(
499 "open-ils.storage.direct.biblio.record_entry.update", $record );
503 warn "committing metarecord update\n";
510 __PACKAGE__->register_method(
511 method => "orgs_for_title",
512 api_name => "open-ils.cat.actor.org_unit.retrieve_by_title"
516 my( $self, $client, $record_id ) = @_;
518 my $vols = $apputils->simple_scalar_request(
520 "open-ils.storage.direct.asset.call_number.search.record.atomic",
523 my $orgs = { map {$_->owning_lib => 1 } @$vols };
524 return [ keys %$orgs ];
529 __PACKAGE__->register_method(
530 method => "retrieve_copies",
531 api_name => "open-ils.cat.asset.copy_tree.retrieve");
533 __PACKAGE__->register_method(
534 method => "retrieve_copies",
535 api_name => "open-ils.cat.asset.copy_tree.global.retrieve");
537 # user_session may be null/undef
538 sub retrieve_copies {
540 my( $self, $client, $user_session, $docid, @org_ids ) = @_;
542 if(ref($org_ids[0])) { @org_ids = @{$org_ids[0]}; }
546 warn " $$ retrieving copy tree for orgs @org_ids and doc $docid at " . time() . "\n";
548 # grabbing copy trees should be available for everyone..
549 if(!@org_ids and $user_session) {
551 OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
552 @org_ids = ($user_obj->home_ou);
555 if( $self->api_name =~ /global/ ) {
556 warn "performing global copy_tree search for $docid\n";
557 return _build_volume_list( { record => $docid } );
562 for my $orgid (@org_ids) {
563 my $vols = _build_volume_list(
564 { record => $docid, owning_lib => $orgid } );
565 warn "Volumes built for org $orgid\n";
566 push( @all_vols, @$vols );
569 warn " $$ Finished copy_tree at " . time() . "\n";
577 sub _build_volume_list {
578 my $search_hash = shift;
580 my $session = OpenSRF::AppSession->create( "open-ils.storage" );
583 my $request = $session->request(
584 "open-ils.storage.direct.asset.call_number.search.atomic", $search_hash );
586 my $vols = $request->gather(1);
589 for my $volume (@$vols) {
591 warn "Grabbing copies for volume: " . $volume->id . "\n";
592 my $creq = $session->request(
593 "open-ils.storage.direct.asset.copy.search.call_number.atomic", $volume->id );
595 my $copies = $creq->gather(1);
597 $copies = [ sort { $a->barcode cmp $b->barcode } @$copies ];
599 $volume->copies($copies);
601 push( @volumes, $volume );
605 $session->disconnect();
611 # -----------------------------------------------------------------
612 # Fleshed volume tree batch add/update. This does everything a
613 # volume tree could want, add, update, delete
614 # -----------------------------------------------------------------
615 __PACKAGE__->register_method(
616 method => "volume_tree_fleshed_update",
617 api_name => "open-ils.cat.asset.volume_tree.fleshed.batch.update",
619 sub volume_tree_fleshed_update {
621 my( $self, $client, $user_session, $volumes ) = @_;
622 return undef unless $volumes;
624 my $user_obj = $apputils->check_user_session($user_session);
627 my $session = $apputils->start_db_session();
628 warn "Looping on volumes in fleshed volume tree update\n";
630 # cycle through the volumes provided and update/create/delete where necessary
631 for my $volume (@$volumes) {
633 warn "updating volume " . $volume->id . "\n";
635 my $update_copy_list = $volume->copies;
638 if( $volume->isdeleted) {
639 my $status = _delete_volume($session, $volume, $user_obj);
641 throw OpenSRF::EX::ERROR
642 ("Volume delete failed for volume " . $volume->id);
644 if(UNIVERSAL::isa($status, "Fieldmapper::perm_ex")) { return $status; }
646 } elsif( $volume->isnew ) {
649 $volume->editor($user_obj->id);
650 $volume->creator($user_obj->id);
651 $volume = _add_volume($session, $volume, $user_obj);
654 if($volume and UNIVERSAL::isa($volume, "Fieldmapper::perm_ex")) { return $volume; }
656 } elsif( $volume->ischanged ) {
658 $volume->editor($user_obj->id);
659 my $stat = _update_volume($session, $volume, $user_obj);
660 if($stat and UNIVERSAL::isa($stat, "Fieldmapper::perm_ex")) { return $stat; }
664 if( ! $volume->isdeleted ) {
665 for my $copy (@{$update_copy_list}) {
667 $copy->editor($user_obj->id);
668 warn "updating copy for volume " . $volume->id . "\n";
673 $copy->call_number($volume->id);
674 $copy->creator($user_obj->id);
675 $copy = _fleshed_copy_update($session,$copy,$user_obj);
677 } elsif( $copy->ischanged ) {
678 $copy->call_number($volume->id);
679 $copy = _fleshed_copy_update($session, $copy, $user_obj);
681 } elsif( $copy->isdeleted ) {
682 warn "Deleting copy " . $copy->id . " for volume " . $volume->id . "\n";
683 my $status = _fleshed_copy_update($session, $copy, $user_obj);
684 warn "Copy delete returned a status of $status\n";
690 $apputils->commit_db_session($session);
691 return scalar(@$volumes);
696 my( $session, $volume, $user_obj ) = @_;
698 if($apputils->check_user_perms(
699 $user_obj->id, $user_obj->home_ou, "DELETE_VOLUME")) {
700 return OpenILS::Perm->new("DELETE_VOLUME"); }
702 #$volume = _find_volume($session, $volume);
703 warn "Deleting volume " . $volume->id . "\n";
705 my $copies = $session->request(
706 "open-ils.storage.direct.asset.copy.search.call_number.atomic",
707 $volume->id )->gather(1);
709 throw OpenSRF::EX::ERROR
710 ("Cannot remove volume with copies attached");
713 my $req = $session->request(
714 "open-ils.storage.direct.asset.call_number.delete",
716 return $req->gather(1);
721 my($session, $volume, $user_obj) = @_;
722 if($apputils->check_user_perms(
723 $user_obj->id, $user_obj->home_ou, "UPDATE_VOLUME")) {
724 return OpenILS::Perm->new("UPDATE_VOLUME"); }
726 my $req = $session->request(
727 "open-ils.storage.direct.asset.call_number.update",
729 my $status = $req->gather(1);
734 my($session, $volume, $user_obj) = @_;
736 if($apputils->check_user_perms(
737 $user_obj->id, $user_obj->home_ou, "CREATE_VOLUME")) {
738 warn "User does not have priveleges to create new volumes\n";
739 return OpenILS::Perm->new("CREATE_VOLUME");
742 my $request = $session->request(
743 "open-ils.storage.direct.asset.call_number.create", $volume );
745 my $id = $request->gather(1);
748 OpenILS::Application::AppUtils->rollback_db_session($session);
749 throw OpenSRF::EX::ERROR (" * -> Error creating new volume");
753 warn "received new volume id: $id\n";
761 __PACKAGE__->register_method(
762 method => "fleshed_copy_update",
763 api_name => "open-ils.cat.asset.copy.fleshed.batch.update",
766 sub fleshed_copy_update {
767 my($self, $client, $user_session, $copies) = @_;
769 my $user_obj = $apputils->check_user_session($user_session);
770 my $session = $apputils->start_db_session();
772 for my $copy (@$copies) {
773 _fleshed_copy_update($session, $copy, $user_obj);
776 $apputils->commit_db_session($session);
783 my($session, $copy, $user_obj) = @_;
785 if($apputils->check_user_perms(
786 $user_obj->id, $user_obj->home_ou, "DELETE_COPY")) {
787 return OpenILS::Perm->new("DELETE_COPY"); }
789 warn "Deleting copy " . $copy->id . "\n";
790 my $request = $session->request(
791 "open-ils.storage.direct.asset.copy.delete",
793 return $request->gather(1);
797 my($session, $copy, $user_obj) = @_;
799 if($apputils->check_user_perms(
800 $user_obj->id, $user_obj->home_ou, "CREATE_COPY")) {
801 return OpenILS::Perm->new("CREATE_COPY"); }
803 my $request = $session->request(
804 "open-ils.storage.direct.asset.copy.create",
806 my $id = $request->gather(1);
809 throw OpenSRF::EX::ERROR
810 ("Unable to create new copy " . Dumper($copy));
813 warn "Created copy " . $copy->id . "\n";
820 my($session, $copy, $user_obj) = @_;
822 if($apputils->check_user_perms(
823 $user_obj->id, $user_obj->home_ou, "UPDATE_COPY")) {
824 return OpenILS::Perm->new("UPDATE_COPY"); }
826 my $request = $session->request(
827 "open-ils.storage.direct.asset.copy.update", $copy );
828 my $status = $request->gather(1);
829 warn "Updated copy " . $copy->id . "\n";
834 # -----------------------------------------------------------------
835 # Creates/Updates/Deletes a fleshed asset.copy.
836 # adds/deletes copy stat_cat maps where necessary
837 # -----------------------------------------------------------------
838 sub _fleshed_copy_update {
839 my($session, $copy, $editor) = @_;
841 my $stat_cat_entries = $copy->stat_cat_entries;
842 $copy->editor($editor->id);
844 # in case we're fleshed
845 if(ref($copy->status)) {$copy->status( $copy->status->id ); }
846 if(ref($copy->location)) {$copy->location( $copy->location->id ); }
847 if(ref($copy->circ_lib)) {$copy->circ_lib( $copy->circ_lib->id ); }
849 warn "Updating copy " . Dumper($copy) . "\n";
851 if( $copy->isdeleted ) {
852 return _delete_copy($session, $copy, $editor);
853 } elsif( $copy->isnew ) {
854 $copy = _create_copy($session, $copy, $editor);
855 } elsif( $copy->ischanged ) {
856 _update_copy($session, $copy, $editor);
860 if(!@$stat_cat_entries) { return 1; }
862 my $stat_maps = $session->request(
863 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.search.owning_copy.atomic",
864 $copy->id )->gather(1);
866 if(!$copy->isnew) { _delete_stale_maps($session, $stat_maps, $copy); }
868 # go through the stat cat update/create process
869 for my $stat_entry (@{$stat_cat_entries}){
870 _copy_update_stat_cats( $session, $copy, $stat_maps, $stat_entry, $editor );
877 # -----------------------------------------------------------------
878 # Deletes stat maps attached to this copy in the database that
879 # are no longer attached to the current copy
880 # -----------------------------------------------------------------
881 sub _delete_stale_maps {
882 my( $session, $stat_maps, $copy) = @_;
884 warn "Deleting stale stat maps for copy " . $copy->id . "\n";
885 for my $map (@$stat_maps) {
886 # if there is no stat cat entry on the copy who's id matches the
887 # current map's id, remove the map from the database
888 if(! grep { $_->id == $map->stat_cat_entry } @{$copy->stat_cat_entries} ) {
889 my $req = $session->request(
890 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.delete", $map );
899 # -----------------------------------------------------------------
900 # Searches the stat maps to see if '$entry' already exists on
901 # the given copy. If it does not, a new stat map is created
902 # for the given entry and copy
903 # -----------------------------------------------------------------
904 sub _copy_update_stat_cats {
905 my ( $session, $copy, $stat_maps, $entry, $editor ) = @_;
907 warn "Updating stat maps for copy " . $copy->id . "\n";
909 # see if this map already exists
910 for my $map (@$stat_maps) {
911 if( $map->stat_cat_entry == $entry->id ) {return;}
914 warn "Creating new stat map for stat " .
915 $entry->stat_cat . " and copy " . $copy->id . "\n";
918 my $new_map = Fieldmapper::asset::stat_cat_entry_copy_map->new();
920 $new_map->stat_cat( $entry->stat_cat );
921 $new_map->stat_cat_entry( $entry->id );
922 $new_map->owning_copy( $copy->id );
924 warn "New map is " . Dumper($new_map) . "\n";
926 my $request = $session->request(
927 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.create",
929 my $status = $request->gather(1);
930 warn "created new map with id $status\n";