1 package OpenILS::Application::Acq::BatchManager;
2 use strict; use warnings;
5 my($class, %args) = @_;
6 my $self = bless(\%args, $class);
13 purchase_order => undef,
24 $self->{conn} = $val if $val;
29 $self->{throttle} = $val if $val;
30 return $self->{throttle};
33 my($self, %other_args) = @_;
34 if($self->throttle and not %other_args) {
36 ($self->{args}->{progress} - $self->{last_respond_progress}) >= $self->throttle
39 $self->conn->respond({ %{$self->{args}}, %other_args });
40 $self->{last_respond_progress} = $self->{args}->{progress};
42 sub respond_complete {
43 my($self, %other_args) = @_;
45 $self->conn->respond_complete({ %{$self->{args}}, %other_args });
50 $self->{args}->{total} = $val if defined $val;
51 return $self->{args}->{total};
55 $self->{args}->{purchase_order} = $val if $val;
60 $self->{args}->{picklist} = $val if $val;
65 $self->{args}->{lid} += 1;
66 $self->{args}->{progress} += 1;
71 $self->{args}->{li} += 1;
72 $self->{args}->{progress} += 1;
77 $self->{args}->{copies} += 1;
78 $self->{args}->{progress} += 1;
82 my($self, $amount) = @_;
83 $self->{args}->{debits_accrued} += $amount;
84 $self->{args}->{progress} += 1;
88 my($self, $editor) = @_;
89 $self->{editor} = $editor if defined $editor;
90 return $self->{editor};
94 $self->{args}->{complete} = 1;
99 my($self, $org, $key, $val) = @_;
100 $self->{cache}->{$org} = {} unless $self->{cache}->{org};
101 $self->{cache}->{$org}->{$key} = $val if defined $val;
102 return $self->{cache}->{$org}->{$key};
106 package OpenILS::Application::Acq::Order;
107 use base qw/OpenILS::Application/;
108 use strict; use warnings;
109 # ----------------------------------------------------------------------------
110 # Break up each component of the order process and pieces into managable
111 # actions that can be shared across different workflows
112 # ----------------------------------------------------------------------------
114 use OpenSRF::Utils::Logger qw(:logger);
115 use OpenSRF::Utils::JSON;
116 use OpenILS::Utils::Fieldmapper;
117 use OpenILS::Utils::CStoreEditor q/:funcs/;
118 use OpenILS::Const qw/:const/;
119 use OpenSRF::EX q/:try/;
120 use OpenILS::Application::AppUtils;
121 use OpenILS::Application::Cat::BibCommon;
122 use OpenILS::Application::Cat::AssetCommon;
126 my $U = 'OpenILS::Application::AppUtils';
129 # ----------------------------------------------------------------------------
131 # ----------------------------------------------------------------------------
132 sub create_lineitem {
133 my($mgr, %args) = @_;
134 my $li = Fieldmapper::acq::lineitem->new;
135 $li->creator($mgr->editor->requestor->id);
136 $li->selector($li->creator);
137 $li->editor($li->creator);
138 $li->create_time('now');
139 $li->edit_time('now');
141 $li->$_($args{$_}) for keys %args;
143 return 0 unless update_picklist($mgr, $li->picklist);
146 return $mgr->editor->create_acq_lineitem($li);
149 sub update_lineitem {
151 $li->edit_time('now');
152 $li->editor($mgr->editor->requestor->id);
153 return $li if $mgr->editor->update_acq_lineitem($li);
158 sub delete_lineitem {
160 $li = $mgr->editor->retrieve_acq_lineitem($li) unless ref $li;
163 return 0 unless update_picklist($mgr, $li->picklist);
166 if($li->purchase_order) {
167 return 0 unless update_purchase_order($mgr, $li->purchase_order);
170 # delete the attached lineitem_details
171 my $lid_ids = $mgr->editor->search_acq_lineitem_detail({lineitem => $li->id}, {idlist=>1});
172 for my $lid_id (@$lid_ids) {
173 return 0 unless delete_lineitem_detail($mgr, undef, $lid_id);
176 return $mgr->editor->delete_acq_lineitem($li);
179 # ----------------------------------------------------------------------------
181 # ----------------------------------------------------------------------------
182 sub create_lineitem_detail {
183 my($mgr, %args) = @_;
184 my $lid = Fieldmapper::acq::lineitem_detail->new;
185 $lid->$_($args{$_}) for keys %args;
186 $mgr->editor->create_acq_lineitem_detail($lid) or return 0;
189 # create some default values
190 unless($lid->barcode) {
191 my $pfx = $U->ou_ancestor_setting_value($lid->owning_lib, 'acq.tmp_barcode_prefix') || 'ACQ';
192 $lid->barcode($pfx.$lid->id);
195 unless($lid->cn_label) {
196 my $pfx = $U->ou_ancestor_setting_value($lid->owning_lib, 'acq.tmp_callnumber_prefix') || 'ACQ';
197 $lid->cn_label($pfx.$lid->id);
200 if(!$lid->location and my $loc = $U->ou_ancestor_setting_value($lid->owning_lib, 'acq.default_copy_location')) {
201 $lid->location($loc);
204 if(!$lid->circ_modifier and my $mod = get_default_circ_modifier($mgr, $lid->owning_lib)) {
205 $lid->circ_modifier($mod);
208 $mgr->editor->update_acq_lineitem_detail($lid) or return 0;
209 my $li = $mgr->editor->retrieve_acq_lineitem($lid->lineitem) or return 0;
210 update_lineitem($mgr, $li) or return 0;
214 sub get_default_circ_modifier {
216 my $mod = $mgr->cache($org, 'def_circ_mod');
218 $mod = $U->ou_ancestor_setting_value($org, 'acq.default_circ_modifier');
219 return $mgr->cache($org, 'def_circ_mod', $mod) if $mod;
223 sub delete_lineitem_detail {
225 $lid = $mgr->editor->retrieve_acq_lineitem_detail($lid) unless ref $lid;
226 return $mgr->editor->delete_acq_lineitem_detail($lid);
230 # ----------------------------------------------------------------------------
232 # ----------------------------------------------------------------------------
233 sub set_lineitem_attr {
234 my($mgr, %args) = @_;
235 my $attr_type = $args{attr_type};
237 # first, see if it's already set. May just need to overwrite it
238 my $attr = $mgr->editor->search_acq_lineitem_attr({
239 lineitem => $args{lineitem},
240 attr_type => $args{attr_type},
241 attr_name => $args{attr_name}
245 $attr->attr_value($args{attr_value});
246 return $attr if $mgr->editor->update_acq_lineitem_attr($attr);
251 $attr = Fieldmapper::acq::lineitem_attr->new;
252 $attr->$_($args{$_}) for keys %args;
254 unless($attr->definition) {
255 my $find = "search_acq_$attr_type";
256 my $attr_def_id = $mgr->editor->$find({code => $attr->attr_name}, {idlist=>1})->[0] or return 0;
257 $attr->definition($attr_def_id);
259 return $mgr->editor->create_acq_lineitem_attr($attr);
265 my $attrs = $li->attributes;
266 my ($marc_estimated, $local_estimated, $local_actual, $prov_estimated, $prov_actual);
268 for my $attr (@$attrs) {
269 if($attr->attr_name eq 'estimated_price') {
270 $local_estimated = $attr->attr_value
271 if $attr->attr_type eq 'lineitem_local_attr_definition';
272 $prov_estimated = $attr->attr_value
273 if $attr->attr_type eq 'lineitem_prov_attr_definition';
274 $marc_estimated = $attr->attr_value
275 if $attr->attr_type eq 'lineitem_marc_attr_definition';
277 } elsif($attr->attr_name eq 'actual_price') {
278 $local_actual = $attr->attr_value
279 if $attr->attr_type eq 'lineitem_local_attr_definition';
280 $prov_actual = $attr->attr_value
281 if $attr->attr_type eq 'lineitem_prov_attr_definition';
285 return ($local_actual, 1) if $local_actual;
286 return ($prov_actual, 2) if $prov_actual;
287 return ($local_estimated, 1) if $local_estimated;
288 return ($prov_estimated, 2) if $prov_estimated;
289 return ($marc_estimated, 3);
293 # ----------------------------------------------------------------------------
295 # ----------------------------------------------------------------------------
296 sub create_lineitem_debits {
297 my($mgr, $li, $price, $ptype) = @_;
299 ($price, $ptype) = get_li_price($li) unless $price;
302 $mgr->editor->event(OpenILS::Event->new('ACQ_LINEITEM_NO_PRICE', payload => $li->id));
303 $mgr->editor->rollback;
307 unless($li->provider) {
308 $mgr->editor->event(OpenILS::Event->new('ACQ_LINEITEM_NO_PROVIDER', payload => $li->id));
309 $mgr->editor->rollback;
313 my $lid_ids = $mgr->editor->search_acq_lineitem_detail(
314 {lineitem => $li->id},
318 for my $lid_id (@$lid_ids) {
320 my $lid = $mgr->editor->retrieve_acq_lineitem_detail([
323 flesh_fields => {acqlid => ['fund']}
327 create_lineitem_detail_debit($mgr, $li, $lid, $price, $ptype) or return 0;
336 # ptype 1=local, 2=provider, 3=marc
337 sub create_lineitem_detail_debit {
338 my($mgr, $li, $lid, $price, $ptype) = @_;
340 unless(ref $li and ref $li->provider) {
341 $li = $mgr->editor->retrieve_acq_lineitem([
344 flesh_fields => {jub => ['provider']},
349 unless(ref $lid and ref $lid->fund) {
350 $lid = $mgr->editor->retrieve_acq_lineitem_detail([
353 flesh_fields => {acqlid => ['fund']}
358 my $ctype = $lid->fund->currency_type;
361 if($ptype == 2) { # price from vendor
362 $ctype = $li->provider->currency_type;
363 $amount = currency_conversion($mgr, $ctype, $lid->fund->currency_type, $price);
366 my $debit = create_fund_debit(
368 fund => $lid->fund->id,
369 origin_amount => $price,
370 origin_currency_type => $ctype,
374 $lid->fund_debit($debit->id);
375 $lid->fund($lid->fund->id);
376 $mgr->editor->update_acq_lineitem_detail($lid) or return 0;
381 # ----------------------------------------------------------------------------
383 # ----------------------------------------------------------------------------
384 sub create_fund_debit {
385 my($mgr, %args) = @_;
386 my $debit = Fieldmapper::acq::fund_debit->new;
387 $debit->debit_type('purchase');
388 $debit->encumbrance('t');
389 $debit->$_($args{$_}) for keys %args;
390 $mgr->add_debit($debit->amount);
391 return $mgr->editor->create_acq_fund_debit($debit);
394 sub currency_conversion {
395 my($mgr, $src_currency, $dest_currency, $amount) = @_;
396 my $result = $mgr->editor->json_query(
397 {from => ['acq.exchange_ratio', $src_currency, $dest_currency, $amount]});
398 return $result->[0]->{'acq.exchange_ratio'};
402 # ----------------------------------------------------------------------------
404 # ----------------------------------------------------------------------------
405 sub create_picklist {
406 my($mgr, %args) = @_;
407 my $picklist = Fieldmapper::acq::picklist->new;
408 $picklist->creator($mgr->editor->requestor->id);
409 $picklist->owner($picklist->creator);
410 $picklist->editor($picklist->creator);
411 $picklist->create_time('now');
412 $picklist->edit_time('now');
413 $picklist->org_unit($mgr->editor->requestor->ws_ou);
414 $picklist->owner($mgr->editor->requestor->id);
415 $picklist->$_($args{$_}) for keys %args;
416 $mgr->picklist($picklist);
417 return $mgr->editor->create_acq_picklist($picklist);
420 sub update_picklist {
421 my($mgr, $picklist) = @_;
422 $picklist = $mgr->editor->retrieve_acq_picklist($picklist) unless ref $picklist;
423 $picklist->edit_time('now');
424 $picklist->editor($mgr->editor->requestor->id);
425 $mgr->picklist($picklist);
426 return $picklist if $mgr->editor->update_acq_picklist($picklist);
430 sub delete_picklist {
431 my($mgr, $picklist) = @_;
432 $picklist = $mgr->editor->retrieve_acq_picklist($picklist) unless ref $picklist;
434 # delete all 'new' lineitems
435 my $lis = $mgr->editor->search_acq_lineitem({picklist => $picklist->id, state => 'new'});
437 return 0 unless delete_lineitem($mgr, $li);
440 # detach all non-'new' lineitems
441 $lis = $mgr->editor->search_acq_lineitem({picklist => $picklist->id, state => {'!=' => 'new'}});
444 return 0 unless update_lineitem($li);
447 # remove any picklist-specific object perms
448 my $ops = $mgr->editor->search_permission_usr_object_perm_map({object_type => 'acqpl', object_id => ''.$picklist->id});
450 return 0 unless $mgr->editor->delete_usr_object_perm_map($op);
453 return $mgr->editor->delete_acq_picklist($picklist);
456 # ----------------------------------------------------------------------------
458 # ----------------------------------------------------------------------------
459 sub update_purchase_order {
461 $po = $mgr->editor->retrieve_acq_purchase_order($po) unless ref $po;
462 $po->editor($mgr->editor->requestor->id);
463 $po->edit_time('now');
464 $mgr->purchase_order($po);
465 return $po if $mgr->editor->update_acq_purchase_order($po);
469 sub create_purchase_order {
470 my($mgr, %args) = @_;
471 my $po = Fieldmapper::acq::purchase_order->new;
472 $po->creator($mgr->editor->requestor->id);
473 $po->editor($mgr->editor->requestor->id);
474 $po->owner($mgr->editor->requestor->id);
475 $po->edit_time('now');
476 $po->create_time('now');
477 $po->ordering_agency($mgr->editor->requestor->ws_ou);
478 $po->$_($args{$_}) for keys %args;
479 $mgr->purchase_order($po);
480 return $mgr->editor->create_acq_purchase_order($po);
484 # ----------------------------------------------------------------------------
485 # Bib, Callnumber, and Copy data
486 # ----------------------------------------------------------------------------
488 sub create_lineitem_assets {
489 my($mgr, $li_id) = @_;
492 my $li = $mgr->editor->retrieve_acq_lineitem([
495 flesh_fields => {jub => ['purchase_order', 'attributes']}
499 # -----------------------------------------------------------------
500 # first, create the bib record if necessary
501 # -----------------------------------------------------------------
502 unless($li->eg_bib_id) {
503 create_bib($mgr, $li) or return 0;
506 my $li_details = $mgr->editor->search_acq_lineitem_detail({lineitem => $li_id}, {idlist=>1});
508 # -----------------------------------------------------------------
509 # for each lineitem_detail, create the volume if necessary, create
510 # a copy, and link them all together.
511 # -----------------------------------------------------------------
512 for my $lid_id (@{$li_details}) {
514 my $lid = $mgr->editor->retrieve_acq_lineitem_detail($lid_id) or return 0;
515 next if $lid->eg_copy_id;
517 my $org = $lid->owning_lib;
518 my $label = $lid->cn_label;
519 my $bibid = $li->eg_bib_id;
521 my $volume = $mgr->cache($org, "cn.$bibid.$label");
523 $volume = create_volume($mgr, $li, $lid) or return 0;
524 $mgr->cache($org, "cn.$bibid.$label", $volume);
526 create_copy($mgr, $volume, $lid) or return 0;
535 my $record = OpenILS::Application::Cat::BibCommon->biblio_record_xml_import(
536 $mgr->editor, $li->marc, undef, undef, 1); #$rec->bib_source
538 if($U->event_code($record)) {
539 $mgr->editor->event($record);
540 $mgr->editor->rollback;
544 $li->eg_bib_id($record->id);
545 return update_lineitem($mgr, $li);
549 my($mgr, $li, $lid) = @_;
552 OpenILS::Application::Cat::AssetCommon->find_or_create_volume(
560 $mgr->editor->event($evt);
568 my($mgr, $volume, $lid) = @_;
569 my $copy = Fieldmapper::asset::copy->new;
571 $copy->loan_duration(2);
572 $copy->fine_level(2);
573 $copy->status(OILS_COPY_STATUS_ON_ORDER);
574 $copy->barcode($lid->barcode);
575 $copy->location($lid->location);
576 $copy->call_number($volume->id);
577 $copy->circ_lib($volume->owning_lib);
578 $copy->circ_modifier($lid->circ_modifier);
580 my $evt = OpenILS::Application::Cat::AssetCommon->create_copy($mgr->editor, $volume, $copy);
582 $mgr->editor->event($evt);
587 $lid->eg_copy_id($copy->id);
588 $mgr->editor->update_acq_lineitem_detail($lid) or return 0;
596 # ----------------------------------------------------------------------------
597 # Workflow: Build a selection list from a Z39.50 search
598 # ----------------------------------------------------------------------------
600 __PACKAGE__->register_method(
602 api_name => 'open-ils.acq.picklist.search.z3950',
605 desc => 'Performs a z3950 federated search and creates a picklist and associated lineitems',
607 {desc => 'Authentication token', type => 'string'},
608 {desc => 'Search definition', type => 'object'},
609 {desc => 'Picklist name, optional', type => 'string'},
615 my($self, $conn, $auth, $search, $name, $options) = @_;
616 my $e = new_editor(authtoken=>$auth);
617 return $e->event unless $e->checkauth;
618 return $e->event unless $e->allowed('CREATE_PICKLIST');
620 $search->{limit} ||= 10;
623 my $ses = OpenSRF::AppSession->create('open-ils.search');
624 my $req = $ses->request('open-ils.search.z3950.search_class', $auth, $search);
629 while(my $resp = $req->recv(timeout=>60)) {
632 my $e = new_editor(requestor=>$e->requestor, xact=>1);
633 $mgr = OpenILS::Application::Acq::BatchManager->new(editor => $e, conn => $conn);
634 $picklist = zsearch_build_pl($mgr, $name);
638 my $result = $resp->content;
639 my $count = $result->{count};
640 $mgr->total( (($count < $search->{limit}) ? $count : $search->{limit})+1 );
642 for my $rec (@{$result->{records}}) {
644 my $li = create_lineitem($mgr,
645 picklist => $picklist->id,
646 source_label => $result->{service},
647 marc => $rec->{marcxml},
648 eg_bib_id => $rec->{bibid}
651 if($$options{respond_li}) {
652 $li->attributes($mgr->editor->search_acq_lineitem_attr({lineitem => $li->id}))
653 if $$options{flesh_attrs};
654 $li->clear_marc if $$options{clear_marc};
655 $mgr->respond(lineitem => $li);
662 $mgr->editor->commit;
663 return $mgr->respond_complete;
666 sub zsearch_build_pl {
667 my($mgr, $name) = @_;
670 my $picklist = $mgr->editor->search_acq_picklist({
671 owner => $mgr->editor->requestor->id,
675 if($name eq '' and $picklist) {
676 return 0 unless delete_picklist($mgr, $picklist);
680 return update_picklist($mgr, $picklist) if $picklist;
681 return create_picklist($mgr, name => $name);
685 # ----------------------------------------------------------------------------
686 # Workflow: Build a selection list / PO by importing a batch of MARC records
687 # ----------------------------------------------------------------------------
689 __PACKAGE__->register_method(
690 method => 'upload_records',
691 api_name => 'open-ils.acq.process_upload_records',
696 my($self, $conn, $auth, $key) = @_;
698 my $e = new_editor(authtoken => $auth, xact => 1);
699 return $e->die_event unless $e->checkauth;
701 my $mgr = OpenILS::Application::Acq::BatchManager->new(
707 my $cache = OpenSRF::Utils::Cache->new;
709 my $data = $cache->get_cache("vandelay_import_spool_$key");
710 my $purpose = $data->{purpose};
711 my $filename = $data->{path};
712 my $provider = $data->{provider};
713 my $picklist = $data->{picklist};
714 my $create_po = $data->{create_po};
715 my $ordering_agency = $data->{ordering_agency};
716 my $create_assets = $data->{create_assets};
720 unless(-r $filename) {
721 $logger->error("unable to read MARC file $filename");
723 return OpenILS::Event->new('FILE_UPLOAD_ERROR', payload => {filename => $filename});
726 $provider = $e->retrieve_acq_provider($provider) or return $e->die_event;
729 $picklist = $e->retrieve_acq_picklist($picklist) or return $e->die_event;
730 if($picklist->owner != $e->requestor->id) {
731 return $e->die_event unless
732 $e->allowed('CREATE_PICKLIST', $picklist->org_unit, $picklist);
737 $po = create_purchase_order($mgr,
738 ordering_agency => $ordering_agency,
739 provider => $provider->id
740 ) or return $mgr->editor->die_event;
743 $logger->info("acq processing MARC file=$filename");
745 my $marctype = 'USMARC'; # ?
746 my $batch = new MARC::Batch ($marctype, $filename);
762 $logger->warn("Proccessing of record $count in set $key failed with error $err. Skipping this record");
769 ($xml = $r->as_xml_record()) =~ s/\n//sog;
770 $xml =~ s/^<\?xml.+\?\s*>//go;
771 $xml =~ s/>\s+</></go;
772 $xml =~ s/\p{Cc}//go;
773 $xml = $U->entityize($xml);
774 $xml =~ s/[\x00-\x1f]//go;
778 $logger->warn("Proccessing XML of record $count in set $key failed with error $err. Skipping this record");
781 next if $err or not $xml;
784 source_label => $provider->code,
785 provider => $provider->id,
789 $args{picklist} = $picklist->id if $picklist;
791 $args{purchase_order} = $po->id;
792 $args{state} = 'on-order';
795 my $li = create_lineitem($mgr, %args) or return $mgr->editor->die_event;
797 $li->provider($provider); # flesh it, we'll need it later
799 import_lineitem_details($mgr, $ordering_agency, $li) or return $mgr->editor->die_event;
803 create_lineitem_assets($mgr, $li->id) or return $mgr->editor->die_event;
811 $cache->delete_cache('vandelay_import_spool_' . $key);
813 return $mgr->respond_complete;
816 sub import_lineitem_details {
817 my($mgr, $ordering_agency, $li) = @_;
819 my $holdings = $mgr->editor->json_query({from => ['acq.extract_provider_holding_data', $li->id]});
820 return 1 unless @$holdings;
821 my $org_path = $U->get_org_ancestors($ordering_agency);
822 $org_path = [ reverse (@$org_path) ];
827 # create a lineitem detail for each copy in the data
829 my $compiled = extract_lineitem_detail_data($mgr, $org_path, $holdings, $idx);
830 last unless defined $compiled;
831 return 0 unless $compiled;
833 # this takes the price of the last copy and uses it as the lineitem price
834 # need to determine if a given record would include different prices for the same item
835 $price = $$compiled{price};
837 for(1..$$compiled{quantity}) {
838 my $lid = create_lineitem_detail($mgr,
840 owning_lib => $$compiled{owning_lib},
841 cn_label => $$compiled{call_number},
842 fund => $$compiled{fund},
843 circ_modifier => $$compiled{circ_modifier},
844 note => $$compiled{note},
845 location => $$compiled{copy_location}
853 # set the price attr so we'll know the source of the price
856 attr_name => 'estimated_price',
857 attr_type => 'lineitem_local_attr_definition',
858 attr_value => $price,
862 # if we're creating a purchase order, create the debits
863 if($li->purchase_order) {
864 create_lineitem_debits($mgr, $li, $price, 2) or return 0;
871 # return hash on success, 0 on error, undef on no more holdings
872 sub extract_lineitem_detail_data {
873 my($mgr, $org_path, $holdings, $index) = @_;
875 my @data_list = grep { $_->{holding} eq $index } @$holdings;
876 return undef unless @data_list;
878 my %compiled = map { $_->{attr} => $_->{data} } @data_list;
879 my $base_org = $$org_path[0];
883 $logger->error("Item import extraction error: $msg");
884 $logger->error('Holdings Data: ' . OpenSRF::Utils::JSON->perl2JSON(\%compiled));
885 $mgr->editor->rollback;
886 $mgr->editor->event(OpenILS::Event->new('ACQ_IMPORT_ERROR', payload => $msg));
890 $compiled{quantity} ||= 1;
892 # ---------------------------------------------------------------------
894 my $code = $compiled{fund_code};
895 return $killme->('no fund code provided') unless $code;
897 my $fund = $mgr->cache($base_org, "fund.$code");
899 # search up the org tree for the most appropriate fund
900 for my $org (@$org_path) {
901 $fund = $mgr->editor->search_acq_fund(
902 {org => $org, code => $code, year => DateTime->now->year}, {idlist => 1})->[0];
906 return $killme->("no fund with code $code at orgs [@$org_path]") unless $fund;
907 $compiled{fund} = $fund;
908 $mgr->cache($base_org, "fund.$code", $fund);
911 # ---------------------------------------------------------------------
913 my $sn = $compiled{owning_lib};
914 return $killme->('no owning_lib defined') unless $sn;
916 $mgr->cache($base_org, "orgsn.$sn") ||
917 $mgr->editor->search_actor_org_unit({shortname => $sn}, {idlist => 1})->[0];
918 return $killme->("invalid owning_lib defined: $sn") unless $org_id;
919 $compiled{owning_lib} = $org_id;
920 $mgr->cache($$org_path[0], "orgsn.$sn", $org_id);
923 # ---------------------------------------------------------------------
926 $code = $compiled{circ_modifier};
930 $mod = $mgr->cache($base_org, "mod.$code") ||
931 $mgr->editor->retrieve_config_circ_modifier($code);
932 return $killme->("invlalid circ_modifier $code") unless $mod;
933 $mgr->cache($base_org, "mod.$code", $mod);
937 $mod = get_default_circ_modifier($mgr, $base_org)
938 or return $killme->('no circ_modifier defined');
941 $compiled{circ_modifier} = $mod;
944 # ---------------------------------------------------------------------
946 my $name = $compiled{copy_location};
947 return $killme->('no copy_location defined') unless $name;
948 my $loc = $mgr->cache($base_org, "copy_loc.$name");
950 for my $org (@$org_path) {
951 $loc = $mgr->editor->search_asset_copy_location(
952 {owning_lib => $org, name => $name}, {idlist => 1})->[0];
956 return $killme->("Invalid copy location $name") unless $loc;
957 $compiled{copy_location} = $loc;
958 $mgr->cache($base_org, "copy_loc.$name", $loc);
965 # ----------------------------------------------------------------------------
966 # Workflow: Given an existing purchase order, import/create the bibs,
967 # callnumber and copy objects
968 # ----------------------------------------------------------------------------
970 __PACKAGE__->register_method(
971 method => 'create_po_assets',
972 api_name => 'open-ils.acq.purchase_order.assets.create',
974 desc => q/Creates assets for each lineitem in the purchase order/,
976 {desc => 'Authentication token', type => 'string'},
977 {desc => 'The purchase order id', type => 'number'},
979 return => {desc => 'Streams a total versus completed counts object, event on error'}
983 sub create_po_assets {
984 my($self, $conn, $auth, $po_id) = @_;
985 my $e = new_editor(authtoken=>$auth, xact=>1);
986 return $e->die_event unless $e->checkauth;
988 my $mgr = OpenILS::Application::Acq::BatchManager->new(
994 my $po = $e->retrieve_acq_purchase_order($po_id) or return $e->die_event;
995 return $e->die_event unless $e->allowed('IMPORT_PURCHASE_ORDER_ASSETS', $po->ordering_agency);
997 my $li_ids = $e->search_acq_lineitem({purchase_order => $po_id}, {idlist => 1});
999 # it's ugly, but it's fast. Get the total count of lineitem detail objects to process
1000 my $lid_total = $e->json_query({
1001 select => { acqlid => [{aggregate => 1, transform => 'count', column => 'id'}] },
1007 join => {acqpo => {fkey => 'purchase_order', field => 'id'}}
1011 where => {'+acqpo' => {id => $po_id}}
1014 $mgr->total(scalar(@$li_ids) + $lid_total);
1016 for my $li_id (@$li_ids) {
1017 return $e->die_event unless create_lineitem_assets($mgr, $li_id);
1021 return $e->die_event unless update_purchase_order($mgr, $po);
1024 return $mgr->respond_complete;