1 package OpenILS::Application::Acq::Financials;
2 use base qw/OpenILS::Application/;
3 use strict; use warnings;
5 use OpenSRF::Utils::Logger qw(:logger);
6 use OpenILS::Utils::Fieldmapper;
7 use OpenILS::Utils::CStoreEditor q/:funcs/;
8 use OpenILS::Const qw/:const/;
9 use OpenSRF::Utils::SettingsClient;
11 use OpenILS::Application::AppUtils;
12 use OpenILS::Application::Acq::Lineitem;
13 my $U = 'OpenILS::Application::AppUtils';
15 # ----------------------------------------------------------------------------
17 # ----------------------------------------------------------------------------
19 __PACKAGE__->register_method(
20 method => 'create_funding_source',
21 api_name => 'open-ils.acq.funding_source.create',
23 desc => 'Creates a new funding_source',
25 {desc => 'Authentication token', type => 'string'},
26 {desc => 'funding source object to create', type => 'object'}
28 return => {desc => 'The ID of the new funding_source'}
32 sub create_funding_source {
33 my($self, $conn, $auth, $funding_source) = @_;
34 my $e = new_editor(xact=>1, authtoken=>$auth);
35 return $e->die_event unless $e->checkauth;
36 return $e->die_event unless $e->allowed('ADMIN_FUNDING_SOURCE', $funding_source->owner);
37 $e->create_acq_funding_source($funding_source) or return $e->die_event;
39 return $funding_source->id;
43 __PACKAGE__->register_method(
44 method => 'delete_funding_source',
45 api_name => 'open-ils.acq.funding_source.delete',
47 desc => 'Deletes a funding_source',
49 {desc => 'Authentication token', type => 'string'},
50 {desc => 'funding source ID', type => 'number'}
52 return => {desc => '1 on success, Event on failure'}
56 sub delete_funding_source {
57 my($self, $conn, $auth, $funding_source_id) = @_;
58 my $e = new_editor(xact=>1, authtoken=>$auth);
59 return $e->die_event unless $e->checkauth;
60 my $funding_source = $e->retrieve_acq_funding_source($funding_source_id) or return $e->die_event;
61 return $e->die_event unless $e->allowed('ADMIN_FUNDING_SOURCE', $funding_source->owner, $funding_source);
62 $e->delete_acq_funding_source($funding_source) or return $e->die_event;
67 __PACKAGE__->register_method(
68 method => 'retrieve_funding_source',
69 api_name => 'open-ils.acq.funding_source.retrieve',
71 desc => 'Retrieves a new funding_source',
73 {desc => 'Authentication token', type => 'string'},
74 {desc => 'funding source ID', type => 'number'}
76 return => {desc => 'The funding_source object on success, Event on failure'}
80 sub retrieve_funding_source {
81 my($self, $conn, $auth, $funding_source_id, $options) = @_;
82 my $e = new_editor(authtoken=>$auth);
83 return $e->event unless $e->checkauth;
86 my $flesh = {flesh => 1, flesh_fields => {acqfs => []}};
87 push(@{$flesh->{flesh_fields}->{acqfs}}, 'credits') if $$options{flesh_credits};
88 push(@{$flesh->{flesh_fields}->{acqfs}}, 'allocations') if $$options{flesh_allocations};
90 my $funding_source = $e->retrieve_acq_funding_source([$funding_source_id, $flesh]) or return $e->event;
92 return $e->event unless $e->allowed(
93 ['ADMIN_FUNDING_SOURCE','MANAGE_FUNDING_SOURCE', 'VIEW_FUNDING_SOURCE'],
94 $funding_source->owner, $funding_source);
96 $funding_source->summary(retrieve_funding_source_summary_impl($e, $funding_source))
97 if $$options{flesh_summary};
98 return $funding_source;
101 __PACKAGE__->register_method(
102 method => 'retrieve_org_funding_sources',
103 api_name => 'open-ils.acq.funding_source.org.retrieve',
106 desc => 'Retrieves all the funding_sources associated with an org unit that the requestor has access to see',
108 {desc => 'Authentication token', type => 'string'},
109 {desc => 'List of org Unit IDs. If no IDs are provided, this method returns the
110 full set of funding sources this user has permission to view', type => 'number'},
111 {desc => q/Limiting permission. this permission is used find the work-org tree from which
112 the list of orgs is generated if no org ids are provided.
113 The default is ADMIN_FUNDING_SOURCE/, type => 'string'},
115 return => {desc => 'The funding_source objects on success, empty array otherwise'}
119 sub retrieve_org_funding_sources {
120 my($self, $conn, $auth, $org_id_list, $options) = @_;
121 my $e = new_editor(authtoken=>$auth);
122 return $e->event unless $e->checkauth;
125 my $limit_perm = ($$options{limit_perm}) ? $$options{limit_perm} : 'ADMIN_FUNDING_SOURCE';
126 return OpenILS::Event->new('BAD_PARAMS')
127 unless $limit_perm =~ /(ADMIN|MANAGE|VIEW)_FUNDING_SOURCE/;
129 my $org_ids = ($org_id_list and @$org_id_list) ? $org_id_list :
130 $U->user_has_work_perm_at($e, $limit_perm, {descendants =>1});
132 return [] unless @$org_ids;
133 my $sources = $e->search_acq_funding_source({owner => $org_ids});
135 for my $source (@$sources) {
136 $source->summary(retrieve_funding_source_summary_impl($e, $source))
137 if $$options{flesh_summary};
138 $conn->respond($source);
144 sub retrieve_funding_source_summary_impl {
145 my($e, $source) = @_;
146 my $at = $e->search_acq_funding_source_allocation_total({funding_source => $source->id})->[0];
147 my $b = $e->search_acq_funding_source_balance({funding_source => $source->id})->[0];
148 my $ct = $e->search_acq_funding_source_credit_total({funding_source => $source->id})->[0];
150 allocation_total => ($at) ? $at->amount : 0,
151 balance => ($b) ? $b->amount : 0,
152 credit_total => ($ct) ? $ct->amount : 0,
157 __PACKAGE__->register_method(
158 method => 'create_funding_source_credit',
159 api_name => 'open-ils.acq.funding_source_credit.create',
161 desc => 'Create a new funding source credit',
163 {desc => 'Authentication token', type => 'string'},
164 {desc => 'funding source credit object', type => 'object'}
166 return => {desc => 'The ID of the new funding source credit on success, Event on failure'}
170 sub create_funding_source_credit {
171 my($self, $conn, $auth, $fs_credit) = @_;
172 my $e = new_editor(authtoken=>$auth, xact=>1);
173 return $e->event unless $e->checkauth;
175 my $fs = $e->retrieve_acq_funding_source($fs_credit->funding_source)
176 or return $e->die_event;
177 return $e->die_event unless $e->allowed(['MANAGE_FUNDING_SOURCE'], $fs->owner, $fs);
179 $e->create_acq_funding_source_credit($fs_credit) or return $e->die_event;
181 return $fs_credit->id;
185 # ---------------------------------------------------------------
187 # ---------------------------------------------------------------
189 __PACKAGE__->register_method(
190 method => 'create_fund',
191 api_name => 'open-ils.acq.fund.create',
193 desc => 'Creates a new fund',
195 {desc => 'Authentication token', type => 'string'},
196 {desc => 'fund object to create', type => 'object'}
198 return => {desc => 'The ID of the newly created fund object'}
203 my($self, $conn, $auth, $fund) = @_;
204 my $e = new_editor(xact=>1, authtoken=>$auth);
205 return $e->die_event unless $e->checkauth;
206 return $e->die_event unless $e->allowed('ADMIN_FUND', $fund->org);
207 $e->create_acq_fund($fund) or return $e->die_event;
213 __PACKAGE__->register_method(
214 method => 'delete_fund',
215 api_name => 'open-ils.acq.fund.delete',
217 desc => 'Deletes a fund',
219 {desc => 'Authentication token', type => 'string'},
220 {desc => 'fund ID', type => 'number'}
222 return => {desc => '1 on success, Event on failure'}
227 my($self, $conn, $auth, $fund_id) = @_;
228 my $e = new_editor(xact=>1, authtoken=>$auth);
229 return $e->die_event unless $e->checkauth;
230 my $fund = $e->retrieve_acq_fund($fund_id) or return $e->die_event;
231 return $e->die_event unless $e->allowed('ADMIN_FUND', $fund->org, $fund);
232 $e->delete_acq_fund($fund) or return $e->die_event;
237 __PACKAGE__->register_method(
238 method => 'retrieve_fund',
239 api_name => 'open-ils.acq.fund.retrieve',
241 desc => 'Retrieves a new fund',
243 {desc => 'Authentication token', type => 'string'},
244 {desc => 'fund ID', type => 'number'}
246 return => {desc => 'The fund object on success, Event on failure'}
251 my($self, $conn, $auth, $fund_id, $options) = @_;
252 my $e = new_editor(authtoken=>$auth);
253 return $e->event unless $e->checkauth;
256 my $flesh = {flesh => 2, flesh_fields => {acqf => []}};
257 push(@{$flesh->{flesh_fields}->{acqf}}, 'debits') if $$options{flesh_debits};
258 push(@{$flesh->{flesh_fields}->{acqf}}, 'allocations') if $$options{flesh_allocations};
259 push(@{$flesh->{flesh_fields}->{acqfa}}, 'funding_source') if $$options{flesh_allocation_sources};
261 my $fund = $e->retrieve_acq_fund([$fund_id, $flesh]) or return $e->event;
262 return $e->event unless $e->allowed(['ADMIN_FUND','MANAGE_FUND', 'VIEW_FUND'], $fund->org, $fund);
263 $fund->summary(retrieve_fund_summary_impl($e, $fund))
264 if $$options{flesh_summary};
268 __PACKAGE__->register_method(
269 method => 'retrieve_org_funds',
270 api_name => 'open-ils.acq.fund.org.retrieve',
273 desc => 'Retrieves all the funds associated with an org unit',
275 {desc => 'Authentication token', type => 'string'},
276 {desc => 'List of org Unit IDs. If no IDs are provided, this method returns the
277 full set of funding sources this user has permission to view', type => 'number'},
278 {desc => q/Options hash.
279 "limit_perm" -- this permission is used find the work-org tree from which
280 the list of orgs is generated if no org ids are provided. The default is ADMIN_FUND.
281 "flesh_summary" -- if true, the summary field on each fund is fleshed
282 The default is ADMIN_FUND/, type => 'string'},
284 return => {desc => 'The fund objects on success, Event on failure'}
288 sub retrieve_org_funds {
289 my($self, $conn, $auth, $org_id_list, $options) = @_;
290 my $e = new_editor(authtoken=>$auth);
291 return $e->event unless $e->checkauth;
294 my $limit_perm = ($$options{limit_perm}) ? $$options{limit_perm} : 'ADMIN_FUND';
295 return OpenILS::Event->new('BAD_PARAMS')
296 unless $limit_perm =~ /(ADMIN|MANAGE|VIEW)_FUND/;
298 my $org_ids = ($org_id_list and @$org_id_list) ? $org_id_list :
299 $U->user_has_work_perm_at($e, $limit_perm, {descendants =>1});
300 return undef unless @$org_ids;
301 my $funds = $e->search_acq_fund({org => $org_ids});
303 for my $fund (@$funds) {
304 $fund->summary(retrieve_fund_summary_impl($e, $fund))
305 if $$options{flesh_summary};
306 $conn->respond($fund);
312 __PACKAGE__->register_method(
313 method => 'retrieve_fund_summary',
314 api_name => 'open-ils.acq.fund.summary.retrieve',
316 desc => 'Returns a summary of credits/debits/encumbrances for a fund',
318 {desc => 'Authentication token', type => 'string'},
319 {desc => 'fund id', type => 'number' }
321 return => {desc => 'A hash of summary information, Event on failure'}
325 sub retrieve_fund_summary {
326 my($self, $conn, $auth, $fund_id) = @_;
327 my $e = new_editor(authtoken=>$auth);
328 return $e->event unless $e->checkauth;
329 my $fund = $e->retrieve_acq_fund($fund_id) or return $e->event;
330 return $e->event unless $e->allowed('MANAGE_FUND', $fund->org, $fund);
331 return retrieve_fund_summary_impl($e, $fund);
335 sub retrieve_fund_summary_impl {
338 my $at = $e->search_acq_fund_allocation_total({fund => $fund->id})->[0];
339 my $dt = $e->search_acq_fund_debit_total({fund => $fund->id})->[0];
340 my $et = $e->search_acq_fund_encumbrance_total({fund => $fund->id})->[0];
341 my $st = $e->search_acq_fund_spent_total({fund => $fund->id})->[0];
342 my $cb = $e->search_acq_fund_combined_balance({fund => $fund->id})->[0];
343 my $sb = $e->search_acq_fund_spent_balance({fund => $fund->id})->[0];
346 allocation_total => ($at) ? $at->amount : 0,
347 debit_total => ($dt) ? $dt->amount : 0,
348 encumbrance_total => ($et) ? $et->amount : 0,
349 spent_total => ($st) ? $st->amount : 0,
350 combined_balance => ($cb) ? $cb->amount : 0,
351 spent_balance => ($sb) ? $sb->amount : 0,
356 # ---------------------------------------------------------------
358 # ---------------------------------------------------------------
360 __PACKAGE__->register_method(
361 method => 'create_fund_alloc',
362 api_name => 'open-ils.acq.fund_allocation.create',
364 desc => 'Creates a new fund_allocation',
366 {desc => 'Authentication token', type => 'string'},
367 {desc => 'fund allocation object to create', type => 'object'}
369 return => {desc => 'The ID of the new fund_allocation'}
373 sub create_fund_alloc {
374 my($self, $conn, $auth, $fund_alloc) = @_;
375 my $e = new_editor(xact=>1, authtoken=>$auth);
376 return $e->die_event unless $e->checkauth;
378 # this action is equivalent to both debiting a funding source and crediting a fund
380 my $source = $e->retrieve_acq_funding_source($fund_alloc->funding_source)
381 or return $e->die_event;
382 return $e->die_event unless $e->allowed('MANAGE_FUNDING_SOURCE', $source->owner);
384 my $fund = $e->retrieve_acq_fund($fund_alloc->fund) or return $e->die_event;
385 return $e->die_event unless $e->allowed('MANAGE_FUND', $fund->org, $fund);
387 $fund_alloc->allocator($e->requestor->id);
388 $e->create_acq_fund_allocation($fund_alloc) or return $e->die_event;
390 return $fund_alloc->id;
394 __PACKAGE__->register_method(
395 method => 'delete_fund_alloc',
396 api_name => 'open-ils.acq.fund_allocation.delete',
398 desc => 'Deletes a fund_allocation',
400 {desc => 'Authentication token', type => 'string'},
401 {desc => 'fund Alocation ID', type => 'number'}
403 return => {desc => '1 on success, Event on failure'}
407 sub delete_fund_alloc {
408 my($self, $conn, $auth, $fund_alloc_id) = @_;
409 my $e = new_editor(xact=>1, authtoken=>$auth);
410 return $e->die_event unless $e->checkauth;
412 my $fund_alloc = $e->retrieve_acq_fund_allocation($fund_alloc_id) or return $e->die_event;
414 my $source = $e->retrieve_acq_funding_source($fund_alloc->funding_source)
415 or return $e->die_event;
416 return $e->die_event unless $e->allowed('MANAGE_FUNDING_SOURCE', $source->owner, $source);
418 my $fund = $e->retrieve_acq_fund($fund_alloc->fund) or return $e->die_event;
419 return $e->die_event unless $e->allowed('MANAGE_FUND', $fund->org, $fund);
421 $e->delete_acq_fund_allocation($fund_alloc) or return $e->die_event;
426 __PACKAGE__->register_method(
427 method => 'retrieve_fund_alloc',
428 api_name => 'open-ils.acq.fund_allocation.retrieve',
430 desc => 'Retrieves a new fund_allocation',
432 {desc => 'Authentication token', type => 'string'},
433 {desc => 'fund Allocation ID', type => 'number'}
435 return => {desc => 'The fund allocation object on success, Event on failure'}
439 sub retrieve_fund_alloc {
440 my($self, $conn, $auth, $fund_alloc_id) = @_;
441 my $e = new_editor(authtoken=>$auth);
442 return $e->event unless $e->checkauth;
443 my $fund_alloc = $e->retrieve_acq_fund_allocation($fund_alloc_id) or return $e->event;
445 my $source = $e->retrieve_acq_funding_source($fund_alloc->funding_source)
446 or return $e->die_event;
447 return $e->die_event unless $e->allowed('MANAGE_FUNDING_SOURCE', $source->owner, $source);
449 my $fund = $e->retrieve_acq_fund($fund_alloc->fund) or return $e->die_event;
450 return $e->die_event unless $e->allowed('MANAGE_FUND', $fund->org, $fund);
456 __PACKAGE__->register_method(
457 method => 'retrieve_funding_source_allocations',
458 api_name => 'open-ils.acq.funding_source.allocations.retrieve',
460 desc => 'Retrieves a new fund_allocation',
462 {desc => 'Authentication token', type => 'string'},
463 {desc => 'fund Allocation ID', type => 'number'}
465 return => {desc => 'The fund allocation object on success, Event on failure'}
469 sub retrieve_funding_source_allocations {
470 my($self, $conn, $auth, $fund_alloc_id) = @_;
471 my $e = new_editor(authtoken=>$auth);
472 return $e->event unless $e->checkauth;
473 my $fund_alloc = $e->retrieve_acq_fund_allocation($fund_alloc_id) or return $e->event;
475 my $source = $e->retrieve_acq_funding_source($fund_alloc->funding_source)
476 or return $e->die_event;
477 return $e->die_event unless $e->allowed('MANAGE_FUNDING_SOURCE', $source->owner, $source);
479 my $fund = $e->retrieve_acq_fund($fund_alloc->fund) or return $e->die_event;
480 return $e->die_event unless $e->allowed('MANAGE_FUND', $fund->org, $fund);
485 # ----------------------------------------------------------------------------
487 # ----------------------------------------------------------------------------
489 __PACKAGE__->register_method(
490 method => 'retrieve_all_currency_type',
491 api_name => 'open-ils.acq.currency_type.all.retrieve',
494 desc => 'Retrieves all currency_type objects',
496 {desc => 'Authentication token', type => 'string'},
498 return => {desc => 'List of currency_type objects', type => 'list'}
502 sub retrieve_all_currency_type {
503 my($self, $conn, $auth, $fund_alloc_id) = @_;
504 my $e = new_editor(authtoken=>$auth);
505 return $e->event unless $e->checkauth;
506 return $e->event unless $e->allowed('GENERAL_ACQ');
507 $conn->respond($_) for @{$e->retrieve_all_acq_currency_type()};
510 sub currency_conversion_impl {
511 my($src_currency, $dest_currency, $amount) = @_;
512 my $result = new_editor()->json_query({
515 params => [$dest_currency, $amount],
516 transform => 'acq.exchange_ratio',
521 where => {code => $src_currency},
525 return $result->[0]->{value};
529 # ----------------------------------------------------------------------------
531 # ----------------------------------------------------------------------------
533 __PACKAGE__->register_method(
534 method => 'create_purchase_order',
535 api_name => 'open-ils.acq.purchase_order.create',
537 desc => 'Creates a new purchase order',
539 {desc => 'Authentication token', type => 'string'},
540 {desc => 'purchase_order to create', type => 'object'}
542 return => {desc => 'The purchase order id, Event on failure'}
546 sub create_purchase_order {
547 my($self, $conn, $auth, $po, $args) = @_;
550 my $e = new_editor(xact=>1, authtoken=>$auth);
551 return $e->die_event unless $e->checkauth;
552 return $e->die_event unless $e->allowed('CREATE_PURCHASE_ORDER', $po->ordering_agency);
555 $po->ordering_agency($e->requestor->ws_ou);
556 my $evt = create_purchase_order_impl($e, $po);
560 my $total_debits = 0;
561 my $total_copies = 0;
566 progress => ++$progress,
567 total_debits => $total_debits,
568 total_copies => $total_copies,
572 if($$args{lineitems}) {
574 for my $li_id (@{$$args{lineitems}}) {
576 my $li = $e->retrieve_acq_lineitem([
578 {flesh => 1, flesh_fields => {jub => ['attributes']}}
579 ]) or return $e->die_event;
581 # point the lineitems at the new PO
582 $li->purchase_order($po->id);
583 $li->editor($e->requestor->id);
584 $li->edit_time('now');
585 $e->update_acq_lineitem($li) or return $e->die_event;
587 # create the bibs/volumes/copies in the Evergreen database
588 if($$args{create_assets}) {
589 # args = {circ_modifier => code}
590 my ($count, $evt) = create_lineitem_assets_impl($e, $li_id, $args);
592 $total_copies+= $count;
593 $respond->(action => 'create_assets');
597 if($$args{create_debits}) {
598 # args = {encumberance => true}
599 my ($total, $evt) = create_li_debit_impl($e, $li, $args);
601 $total_debits += $total;
602 $respond->(action => 'create_debit');
608 $respond->(complete => 1, purchase_order => $po->id);
613 __PACKAGE__->register_method(
614 method => 'create_po_assets',
615 api_name => 'open-ils.acq.purchase_order.assets.create',
617 desc => q/Creates assets for each lineitem in the purchase order/,
619 {desc => 'Authentication token', type => 'string'},
620 {desc => 'The purchase order id', type => 'number'},
621 {desc => q/Options hash./}
623 return => {desc => 'Streams a total versus completed counts object, event on error'}
627 sub create_po_assets {
628 my($self, $conn, $auth, $po_id, $options) = @_;
629 my $e = new_editor(authtoken=>$auth, xact=>1);
630 return $e->die_event unless $e->checkauth;
632 my $po = $e->retrieve_acq_purchase_order($po_id) or return $e->event;
633 return $e->die_event unless
634 $e->allowed('CREATE_PURCHASE_ORDER', $po->ordering_agency);
636 my $li_ids = $e->search_acq_lineitem({purchase_order=>$po_id},{idlist=>1});
637 my $total = @$li_ids;
640 for my $li_id (@$li_ids) {
641 my ($num, $evt) = create_lineitem_assets_impl($e, $li_id);
643 $conn->respond({total=>$count, progress=>++$count});
646 $po->edit_time('now');
647 $e->update_acq_purchase_order($po) or return $e->die_event;
650 return {complete=>1};
653 __PACKAGE__->register_method(
654 method => 'create_lineitem_assets',
655 api_name => 'open-ils.acq.lineitem.assets.create',
657 desc => q/Creates the bibliographic data, volume, and copies associated with a lineitem./,
659 {desc => 'Authentication token', type => 'string'},
660 {desc => 'The lineitem id', type => 'number'},
661 {desc => q/Options hash./}
663 return => {desc => 'ID of newly created bib record, Event on error'}
667 sub create_lineitem_assets {
668 my($self, $conn, $auth, $li_id, $options) = @_;
669 my $e = new_editor(authtoken=>$auth, xact=>1);
670 return $e->die_event unless $e->checkauth;
671 my ($count, $resp) = create_lineitem_assets_impl($e, $li_id, $options);
672 return $resp if $resp;
677 sub create_lineitem_assets_impl {
678 my($e, $li_id, $options) = @_;
682 my $li = $e->retrieve_acq_lineitem([
685 flesh_fields => {jub => ['purchase_order', 'attributes']}
687 ]) or return (undef, $e->die_event);
689 # -----------------------------------------------------------------
690 # first, create the bib record if necessary
691 # -----------------------------------------------------------------
692 unless($li->eg_bib_id) {
694 my $record = OpenILS::Application::Cat::BibCommon->biblio_record_xml_import(
695 $e, $li->marc, undef, undef, undef, 1); #$rec->bib_source
697 if($U->event_code($record)) {
699 return (undef, $record);
702 $li->editor($e->requestor->id);
703 $li->edit_time('now');
704 $li->eg_bib_id($record->id);
705 $e->update_acq_lineitem($li) or return (undef, $e->die_event);
708 my $li_details = $e->search_acq_lineitem_detail({lineitem => $li_id}, {idlist=>1});
710 # -----------------------------------------------------------------
711 # for each lineitem_detail, create the volume if necessary, create
712 # a copy, and link them all together.
713 # -----------------------------------------------------------------
715 for my $li_detail_id (@{$li_details}) {
717 my $li_detail = $e->retrieve_acq_lineitem_detail($li_detail_id)
718 or return (undef, $e->die_event);
720 # Create the volume object if necessary
721 my $volume = $volcache{$li_detail->cn_label};
722 unless($volume and $volume->owning_lib == $li_detail->owning_lib) {
724 OpenILS::Application::Cat::AssetCommon->find_or_create_volume(
725 $e, $li_detail->cn_label, $li->eg_bib_id, $li_detail->owning_lib);
726 return (undef, $evt) if $evt;
727 $volcache{$volume->id} = $volume;
730 my $copy = Fieldmapper::asset::copy->new;
732 $copy->loan_duration(2);
733 $copy->fine_level(2);
734 $copy->status(OILS_COPY_STATUS_ON_ORDER);
735 $copy->barcode($li_detail->barcode);
736 $copy->location($li_detail->location);
737 $copy->call_number($volume->id);
738 $copy->circ_lib($volume->owning_lib);
739 $copy->circ_modifier($$options{circ_modifier} || 'book');
741 $evt = OpenILS::Application::Cat::AssetCommon->create_copy($e, $volume, $copy);
742 return (undef, $evt) if $evt;
744 $li_detail->eg_copy_id($copy->id);
745 $e->update_acq_lineitem_detail($li_detail) or return (undef, $e->die_event);
748 return (scalar @{$li_details});
754 sub create_purchase_order_impl {
755 my($e, $p_order) = @_;
757 $p_order->creator($e->requestor->id);
758 $p_order->editor($e->requestor->id);
759 $p_order->owner($e->requestor->id);
760 $p_order->edit_time('now');
762 return $e->die_event unless
763 $e->allowed('CREATE_PURCHASE_ORDER', $p_order->ordering_agency);
765 my $provider = $e->retrieve_acq_provider($p_order->provider)
766 or return $e->die_event;
767 return $e->die_event unless
768 $e->allowed('MANAGE_PROVIDER', $provider->owner, $provider);
770 $e->create_acq_purchase_order($p_order) or return $e->die_event;
775 # returns (price, type), where type=1 is local, type=2 is provider, type=3 is marc
778 my $attrs = $li->attributes;
779 my ($marc_estimated, $local_estimated, $local_actual, $prov_estimated, $prov_actual);
781 for my $attr (@$attrs) {
782 if($attr->attr_name eq 'estimated_price') {
783 $local_estimated = $attr->attr_value
784 if $attr->attr_type eq 'lineitem_local_attr_definition';
785 $prov_estimated = $attr->attr_value
786 if $attr->attr_type eq 'lineitem_prov_attr_definition';
787 $marc_estimated = $attr->attr_value
788 if $attr->attr_type eq 'lineitem_marc_attr_definition';
790 } elsif($attr->attr_name eq 'actual_price') {
791 $local_actual = $attr->attr_value
792 if $attr->attr_type eq 'lineitem_local_attr_definition';
793 $prov_actual = $attr->attr_value
794 if $attr->attr_type eq 'lineitem_prov_attr_definition';
798 return ($local_actual, 1) if $local_actual;
799 return ($prov_actual, 2) if $prov_actual;
800 return ($local_estimated, 1) if $local_estimated;
801 return ($prov_estimated, 2) if $prov_estimated;
802 return ($marc_estimated, 3);
806 __PACKAGE__->register_method(
807 method => 'create_purchase_order_debits',
808 api_name => 'open-ils.acq.purchase_order.debits.create',
810 desc => 'Creates debits associated with a PO',
812 {desc => 'Authentication token', type => 'string'},
813 {desc => 'purchase_order whose debits to create', type => 'number'},
814 {desc => 'arguments hash. Options include: encumbrance=bool', type => 'object'},
816 return => {desc => 'The total amount of all created debits, Event on error'}
820 sub create_purchase_order_debits {
821 my($self, $conn, $auth, $po_id, $args) = @_;
822 my $e = new_editor(xact=>1, authtoken=>$auth);
823 return $e->die_event unless $e->checkauth;
825 my $po = $e->retrieve_acq_purchase_order($po_id) or return $e->die_event;
827 my $li_ids = $e->search_acq_lineitem(
828 {purchase_order => $po_id},
832 for my $li_id (@$li_ids) {
833 my $li = $e->retrieve_acq_lineitem([
836 flesh_fields => {jub => ['attributes']},
840 my ($total, $evt) = create_li_debit_impl($e, $li);
847 sub create_li_debit_impl {
848 my($e, $li, $args) = @_;
851 my ($price, $ptype) = get_li_price($li);
855 return (undef, OpenILS::Event->new('ACQ_LINEITEM_NO_PRICE', payload => $li->id));
858 unless($li->provider) {
860 return (undef, OpenILS::Event->new('ACQ_LINEITEM_NO_PROVIDER', payload => $li->id));
863 my $lid_ids = $e->search_acq_lineitem_detail(
864 {lineitem => $li->id},
869 for my $lid_id (@$lid_ids) {
871 my $lid = $e->retrieve_acq_lineitem_detail([
874 flesh_fields => {acqlid => ['fund']}
878 my $debit = Fieldmapper::acq::fund_debit->new;
879 $debit->fund($lid->fund->id);
880 $debit->origin_amount($price);
882 if($ptype == 2) { # price from vendor
883 $debit->origin_currency_type($li->provider->currency_type);
884 $debit->amount(currency_conversion_impl(
885 $li->provider->currency_type, $lid->fund->currency_type, $price));
887 $debit->origin_currency_type($lid->fund->currency_type);
888 $debit->amount($price);
891 $debit->encumbrance($args->{encumbrance});
892 $debit->debit_type('purchase');
893 $e->create_acq_fund_debit($debit) or return (undef, $e->die_event);
895 # point the lineitem detail at the fund debit object
896 $lid->fund_debit($debit->id);
897 $lid->fund($lid->fund->id);
898 $e->update_acq_lineitem_detail($lid) or return (undef, $e->die_event);
899 $total += $debit->amount;
906 __PACKAGE__->register_method(
907 method => 'retrieve_all_user_purchase_order',
908 api_name => 'open-ils.acq.purchase_order.user.all.retrieve',
911 desc => 'Retrieves a purchase order',
913 {desc => 'Authentication token', type => 'string'},
914 {desc => 'purchase_order to retrieve', type => 'number'},
915 {desc => q/Options hash. flesh_lineitems: to get the lineitems and lineitem_attrs;
916 clear_marc: to clear the MARC data from the lineitem (for reduced bandwidth);
917 limit: number of items to return ,defaults to 50;
918 offset: offset in the list of items to return
919 order_by: sort the result, provide one or more colunm names, separated by commas,
920 optionally followed by ASC or DESC as a single string
921 li_limit : number of lineitems to return if fleshing line items;
922 li_offset : lineitem offset if fleshing line items
923 li_order_by : lineitem sort definition if fleshing line items
924 flesh_lineitem_detail_count : flesh lineitem_detail_count field
928 return => {desc => 'The purchase order, Event on failure'}
932 sub retrieve_all_user_purchase_order {
933 my($self, $conn, $auth, $options) = @_;
934 my $e = new_editor(authtoken=>$auth);
935 return $e->event unless $e->checkauth;
938 # grab purchase orders I have
939 my $perm_orgs = $U->user_has_work_perm_at($e, 'MANAGE_PROVIDER', {descendants =>1});
940 return OpenILS::Event->new('PERM_FAILURE', ilsperm => 'MANAGE_PROVIDER')
942 my $provider_ids = $e->search_acq_provider({owner => $perm_orgs}, {idlist=>1});
943 my $po_ids = $e->search_acq_purchase_order({provider => $provider_ids}, {idlist=>1});
945 # grab my purchase orders
946 push(@$po_ids, @{$e->search_acq_purchase_order({owner => $e->requestor->id}, {idlist=>1})});
948 return undef unless @$po_ids;
950 # now get the db to limit/sort for us
951 $po_ids = $e->search_acq_purchase_order(
953 limit => $$options{limit} || 50,
954 offset => $$options{offset} || 0,
955 order_by => {acqpo => $$options{order_by} || 'create_time'}
961 $conn->respond(retrieve_purchase_order_impl($e, $_, $options)) for @$po_ids;
966 __PACKAGE__->register_method(
967 method => 'search_purchase_order',
968 api_name => 'open-ils.acq.purchase_order.search',
971 desc => 'Search for a purchase order',
973 {desc => 'Authentication token', type => 'string'},
974 {desc => q/Search hash. Search fields include id, provider/, type => 'hash'}
976 return => {desc => 'A stream of POs'}
980 sub search_purchase_order {
981 my($self, $conn, $auth, $search, $options) = @_;
982 my $e = new_editor(authtoken=>$auth);
983 return $e->event unless $e->checkauth;
984 my $po_ids = $e->search_acq_purchase_order($search, {idlist=>1});
985 for my $po_id (@$po_ids) {
986 $conn->respond($e->retrieve_acq_purchase_order($po_id))
987 unless po_perm_failure($e, $po_id);
995 __PACKAGE__->register_method(
996 method => 'retrieve_purchase_order',
997 api_name => 'open-ils.acq.purchase_order.retrieve',
999 desc => 'Retrieves a purchase order',
1001 {desc => 'Authentication token', type => 'string'},
1002 {desc => 'purchase_order to retrieve', type => 'number'},
1003 {desc => q/Options hash. flesh_lineitems, to get the lineitems and lineitem_attrs;
1004 clear_marc, to clear the MARC data from the lineitem (for reduced bandwidth)
1005 li_limit : number of lineitems to return if fleshing line items;
1006 li_offset : lineitem offset if fleshing line items
1007 li_order_by : lineitem sort definition if fleshing line items
1011 return => {desc => 'The purchase order, Event on failure'}
1015 sub retrieve_purchase_order {
1016 my($self, $conn, $auth, $po_id, $options) = @_;
1017 my $e = new_editor(authtoken=>$auth);
1018 return $e->event unless $e->checkauth;
1019 return $e->event if po_perm_failure($e, $po_id);
1020 return retrieve_purchase_order_impl($e, $po_id, $options);
1024 # if the user does not have permission to perform actions on this PO, return the perm failure event
1025 sub po_perm_failure {
1026 my($e, $po_id, $fund_id) = @_;
1027 my $po = $e->retrieve_acq_purchase_order($po_id) or return $e->event;
1028 my $provider = $e->retrieve_acq_provider($po->provider) or return $e->event;
1029 return $e->event unless $e->allowed('MANAGE_PROVIDER', $provider->owner, $provider);
1031 my $fund = $e->retrieve_acq_fund($po->$fund_id);
1032 return $e->event unless $e->allowed('MANAGE_FUND', $fund->org, $fund);
1037 sub retrieve_purchase_order_impl {
1038 my($e, $po_id, $options) = @_;
1041 my $po = $e->retrieve_acq_purchase_order($po_id) or return $e->event;
1043 if($$options{flesh_lineitems}) {
1044 my $items = $e->search_acq_lineitem([
1045 {purchase_order => $po_id},
1049 jub => ['attributes']
1051 limit => $$options{li_limit} || 50,
1052 offset => $$options{li_offset} || 0,
1053 order_by => {jub => $$options{li_order_by} || 'create_time'}
1057 if($$options{clear_marc}) {
1058 $_->clear_marc for @$items;
1061 $po->lineitems($items);
1064 if($$options{flesh_lineitem_count}) {
1065 my $items = $e->search_acq_lineitem({purchase_order => $po_id}, {idlist=>1});
1066 $po->lineitem_count(scalar(@$items));