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