]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Cat.pm
fixed bug in opensrf_all
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Cat.pm
1 use strict; use warnings;
2 package OpenILS::Application::Cat;
3 use OpenILS::Application::AppUtils;
4 use OpenSRF::Application;
5 use OpenILS::Application::Cat::Utils;
6 use base qw/OpenSRF::Application/;
7 use Time::HiRes qw(time);
8 use OpenSRF::EX qw(:try);
9 use JSON;
10 use OpenILS::Utils::Fieldmapper;
11 use XML::LibXML;
12 use Data::Dumper;
13 use OpenILS::Utils::FlatXML;
14 use OpenILS::Perm;
15 use OpenSRF::Utils::SettingsClient;
16
17 my $apputils = "OpenILS::Application::AppUtils";
18
19 my $utils = "OpenILS::Application::Cat::Utils";
20
21 my $conf;
22
23 my %marctemplates;
24
25
26 __PACKAGE__->register_method(
27         method  => "retrieve_marc_template",
28         api_name        => "open-ils.cat.biblio.marc_template.retrieve",
29         notes           => <<"  NOTES");
30         Returns a MARC 'record tree' based on a set of pre-defined templates.
31         Templates include : book
32         NOTES
33
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 );
39 }
40
41 sub _load_marc_template {
42         my $type = shift;
43
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 );
48                 open( F, $template );
49                 my @xml = <F>;
50                 $marctemplates{$type} = join('', @xml);
51         }
52
53         warn "Loaded MARC template XML:\n" . $marctemplates{$type} . "\n";
54
55         return $marctemplates{$type};
56 }
57
58
59
60 __PACKAGE__->register_method(
61         method  => "create_record_tree",
62         api_name        => "open-ils.cat.biblio.record_tree.create",
63         notes           => <<"  NOTES");
64         Inserts a new MARC 'record tree' into the system
65         NOTES
66
67 sub create_record_tree {
68         my( $self, $client, $login, $tree ) = @_;
69
70         my $user_obj = $apputils->check_user_session($login);
71
72         if($apputils->check_user_perms(
73                         $user_obj->id, $user_obj->home_ou, "CREATE_MARC")) {
74                 return OpenILS::Perm->new("CREATE_MARC"); 
75         }
76
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);
80         return $s;
81 }
82
83
84
85
86 __PACKAGE__->register_method(
87         method  => "biblio_record_tree_import",
88         api_name        => "open-ils.cat.biblio.record.tree.import",
89         notes           => <<"  NOTES");
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
94         in the database.
95         user_session must have IMPORT_MARC permissions
96         NOTES
97
98
99 sub biblio_record_tree_import {
100         my( $self, $client, $user_session, $tree) = @_;
101         my $user_obj = $apputils->check_user_session($user_session);
102
103         if($apputils->check_user_perms(
104                         $user_obj->id, $user_obj->home_ou, "IMPORT_MARC")) {
105                 return OpenILS::Perm->new("IMPORT_MARC"); 
106         }
107
108         warn "importing new record " . Dumper($tree) . "\n";
109
110         my $nodeset = $utils->tree2nodeset($tree);
111         warn "turned into nodeset " . Dumper($nodeset) . "\n";
112
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);
116
117         $marcxml->documentElement->setNamespace( "http://www.loc.gov/MARC21/slim", "marc", 1 );
118         my $tcn;
119
120
121         warn "Starting db session in import\n";
122         my $session = $apputils->start_db_session();
123         my $source = 2; # system local source
124
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";
129
130
131         if(!$tcn) {
132                 $xpath = '//datafield[@tag="020"]';
133                 $tcn = $marcxml->documentElement->findvalue($xpath);
134                 $tcn_source = "ISBN";
135                 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
136         }
137
138         if(!$tcn) { 
139                 $xpath = '//datafield[@tag="022"]';
140                 $tcn = $marcxml->documentElement->findvalue($xpath);
141                 $tcn_source = "ISSN";
142                 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
143         }
144
145         if(!$tcn) {
146                 $xpath = '//datafield[@tag="010"]';
147                 $tcn = $marcxml->documentElement->findvalue($xpath);
148                 $tcn_source = "LCCN";
149                 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
150         }
151
152         if(!$tcn) {
153                 $xpath = '//datafield[@tag="035"]';
154                 $tcn = $marcxml->documentElement->findvalue($xpath);
155                 $tcn_source = "System";
156                 if(_tcn_exists($session, $tcn)) {$tcn = undef;}
157         }
158
159         $tcn =~ s/^\s+//g;
160         $tcn =~ s/\s+$//g;
161
162         warn "Record import with tcn: $tcn and source $tcn_source\n";
163
164         my $record = Fieldmapper::biblio::record_entry->new;
165
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);
172
173
174         my $req = $session->request(
175                 "open-ils.storage.direct.biblio.record_entry.create", $record );
176
177         my $id = $req->gather(1);
178
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";
181
182         $apputils->commit_db_session($session);
183
184         $session = OpenSRF::AppSession->create("open-ils.storage");
185
186         my $wreq = $session->request("open-ils.worm.wormize", $id)->gather(1);
187         warn "Done worming record $id\n";
188
189         if(!$wreq) { throw OpenSRF::EX::ERROR ("Unable to wormize imported record"); }
190
191         return $self->biblio_record_tree_retrieve($client, $id);
192
193 }
194
195 sub _tcn_exists {
196         my $session = shift;
197         my $tcn = shift;
198
199         if(!$tcn) {return 0;}
200
201         my $req = $session->request(      
202                 "open-ils.storage.direct.biblio.record_entry.search.tcn_value.atomic",
203                 $tcn );
204         my $recs = $req->gather(1);
205
206         if($recs and $recs->[0]) {
207                 return 1;
208         }
209         return 0;
210 }
211
212
213
214 __PACKAGE__->register_method(
215         method  => "biblio_record_tree_retrieve",
216         api_name        => "open-ils.cat.biblio.record.tree.retrieve",
217 );
218
219 sub biblio_record_tree_retrieve {
220
221         my( $self, $client, $recordid ) = @_;
222
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);
227
228         if(!$marcxml) {
229                 throw OpenSRF::EX::ERROR 
230                         ("No record in database with id $recordid");
231         }
232
233         $session->disconnect();
234         $session->kill_me();
235
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 );
240
241         $tree->owner_doc( $marcxml->id() );
242
243         warn "returning tree\n";
244
245         return $tree;
246 }
247
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
256         method will fail
257         NOTES
258
259 sub biblio_record_tree_commit {
260
261         my( $self, $client, $user_session,  $tree ) = @_;
262
263         throw OpenSRF::EX::InvalidArg 
264                 ("Not enough args to to open-ils.cat.biblio.record.tree.commit")
265                 unless ( $user_session and $tree );
266
267         my $user_obj = $apputils->check_user_session($user_session); 
268
269         if($apputils->check_user_perms(
270                         $user_obj->id, $user_obj->home_ou, "UPDATE_MARC")) {
271                 return OpenILS::Perm->new("UPDATE_MARC"); 
272         }
273
274
275         # capture the doc id
276         my $docid = $tree->owner_doc();
277         my $session = OpenILS::Application::AppUtils->start_db_session();
278
279         warn "Retrieving biblio record from storage for update\n";
280
281         my $req1 = $session->request(
282                         "open-ils.storage.direct.biblio.record_entry.batch.retrieve", $docid );
283         my $biblio = $req1->gather(1);
284
285         warn "retrieved doc $docid\n";
286
287
288         # turn the tree into a nodeset
289         my $nodeset = $utils->tree2nodeset($tree);
290         $nodeset = $utils->clean_nodeset($nodeset);
291
292         if(!defined($docid)) { # be sure
293                 for my $node (@$nodeset) {
294                         $docid = $node->owner_doc();
295                         last if defined($docid);
296                 }
297         }
298
299         # turn the nodeset into a doc
300         my $marcxml = OpenILS::Utils::FlatXML->new()->nodeset_to_xml( $nodeset );
301
302         $biblio->marc( $marcxml->toString() );
303
304         warn "Starting db session\n";
305
306         my $x = _update_record_metadata( $session, { user => $user_obj, docid => $docid } );
307         OpenILS::Application::AppUtils->rollback_db_session($session) unless $x;
308
309         warn "Sending updated doc $docid to db\n";
310         my $req = $session->request( "open-ils.storage.direct.biblio.record_entry.update", $biblio );
311
312         $req->wait_complete;
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");
318         }
319         $req->finish();
320
321         # Send the doc to the wormer for wormizing
322         warn "Starting worm session\n";
323
324         my $success = 0;
325         my $wresp;
326
327         my $wreq = $session->request( "open-ils.worm.wormize", $docid );
328
329         try {
330                 $wreq->gather(1);
331
332         } catch Error with {
333                 my $e = shift;
334                 warn "wormizing failed, rolling back\n";
335                 OpenILS::Application::AppUtils->rollback_db_session($session);
336
337                 if($e) { throw $e ($e); }
338                 throw OpenSRF::EX::ERROR ("Wormizing Failed for $docid" );
339         };
340
341         warn "Committing db session...\n";
342         OpenILS::Application::AppUtils->commit_db_session( $session );
343
344         $nodeset = OpenILS::Utils::FlatXML->new()->xmldoc_to_nodeset($marcxml);
345         $tree = $utils->nodeset2tree($nodeset->nodeset);
346         $tree->owner_doc($docid);
347
348 #       $client->respond_complete($tree);
349
350         warn "Done wormizing\n";
351
352         #use Data::Dumper;
353         #warn "Returning tree:\n";
354         #warn Dumper $tree;
355
356         return $tree;
357
358 }
359
360
361
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",
368 );
369
370 sub biblio_record_record_metadata {
371         my( $self, $client, @ids ) = @_;
372
373         if(!@ids){return undef;}
374
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 );
378
379         my $results = [];
380
381         while( my $response = $request->recv() ) {
382
383                 if(!$response) {
384                         throw OpenSRF::EX::ERROR ("No Response from Storage");
385                 }
386                 if($response->isa("Error")) {
387                         throw $response ($response->stringify);
388                 }
389
390                 my $record_entry = $response->content;
391
392                 my $creator = $record_entry->creator;
393                 my $editor      = $record_entry->editor;
394
395                 ($creator, $editor) = _get_userid_by_id($creator, $editor);
396
397                 $record_entry->creator($creator);
398                 $record_entry->editor($editor);
399
400                 push @$results, $record_entry;
401
402         }
403
404         $request->finish;
405         $session->disconnect();
406         $session->finish();
407
408         return $results;
409
410 }
411
412 # gets the username
413 sub _get_userid_by_id {
414
415         my @ids = @_;
416         my @users;
417
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 );
421
422         $request->wait_complete;
423         my $response = $request->recv();
424         if(!$request->complete) { return undef; }
425
426         if($response->isa("Error")){
427                 throw $response ($response);
428         }
429
430         for my $u (@{$response->content}) {
431                 next unless ref($u);
432                 push @users, $u->usrname;
433         }
434
435         $request->finish;
436         $session->disconnect;
437         $session->kill_me();
438
439         return @users;
440 }
441
442 sub _get_id_by_userid {
443
444         my @users = @_;
445         my @ids;
446
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 );
450
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");
455         }
456
457         if(UNIVERSAL::isa( $response, "Error")){
458                 throw $response ($response);
459         }
460
461         for my $u (@{$response->content}) {
462                 next unless ref($u);
463                 push @ids, $u->id();
464         }
465
466         $request->finish;
467         $session->disconnect;
468         $session->kill_me();
469
470         return @ids;
471 }
472
473
474 # commits metadata objects to the db
475 sub _update_record_metadata {
476
477         my ($session, @docs ) = @_;
478
479         for my $doc (@docs) {
480
481                 my $user_obj = $doc->{user};
482                 my $docid = $doc->{docid};
483
484                 warn "Updating metata for doc $docid\n";
485
486                 my $request = $session->request( 
487                         "open-ils.storage.direct.biblio.record_entry.retrieve", $docid );
488                 my $record = $request->gather(1);
489
490                 warn "retrieved record\n";
491                 my ($id) = _get_id_by_userid($user_obj->usrname);
492
493                 warn "got $id from _get_id_by_userid\n";
494                 $record->editor($id);
495                 
496                 warn "Grabbed the record, updating and moving on\n";
497
498                 $request = $session->request( 
499                         "open-ils.storage.direct.biblio.record_entry.update", $record );
500                 $request->gather(1);
501         }
502
503         warn "committing metarecord update\n";
504
505         return 1;
506 }
507
508
509
510 __PACKAGE__->register_method(
511         method  => "orgs_for_title",
512         api_name        => "open-ils.cat.actor.org_unit.retrieve_by_title"
513 );
514
515 sub orgs_for_title {
516         my( $self, $client, $record_id ) = @_;
517
518         my $vols = $apputils->simple_scalar_request(
519                 "open-ils.storage",
520                 "open-ils.storage.direct.asset.call_number.search.record.atomic",
521                 $record_id );
522
523         my $orgs = { map {$_->owning_lib => 1 } @$vols };
524         return [ keys %$orgs ];
525 }
526
527
528
529 __PACKAGE__->register_method(
530         method  => "retrieve_copies",
531         api_name        => "open-ils.cat.asset.copy_tree.retrieve");
532
533 __PACKAGE__->register_method(
534         method  => "retrieve_copies",
535         api_name        => "open-ils.cat.asset.copy_tree.global.retrieve");
536
537 # user_session may be null/undef
538 sub retrieve_copies {
539
540         my( $self, $client, $user_session, $docid, @org_ids ) = @_;
541
542         if(ref($org_ids[0])) { @org_ids = @{$org_ids[0]}; }
543
544         $docid = "$docid";
545
546         warn " $$ retrieving copy tree for orgs @org_ids and doc $docid at " . time() . "\n";
547
548         # grabbing copy trees should be available for everyone..
549         if(!@org_ids and $user_session) {
550                 my $user_obj = 
551                         OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
552                         @org_ids = ($user_obj->home_ou);
553         }
554
555         if( $self->api_name =~ /global/ ) {
556                 warn "performing global copy_tree search for $docid\n";
557                 return _build_volume_list( { record => $docid } );
558
559         } else {
560
561                 my @all_vols;
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 );
567                 }
568                 
569                 warn " $$ Finished copy_tree at " . time() . "\n";
570                 return \@all_vols;
571         }
572
573         return undef;
574 }
575
576
577 sub _build_volume_list {
578         my $search_hash = shift;
579
580         my      $session = OpenSRF::AppSession->create( "open-ils.storage" );
581         
582
583         my $request = $session->request( 
584                         "open-ils.storage.direct.asset.call_number.search.atomic", $search_hash );
585
586         my $vols = $request->gather(1);
587         my @volumes;
588
589         for my $volume (@$vols) {
590
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 );
594
595                 my $copies = $creq->gather(1);
596
597                 $copies = [ sort { $a->barcode cmp $b->barcode } @$copies  ];
598
599                 $volume->copies($copies);
600
601                 push( @volumes, $volume );
602         }
603
604
605         $session->disconnect();
606         return \@volumes;
607
608 }
609
610
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",
618 );
619 sub volume_tree_fleshed_update {
620
621         my( $self, $client, $user_session, $volumes ) = @_;
622         return undef unless $volumes;
623
624         my $user_obj = $apputils->check_user_session($user_session);
625
626
627         my $session = $apputils->start_db_session();
628         warn "Looping on volumes in fleshed volume tree update\n";
629
630         # cycle through the volumes provided and update/create/delete where necessary
631         for my $volume (@$volumes) {
632
633                 warn "updating volume " . $volume->id . "\n";
634
635                 my $update_copy_list = $volume->copies;
636
637
638                 if( $volume->isdeleted) {
639                         my $status = _delete_volume($session, $volume, $user_obj);
640                         if(!$status) {
641                                 throw OpenSRF::EX::ERROR
642                                         ("Volume delete failed for volume " . $volume->id);
643                         }
644                         if(UNIVERSAL::isa($status, "Fieldmapper::perm_ex")) { return $status; }
645
646                 } elsif( $volume->isnew ) {
647
648                         $volume->clear_id;
649                         $volume->editor($user_obj->id);
650                         $volume->creator($user_obj->id);
651                         $volume = _add_volume($session, $volume, $user_obj);
652                         use Data::Dumper;
653                         warn Dumper $volume;
654                         if($volume and UNIVERSAL::isa($volume, "Fieldmapper::perm_ex")) { return $volume; }
655
656                 } elsif( $volume->ischanged ) {
657
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; }
661                 }
662
663
664                 if( ! $volume->isdeleted ) {
665                         for my $copy (@{$update_copy_list}) {
666         
667                                 $copy->editor($user_obj->id);
668                                 warn "updating copy for volume " . $volume->id . "\n";
669         
670                                 if( $copy->isnew ) {
671         
672                                         $copy->clear_id;
673                                         $copy->call_number($volume->id);
674                                         $copy->creator($user_obj->id);
675                                         $copy = _fleshed_copy_update($session,$copy,$user_obj);
676         
677                                 } elsif( $copy->ischanged ) {
678                                         $copy->call_number($volume->id);
679                                         $copy = _fleshed_copy_update($session, $copy, $user_obj);
680         
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";
685                                 }
686                         }
687                 }
688         }
689
690         $apputils->commit_db_session($session);
691         return scalar(@$volumes);
692 }
693
694
695 sub _delete_volume {
696         my( $session, $volume, $user_obj ) = @_;
697
698         if($apputils->check_user_perms(
699                         $user_obj->id, $user_obj->home_ou, "DELETE_VOLUME")) {
700                 return OpenILS::Perm->new("DELETE_VOLUME"); }
701
702         #$volume = _find_volume($session, $volume);
703         warn "Deleting volume " . $volume->id . "\n";
704
705         my $copies = $session->request(
706                 "open-ils.storage.direct.asset.copy.search.call_number.atomic",
707                 $volume->id )->gather(1);
708         if(@$copies) {
709                 throw OpenSRF::EX::ERROR 
710                         ("Cannot remove volume with copies attached");
711         }
712
713         my $req = $session->request(
714                 "open-ils.storage.direct.asset.call_number.delete",
715                 $volume );
716         return $req->gather(1);
717 }
718
719
720 sub _update_volume {
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"); }
725
726         my $req = $session->request(
727                 "open-ils.storage.direct.asset.call_number.update",
728                 $volume );
729         my $status = $req->gather(1);
730 }
731
732 sub _add_volume {
733
734         my($session, $volume, $user_obj) = @_;
735
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"); 
740         }
741
742         my $request = $session->request( 
743                 "open-ils.storage.direct.asset.call_number.create", $volume );
744
745         my $id = $request->gather(1);
746
747         if( $id == 0 ) {
748                 OpenILS::Application::AppUtils->rollback_db_session($session);
749                 throw OpenSRF::EX::ERROR (" * -> Error creating new volume");
750         }
751
752         $volume->id($id);
753         warn "received new volume id: $id\n";
754         return $volume;
755
756 }
757
758
759
760
761 __PACKAGE__->register_method(
762         method  => "fleshed_copy_update",
763         api_name        => "open-ils.cat.asset.copy.fleshed.batch.update",
764 );
765
766 sub fleshed_copy_update {
767         my($self, $client, $user_session, $copies) = @_;
768
769         my $user_obj = $apputils->check_user_session($user_session); 
770         my $session = $apputils->start_db_session();
771
772         for my $copy (@$copies) {
773                 _fleshed_copy_update($session, $copy, $user_obj);
774         }
775
776         $apputils->commit_db_session($session);
777         return 1;
778 }
779
780
781
782 sub _delete_copy {
783         my($session, $copy, $user_obj) = @_;
784
785         if($apputils->check_user_perms(
786                         $user_obj->id, $user_obj->home_ou, "DELETE_COPY")) {
787                 return OpenILS::Perm->new("DELETE_COPY"); }
788
789         warn "Deleting copy " . $copy->id . "\n";
790         my $request = $session->request(
791                 "open-ils.storage.direct.asset.copy.delete",
792                 $copy );
793         return $request->gather(1);
794 }
795
796 sub _create_copy {
797         my($session, $copy, $user_obj) = @_;
798
799         if($apputils->check_user_perms(
800                         $user_obj->id, $user_obj->home_ou, "CREATE_COPY")) {
801                 return OpenILS::Perm->new("CREATE_COPY"); }
802
803         my $request = $session->request(
804                 "open-ils.storage.direct.asset.copy.create",
805                 $copy );
806         my $id = $request->gather(1);
807
808         if($id < 1) {
809                 throw OpenSRF::EX::ERROR
810                         ("Unable to create new copy " . Dumper($copy));
811         }
812         $copy->id($id);
813         warn "Created copy " . $copy->id . "\n";
814
815         return $copy;
816
817 }
818
819 sub _update_copy {
820         my($session, $copy, $user_obj) = @_;
821
822         if($apputils->check_user_perms(
823                         $user_obj->id, $user_obj->home_ou, "UPDATE_COPY")) {
824                 return OpenILS::Perm->new("UPDATE_COPY"); }
825
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";
830         return $status;
831 }
832
833
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) = @_;
840
841         my $stat_cat_entries = $copy->stat_cat_entries;
842         $copy->editor($editor->id);
843         
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 ); }
848
849         warn "Updating copy " . Dumper($copy) . "\n";
850
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);
857         }
858
859         
860         if(!@$stat_cat_entries) { return 1; }
861
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);
865
866         if(!$copy->isnew) { _delete_stale_maps($session, $stat_maps, $copy); }
867         
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 );
871         }
872         
873         return 1;
874 }
875
876
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) = @_;
883
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 );
891                 $req->gather(1);
892                 }
893         }
894
895         return $stat_maps;
896 }
897
898
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 ) = @_;
906
907         warn "Updating stat maps for copy " . $copy->id . "\n";
908
909         # see if this map already exists
910         for my $map (@$stat_maps) {
911                 if( $map->stat_cat_entry == $entry->id ) {return;}
912         }
913
914         warn "Creating new stat map for stat  " . 
915                 $entry->stat_cat . " and copy " . $copy->id . "\n";
916
917         # if not, create it
918         my $new_map = Fieldmapper::asset::stat_cat_entry_copy_map->new();
919
920         $new_map->stat_cat( $entry->stat_cat );
921         $new_map->stat_cat_entry( $entry->id );
922         $new_map->owning_copy( $copy->id );
923
924         warn "New map is " . Dumper($new_map) . "\n";
925
926         my $request = $session->request(
927                 "open-ils.storage.direct.asset.stat_cat_entry_copy_map.create",
928                 $new_map );
929         my $status = $request->gather(1);
930         warn "created new map with id $status\n";
931
932 }
933
934
935
936
937 1;