1 package OpenILS::Application::Acq::BatchManager;
2 use OpenSRF::AppSession;
3 use OpenSRF::EX qw/:try/;
4 use strict; use warnings;
7 my($class, %args) = @_;
8 my $self = bless(\%args, $class);
16 purchase_order => undef,
22 $self->{ingest_queue} = [];
29 $self->{conn} = $val if $val;
34 $self->{throttle} = $val if $val;
35 return $self->{throttle};
38 my($self, %other_args) = @_;
39 if($self->throttle and not %other_args) {
41 ($self->{args}->{progress} - $self->{last_respond_progress}) >= $self->throttle
44 $self->conn->respond({ %{$self->{args}}, %other_args });
45 $self->{last_respond_progress} = $self->{args}->{progress};
47 sub respond_complete {
48 my($self, %other_args) = @_;
50 $self->conn->respond_complete({ %{$self->{args}}, %other_args });
55 $self->{args}->{total} = $val if defined $val;
56 return $self->{args}->{total};
60 $self->{args}->{purchase_order} = $val if $val;
65 $self->{args}->{picklist} = $val if $val;
70 $self->{args}->{lid} += 1;
71 $self->{args}->{progress} += 1;
76 $self->{args}->{li} += 1;
77 $self->{args}->{progress} += 1;
82 $self->{args}->{copies} += 1;
83 $self->{args}->{progress} += 1;
88 $self->{args}->{bibs} += 1;
89 $self->{args}->{progress} += 1;
93 my($self, $amount) = @_;
94 $self->{args}->{debits_accrued} += $amount;
95 $self->{args}->{progress} += 1;
99 my($self, $editor) = @_;
100 $self->{editor} = $editor if defined $editor;
101 return $self->{editor};
105 $self->{args}->{complete} = 1;
110 my($self, $val) = @_;
111 $self->{ingest_ses} = $val if $val;
112 return $self->{ingest_ses};
115 sub push_ingest_queue {
116 my($self, $rec_id) = @_;
118 $self->ingest_ses(OpenSRF::AppSession->connect('open-ils.ingest'))
119 unless $self->ingest_ses;
121 my $req = $self->ingest_ses->request('open-ils.ingest.full.biblio.record', $rec_id);
123 push(@{$self->{ingest_queue}}, $req);
126 sub process_ingest_records {
129 for my $req (@{$self->{ingest_queue}}) {
133 $self->{args}->{indexed} += 1;
134 $self->{args}->{progress} += 1;
139 $self->ingest_ses->disconnect;
144 my($self, $org, $key, $val) = @_;
145 $self->{cache}->{$org} = {} unless $self->{cache}->{org};
146 $self->{cache}->{$org}->{$key} = $val if defined $val;
147 return $self->{cache}->{$org}->{$key};
151 package OpenILS::Application::Acq::Order;
152 use base qw/OpenILS::Application/;
153 use strict; use warnings;
154 # ----------------------------------------------------------------------------
155 # Break up each component of the order process and pieces into managable
156 # actions that can be shared across different workflows
157 # ----------------------------------------------------------------------------
159 use OpenSRF::Utils::Logger qw(:logger);
160 use OpenSRF::Utils::JSON;
161 use OpenILS::Utils::Fieldmapper;
162 use OpenILS::Utils::CStoreEditor q/:funcs/;
163 use OpenILS::Const qw/:const/;
164 use OpenSRF::EX q/:try/;
165 use OpenILS::Application::AppUtils;
166 use OpenILS::Application::Cat::BibCommon;
167 use OpenILS::Application::Cat::AssetCommon;
171 my $U = 'OpenILS::Application::AppUtils';
174 # ----------------------------------------------------------------------------
176 # ----------------------------------------------------------------------------
177 sub create_lineitem {
178 my($mgr, %args) = @_;
179 my $li = Fieldmapper::acq::lineitem->new;
180 $li->creator($mgr->editor->requestor->id);
181 $li->selector($li->creator);
182 $li->editor($li->creator);
183 $li->create_time('now');
184 $li->edit_time('now');
186 $li->$_($args{$_}) for keys %args;
188 return 0 unless update_picklist($mgr, $li->picklist);
191 return $mgr->editor->create_acq_lineitem($li);
194 sub update_lineitem {
196 $li->edit_time('now');
197 $li->editor($mgr->editor->requestor->id);
198 return $li if $mgr->editor->update_acq_lineitem($li);
203 sub delete_lineitem {
205 $li = $mgr->editor->retrieve_acq_lineitem($li) unless ref $li;
208 return 0 unless update_picklist($mgr, $li->picklist);
211 if($li->purchase_order) {
212 return 0 unless update_purchase_order($mgr, $li->purchase_order);
215 # delete the attached lineitem_details
216 my $lid_ids = $mgr->editor->search_acq_lineitem_detail({lineitem => $li->id}, {idlist=>1});
217 for my $lid_id (@$lid_ids) {
218 return 0 unless delete_lineitem_detail($mgr, undef, $lid_id);
221 return $mgr->editor->delete_acq_lineitem($li);
224 # ----------------------------------------------------------------------------
226 # ----------------------------------------------------------------------------
227 sub create_lineitem_detail {
228 my($mgr, %args) = @_;
229 my $lid = Fieldmapper::acq::lineitem_detail->new;
230 $lid->$_($args{$_}) for keys %args;
231 $mgr->editor->create_acq_lineitem_detail($lid) or return 0;
234 # create some default values
235 unless($lid->barcode) {
236 my $pfx = $U->ou_ancestor_setting_value($lid->owning_lib, 'acq.tmp_barcode_prefix') || 'ACQ';
237 $lid->barcode($pfx.$lid->id);
240 unless($lid->cn_label) {
241 my $pfx = $U->ou_ancestor_setting_value($lid->owning_lib, 'acq.tmp_callnumber_prefix') || 'ACQ';
242 $lid->cn_label($pfx.$lid->id);
245 if(!$lid->location and my $loc = $U->ou_ancestor_setting_value($lid->owning_lib, 'acq.default_copy_location')) {
246 $lid->location($loc);
249 if(!$lid->circ_modifier and my $mod = get_default_circ_modifier($mgr, $lid->owning_lib)) {
250 $lid->circ_modifier($mod);
253 $mgr->editor->update_acq_lineitem_detail($lid) or return 0;
254 my $li = $mgr->editor->retrieve_acq_lineitem($lid->lineitem) or return 0;
255 update_lineitem($mgr, $li) or return 0;
259 sub get_default_circ_modifier {
261 my $mod = $mgr->cache($org, 'def_circ_mod');
263 $mod = $U->ou_ancestor_setting_value($org, 'acq.default_circ_modifier');
264 return $mgr->cache($org, 'def_circ_mod', $mod) if $mod;
268 sub delete_lineitem_detail {
270 $lid = $mgr->editor->retrieve_acq_lineitem_detail($lid) unless ref $lid;
271 return $mgr->editor->delete_acq_lineitem_detail($lid);
275 # ----------------------------------------------------------------------------
277 # ----------------------------------------------------------------------------
278 sub set_lineitem_attr {
279 my($mgr, %args) = @_;
280 my $attr_type = $args{attr_type};
282 # first, see if it's already set. May just need to overwrite it
283 my $attr = $mgr->editor->search_acq_lineitem_attr({
284 lineitem => $args{lineitem},
285 attr_type => $args{attr_type},
286 attr_name => $args{attr_name}
290 $attr->attr_value($args{attr_value});
291 return $attr if $mgr->editor->update_acq_lineitem_attr($attr);
296 $attr = Fieldmapper::acq::lineitem_attr->new;
297 $attr->$_($args{$_}) for keys %args;
299 unless($attr->definition) {
300 my $find = "search_acq_$attr_type";
301 my $attr_def_id = $mgr->editor->$find({code => $attr->attr_name}, {idlist=>1})->[0] or return 0;
302 $attr->definition($attr_def_id);
304 return $mgr->editor->create_acq_lineitem_attr($attr);
310 my $attrs = $li->attributes;
311 my ($marc_estimated, $local_estimated, $local_actual, $prov_estimated, $prov_actual);
313 for my $attr (@$attrs) {
314 if($attr->attr_name eq 'estimated_price') {
315 $local_estimated = $attr->attr_value
316 if $attr->attr_type eq 'lineitem_local_attr_definition';
317 $prov_estimated = $attr->attr_value
318 if $attr->attr_type eq 'lineitem_prov_attr_definition';
319 $marc_estimated = $attr->attr_value
320 if $attr->attr_type eq 'lineitem_marc_attr_definition';
322 } elsif($attr->attr_name eq 'actual_price') {
323 $local_actual = $attr->attr_value
324 if $attr->attr_type eq 'lineitem_local_attr_definition';
325 $prov_actual = $attr->attr_value
326 if $attr->attr_type eq 'lineitem_prov_attr_definition';
330 return ($local_actual, 1) if $local_actual;
331 return ($prov_actual, 2) if $prov_actual;
332 return ($local_estimated, 1) if $local_estimated;
333 return ($prov_estimated, 2) if $prov_estimated;
334 return ($marc_estimated, 3);
338 # ----------------------------------------------------------------------------
340 # ----------------------------------------------------------------------------
341 sub create_lineitem_debits {
342 my($mgr, $li, $price, $ptype) = @_;
344 ($price, $ptype) = get_li_price($li) unless $price;
347 $mgr->editor->event(OpenILS::Event->new('ACQ_LINEITEM_NO_PRICE', payload => $li->id));
348 $mgr->editor->rollback;
352 unless($li->provider) {
353 $mgr->editor->event(OpenILS::Event->new('ACQ_LINEITEM_NO_PROVIDER', payload => $li->id));
354 $mgr->editor->rollback;
358 my $lid_ids = $mgr->editor->search_acq_lineitem_detail(
359 {lineitem => $li->id},
363 for my $lid_id (@$lid_ids) {
365 my $lid = $mgr->editor->retrieve_acq_lineitem_detail([
368 flesh_fields => {acqlid => ['fund']}
372 create_lineitem_detail_debit($mgr, $li, $lid, $price, $ptype) or return 0;
381 # ptype 1=local, 2=provider, 3=marc
382 sub create_lineitem_detail_debit {
383 my($mgr, $li, $lid, $price, $ptype) = @_;
385 unless(ref $li and ref $li->provider) {
386 $li = $mgr->editor->retrieve_acq_lineitem([
389 flesh_fields => {jub => ['provider']},
394 unless(ref $lid and ref $lid->fund) {
395 $lid = $mgr->editor->retrieve_acq_lineitem_detail([
398 flesh_fields => {acqlid => ['fund']}
403 my $ctype = $lid->fund->currency_type;
406 if($ptype == 2) { # price from vendor
407 $ctype = $li->provider->currency_type;
408 $amount = currency_conversion($mgr, $ctype, $lid->fund->currency_type, $price);
411 my $debit = create_fund_debit(
413 fund => $lid->fund->id,
414 origin_amount => $price,
415 origin_currency_type => $ctype,
419 $lid->fund_debit($debit->id);
420 $lid->fund($lid->fund->id);
421 $mgr->editor->update_acq_lineitem_detail($lid) or return 0;
426 # ----------------------------------------------------------------------------
428 # ----------------------------------------------------------------------------
429 sub create_fund_debit {
430 my($mgr, %args) = @_;
431 my $debit = Fieldmapper::acq::fund_debit->new;
432 $debit->debit_type('purchase');
433 $debit->encumbrance('t');
434 $debit->$_($args{$_}) for keys %args;
435 $mgr->add_debit($debit->amount);
436 return $mgr->editor->create_acq_fund_debit($debit);
439 sub currency_conversion {
440 my($mgr, $src_currency, $dest_currency, $amount) = @_;
441 my $result = $mgr->editor->json_query(
442 {from => ['acq.exchange_ratio', $src_currency, $dest_currency, $amount]});
443 return $result->[0]->{'acq.exchange_ratio'};
447 # ----------------------------------------------------------------------------
449 # ----------------------------------------------------------------------------
450 sub create_picklist {
451 my($mgr, %args) = @_;
452 my $picklist = Fieldmapper::acq::picklist->new;
453 $picklist->creator($mgr->editor->requestor->id);
454 $picklist->owner($picklist->creator);
455 $picklist->editor($picklist->creator);
456 $picklist->create_time('now');
457 $picklist->edit_time('now');
458 $picklist->org_unit($mgr->editor->requestor->ws_ou);
459 $picklist->owner($mgr->editor->requestor->id);
460 $picklist->$_($args{$_}) for keys %args;
461 $mgr->picklist($picklist);
462 return $mgr->editor->create_acq_picklist($picklist);
465 sub update_picklist {
466 my($mgr, $picklist) = @_;
467 $picklist = $mgr->editor->retrieve_acq_picklist($picklist) unless ref $picklist;
468 $picklist->edit_time('now');
469 $picklist->editor($mgr->editor->requestor->id);
470 $mgr->picklist($picklist);
471 return $picklist if $mgr->editor->update_acq_picklist($picklist);
475 sub delete_picklist {
476 my($mgr, $picklist) = @_;
477 $picklist = $mgr->editor->retrieve_acq_picklist($picklist) unless ref $picklist;
479 # delete all 'new' lineitems
480 my $lis = $mgr->editor->search_acq_lineitem({picklist => $picklist->id, state => 'new'});
482 return 0 unless delete_lineitem($mgr, $li);
485 # detach all non-'new' lineitems
486 $lis = $mgr->editor->search_acq_lineitem({picklist => $picklist->id, state => {'!=' => 'new'}});
489 return 0 unless update_lineitem($li);
492 # remove any picklist-specific object perms
493 my $ops = $mgr->editor->search_permission_usr_object_perm_map({object_type => 'acqpl', object_id => ''.$picklist->id});
495 return 0 unless $mgr->editor->delete_usr_object_perm_map($op);
498 return $mgr->editor->delete_acq_picklist($picklist);
501 # ----------------------------------------------------------------------------
503 # ----------------------------------------------------------------------------
504 sub update_purchase_order {
506 $po = $mgr->editor->retrieve_acq_purchase_order($po) unless ref $po;
507 $po->editor($mgr->editor->requestor->id);
508 $po->edit_time('now');
509 $mgr->purchase_order($po);
510 return $po if $mgr->editor->update_acq_purchase_order($po);
514 sub create_purchase_order {
515 my($mgr, %args) = @_;
516 my $po = Fieldmapper::acq::purchase_order->new;
517 $po->creator($mgr->editor->requestor->id);
518 $po->editor($mgr->editor->requestor->id);
519 $po->owner($mgr->editor->requestor->id);
520 $po->edit_time('now');
521 $po->create_time('now');
522 $po->ordering_agency($mgr->editor->requestor->ws_ou);
523 $po->$_($args{$_}) for keys %args;
524 $mgr->purchase_order($po);
525 return $mgr->editor->create_acq_purchase_order($po);
529 # ----------------------------------------------------------------------------
530 # Bib, Callnumber, and Copy data
531 # ----------------------------------------------------------------------------
533 sub create_lineitem_assets {
534 my($mgr, $li_id) = @_;
537 my $li = $mgr->editor->retrieve_acq_lineitem([
540 flesh_fields => {jub => ['purchase_order', 'attributes']}
544 # -----------------------------------------------------------------
545 # first, create the bib record if necessary
546 # -----------------------------------------------------------------
548 unless($li->eg_bib_id) {
549 create_bib($mgr, $li) or return 0;
553 my $li_details = $mgr->editor->search_acq_lineitem_detail({lineitem => $li_id}, {idlist=>1});
555 # -----------------------------------------------------------------
556 # for each lineitem_detail, create the volume if necessary, create
557 # a copy, and link them all together.
558 # -----------------------------------------------------------------
559 for my $lid_id (@{$li_details}) {
561 my $lid = $mgr->editor->retrieve_acq_lineitem_detail($lid_id) or return 0;
562 next if $lid->eg_copy_id;
564 my $org = $lid->owning_lib;
565 my $label = $lid->cn_label;
566 my $bibid = $li->eg_bib_id;
568 my $volume = $mgr->cache($org, "cn.$bibid.$label");
570 $volume = create_volume($mgr, $li, $lid) or return 0;
571 $mgr->cache($org, "cn.$bibid.$label", $volume);
573 create_copy($mgr, $volume, $lid) or return 0;
576 return { li => $li, new_bib => $new_bib };
582 my $record = OpenILS::Application::Cat::BibCommon->biblio_record_xml_import(
587 1, # override tcn collisions
589 undef # $rec->bib_source
592 if($U->event_code($record)) {
593 $mgr->editor->event($record);
594 $mgr->editor->rollback;
598 $li->eg_bib_id($record->id);
600 return update_lineitem($mgr, $li);
604 my($mgr, $li, $lid) = @_;
607 OpenILS::Application::Cat::AssetCommon->find_or_create_volume(
615 $mgr->editor->event($evt);
623 my($mgr, $volume, $lid) = @_;
624 my $copy = Fieldmapper::asset::copy->new;
626 $copy->loan_duration(2);
627 $copy->fine_level(2);
628 $copy->status(OILS_COPY_STATUS_ON_ORDER);
629 $copy->barcode($lid->barcode);
630 $copy->location($lid->location);
631 $copy->call_number($volume->id);
632 $copy->circ_lib($volume->owning_lib);
633 $copy->circ_modifier($lid->circ_modifier);
635 my $evt = OpenILS::Application::Cat::AssetCommon->create_copy($mgr->editor, $volume, $copy);
637 $mgr->editor->event($evt);
642 $lid->eg_copy_id($copy->id);
643 $mgr->editor->update_acq_lineitem_detail($lid) or return 0;
651 # ----------------------------------------------------------------------------
652 # Workflow: Build a selection list from a Z39.50 search
653 # ----------------------------------------------------------------------------
655 __PACKAGE__->register_method(
657 api_name => 'open-ils.acq.picklist.search.z3950',
660 desc => 'Performs a z3950 federated search and creates a picklist and associated lineitems',
662 {desc => 'Authentication token', type => 'string'},
663 {desc => 'Search definition', type => 'object'},
664 {desc => 'Picklist name, optional', type => 'string'},
670 my($self, $conn, $auth, $search, $name, $options) = @_;
671 my $e = new_editor(authtoken=>$auth);
672 return $e->event unless $e->checkauth;
673 return $e->event unless $e->allowed('CREATE_PICKLIST');
675 $search->{limit} ||= 10;
678 my $ses = OpenSRF::AppSession->create('open-ils.search');
679 my $req = $ses->request('open-ils.search.z3950.search_class', $auth, $search);
684 while(my $resp = $req->recv(timeout=>60)) {
687 my $e = new_editor(requestor=>$e->requestor, xact=>1);
688 $mgr = OpenILS::Application::Acq::BatchManager->new(editor => $e, conn => $conn);
689 $picklist = zsearch_build_pl($mgr, $name);
693 my $result = $resp->content;
694 my $count = $result->{count};
695 $mgr->total( (($count < $search->{limit}) ? $count : $search->{limit})+1 );
697 for my $rec (@{$result->{records}}) {
699 my $li = create_lineitem($mgr,
700 picklist => $picklist->id,
701 source_label => $result->{service},
702 marc => $rec->{marcxml},
703 eg_bib_id => $rec->{bibid}
706 if($$options{respond_li}) {
707 $li->attributes($mgr->editor->search_acq_lineitem_attr({lineitem => $li->id}))
708 if $$options{flesh_attrs};
709 $li->clear_marc if $$options{clear_marc};
710 $mgr->respond(lineitem => $li);
717 $mgr->editor->commit;
718 return $mgr->respond_complete;
721 sub zsearch_build_pl {
722 my($mgr, $name) = @_;
725 my $picklist = $mgr->editor->search_acq_picklist({
726 owner => $mgr->editor->requestor->id,
730 if($name eq '' and $picklist) {
731 return 0 unless delete_picklist($mgr, $picklist);
735 return update_picklist($mgr, $picklist) if $picklist;
736 return create_picklist($mgr, name => $name);
740 # ----------------------------------------------------------------------------
741 # Workflow: Build a selection list / PO by importing a batch of MARC records
742 # ----------------------------------------------------------------------------
744 __PACKAGE__->register_method(
745 method => 'upload_records',
746 api_name => 'open-ils.acq.process_upload_records',
751 my($self, $conn, $auth, $key) = @_;
753 my $e = new_editor(authtoken => $auth, xact => 1);
754 return $e->die_event unless $e->checkauth;
756 my $mgr = OpenILS::Application::Acq::BatchManager->new(
762 my $cache = OpenSRF::Utils::Cache->new;
764 my $data = $cache->get_cache("vandelay_import_spool_$key");
765 my $purpose = $data->{purpose};
766 my $filename = $data->{path};
767 my $provider = $data->{provider};
768 my $picklist = $data->{picklist};
769 my $create_po = $data->{create_po};
770 my $ordering_agency = $data->{ordering_agency};
771 my $create_assets = $data->{create_assets};
775 unless(-r $filename) {
776 $logger->error("unable to read MARC file $filename");
778 return OpenILS::Event->new('FILE_UPLOAD_ERROR', payload => {filename => $filename});
781 $provider = $e->retrieve_acq_provider($provider) or return $e->die_event;
784 $picklist = $e->retrieve_acq_picklist($picklist) or return $e->die_event;
785 if($picklist->owner != $e->requestor->id) {
786 return $e->die_event unless
787 $e->allowed('CREATE_PICKLIST', $picklist->org_unit, $picklist);
792 $po = create_purchase_order($mgr,
793 ordering_agency => $ordering_agency,
794 provider => $provider->id
795 ) or return $mgr->editor->die_event;
798 $logger->info("acq processing MARC file=$filename");
800 my $marctype = 'USMARC'; # ?
801 my $batch = new MARC::Batch ($marctype, $filename);
818 $logger->warn("Proccessing of record $count in set $key failed with error $err. Skipping this record");
825 ($xml = $r->as_xml_record()) =~ s/\n//sog;
826 $xml =~ s/^<\?xml.+\?\s*>//go;
827 $xml =~ s/>\s+</></go;
828 $xml =~ s/\p{Cc}//go;
829 $xml = $U->entityize($xml);
830 $xml =~ s/[\x00-\x1f]//go;
834 $logger->warn("Proccessing XML of record $count in set $key failed with error $err. Skipping this record");
837 next if $err or not $xml;
840 source_label => $provider->code,
841 provider => $provider->id,
845 $args{picklist} = $picklist->id if $picklist;
847 $args{purchase_order} = $po->id;
848 $args{state} = 'on-order';
851 my $li = create_lineitem($mgr, %args) or return $mgr->editor->die_event;
853 $li->provider($provider); # flesh it, we'll need it later
855 import_lineitem_details($mgr, $ordering_agency, $li) or return $mgr->editor->die_event;
858 push(@li_list, $li->id);
864 $cache->delete_cache('vandelay_import_spool_' . $key);
867 # create the bibs/volumes/copies and ingest the records
868 for my $li_id (@li_list) {
870 my $data = create_lineitem_assets($mgr, $li_id) or return $e->die_event;
872 $mgr->push_ingest_queue($data->{li}->eg_bib_id) if $data->{new_bib};
875 $mgr->process_ingest_records;
878 return $mgr->respond_complete;
881 sub import_lineitem_details {
882 my($mgr, $ordering_agency, $li) = @_;
884 my $holdings = $mgr->editor->json_query({from => ['acq.extract_provider_holding_data', $li->id]});
885 return 1 unless @$holdings;
886 my $org_path = $U->get_org_ancestors($ordering_agency);
887 $org_path = [ reverse (@$org_path) ];
892 # create a lineitem detail for each copy in the data
894 my $compiled = extract_lineitem_detail_data($mgr, $org_path, $holdings, $idx);
895 last unless defined $compiled;
896 return 0 unless $compiled;
898 # this takes the price of the last copy and uses it as the lineitem price
899 # need to determine if a given record would include different prices for the same item
900 $price = $$compiled{price};
902 for(1..$$compiled{quantity}) {
903 my $lid = create_lineitem_detail($mgr,
905 owning_lib => $$compiled{owning_lib},
906 cn_label => $$compiled{call_number},
907 fund => $$compiled{fund},
908 circ_modifier => $$compiled{circ_modifier},
909 note => $$compiled{note},
910 location => $$compiled{copy_location}
918 # set the price attr so we'll know the source of the price
921 attr_name => 'estimated_price',
922 attr_type => 'lineitem_local_attr_definition',
923 attr_value => $price,
927 # if we're creating a purchase order, create the debits
928 if($li->purchase_order) {
929 create_lineitem_debits($mgr, $li, $price, 2) or return 0;
936 # return hash on success, 0 on error, undef on no more holdings
937 sub extract_lineitem_detail_data {
938 my($mgr, $org_path, $holdings, $index) = @_;
940 my @data_list = grep { $_->{holding} eq $index } @$holdings;
941 return undef unless @data_list;
943 my %compiled = map { $_->{attr} => $_->{data} } @data_list;
944 my $base_org = $$org_path[0];
948 $logger->error("Item import extraction error: $msg");
949 $logger->error('Holdings Data: ' . OpenSRF::Utils::JSON->perl2JSON(\%compiled));
950 $mgr->editor->rollback;
951 $mgr->editor->event(OpenILS::Event->new('ACQ_IMPORT_ERROR', payload => $msg));
955 $compiled{quantity} ||= 1;
957 # ---------------------------------------------------------------------
959 my $code = $compiled{fund_code};
960 return $killme->('no fund code provided') unless $code;
962 my $fund = $mgr->cache($base_org, "fund.$code");
964 # search up the org tree for the most appropriate fund
965 for my $org (@$org_path) {
966 $fund = $mgr->editor->search_acq_fund(
967 {org => $org, code => $code, year => DateTime->now->year}, {idlist => 1})->[0];
971 return $killme->("no fund with code $code at orgs [@$org_path]") unless $fund;
972 $compiled{fund} = $fund;
973 $mgr->cache($base_org, "fund.$code", $fund);
976 # ---------------------------------------------------------------------
978 my $sn = $compiled{owning_lib};
979 return $killme->('no owning_lib defined') unless $sn;
981 $mgr->cache($base_org, "orgsn.$sn") ||
982 $mgr->editor->search_actor_org_unit({shortname => $sn}, {idlist => 1})->[0];
983 return $killme->("invalid owning_lib defined: $sn") unless $org_id;
984 $compiled{owning_lib} = $org_id;
985 $mgr->cache($$org_path[0], "orgsn.$sn", $org_id);
988 # ---------------------------------------------------------------------
991 $code = $compiled{circ_modifier};
995 $mod = $mgr->cache($base_org, "mod.$code") ||
996 $mgr->editor->retrieve_config_circ_modifier($code);
997 return $killme->("invlalid circ_modifier $code") unless $mod;
998 $mgr->cache($base_org, "mod.$code", $mod);
1002 $mod = get_default_circ_modifier($mgr, $base_org)
1003 or return $killme->('no circ_modifier defined');
1006 $compiled{circ_modifier} = $mod;
1009 # ---------------------------------------------------------------------
1011 my $name = $compiled{copy_location};
1012 return $killme->('no copy_location defined') unless $name;
1013 my $loc = $mgr->cache($base_org, "copy_loc.$name");
1015 for my $org (@$org_path) {
1016 $loc = $mgr->editor->search_asset_copy_location(
1017 {owning_lib => $org, name => $name}, {idlist => 1})->[0];
1021 return $killme->("Invalid copy location $name") unless $loc;
1022 $compiled{copy_location} = $loc;
1023 $mgr->cache($base_org, "copy_loc.$name", $loc);
1030 # ----------------------------------------------------------------------------
1031 # Workflow: Given an existing purchase order, import/create the bibs,
1032 # callnumber and copy objects
1033 # ----------------------------------------------------------------------------
1035 __PACKAGE__->register_method(
1036 method => 'create_po_assets',
1037 api_name => 'open-ils.acq.purchase_order.assets.create',
1039 desc => q/Creates assets for each lineitem in the purchase order/,
1041 {desc => 'Authentication token', type => 'string'},
1042 {desc => 'The purchase order id', type => 'number'},
1044 return => {desc => 'Streams a total versus completed counts object, event on error'}
1048 sub create_po_assets {
1049 my($self, $conn, $auth, $po_id) = @_;
1050 my $e = new_editor(authtoken=>$auth, xact=>1);
1051 return $e->die_event unless $e->checkauth;
1053 my $mgr = OpenILS::Application::Acq::BatchManager->new(
1059 my $po = $e->retrieve_acq_purchase_order($po_id) or return $e->die_event;
1060 return $e->die_event unless $e->allowed('IMPORT_PURCHASE_ORDER_ASSETS', $po->ordering_agency);
1062 my $li_ids = $e->search_acq_lineitem({purchase_order => $po_id}, {idlist => 1});
1064 # it's ugly, but it's fast. Get the total count of lineitem detail objects to process
1065 my $lid_total = $e->json_query({
1066 select => { acqlid => [{aggregate => 1, transform => 'count', column => 'id'}] },
1072 join => {acqpo => {fkey => 'purchase_order', field => 'id'}}
1076 where => {'+acqpo' => {id => $po_id}}
1079 $mgr->total(scalar(@$li_ids) + $lid_total);
1081 for my $li_id (@$li_ids) {
1082 return $e->die_event unless create_lineitem_assets($mgr, $li_id);
1086 return $e->die_event unless update_purchase_order($mgr, $po);
1089 $mgr->process_ingest_records;
1091 return $mgr->respond_complete;