1 package OpenILS::Application::Acq::Invoice;
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::Application::AppUtils;
10 my $U = 'OpenILS::Application::AppUtils';
13 __PACKAGE__->register_method(
14 method => 'build_invoice_api',
15 api_name => 'open-ils.acq.invoice.update',
17 desc => q/Creates, updates, and deletes invoices, and related invoice entries, and invoice items/,
19 {desc => 'Authentication token', type => 'string'},
20 {desc => q/Invoice/, type => 'number'},
21 {desc => q/Entries. Array of 'acqie' objects/, type => 'array'},
22 {desc => q/Items. Array of 'acqii' objects/, type => 'array'},
24 return => {desc => 'The invoice w/ entries and items attached', type => 'object', class => 'acqinv'}
28 sub build_invoice_api {
29 my($self, $conn, $auth, $invoice, $entries, $items) = @_;
31 my $e = new_editor(xact => 1, authtoken=>$auth);
32 return $e->die_event unless $e->checkauth;
36 $invoice->receiver($e->requestor->ws_ou) unless $invoice->receiver;
37 $invoice->recv_method('PPR') unless $invoice->recv_method;
38 $invoice->recv_date('now') unless $invoice->recv_date;
39 $e->create_acq_invoice($invoice) or return $e->die_event;
40 } elsif($invoice->isdeleted) {
41 i$e->delete_acq_invoice($invoice) or return $e->die_event;
43 $e->update_acq_invoice($invoice) or return $e->die_event;
46 # call only provided the ID
47 $invoice = $e->retrieve_acq_invoice($invoice) or return $e->die_event;
50 return $e->die_event unless $e->allowed('CREATE_INVOICE', $invoice->receiver);
53 for my $entry (@$entries) {
54 $entry->invoice($invoice->id);
56 $e->create_acq_invoice_entry($entry) or return $e->die_event;
57 } elsif($entry->isdeleted) {
58 $e->delete_acq_invoice_entry($entry) or return $e->die_event;
59 } elsif($entry->ischanged) {
60 $e->update_acq_invoice_entry($entry) or return $e->die_event;
66 for my $item (@$items) {
67 $item->invoice($invoice->id);
69 $e->create_acq_invoice_item($item) or return $e->die_event;
70 } elsif($item->isdeleted) {
71 $e->delete_acq_invoice_item($item) or return $e->die_event;
72 } elsif($item->ischanged) {
73 $e->update_acq_invoice_item($item) or return $e->die_event;
78 $invoice = fetch_invoice_impl($e, $invoice->id);
84 __PACKAGE__->register_method(
85 method => 'build_invoice_api',
86 api_name => 'open-ils.acq.invoice.retrieve',
88 desc => q/Creates a new stub invoice/,
90 {desc => 'Authentication token', type => 'string'},
91 {desc => q/Invoice Id/, type => 'number'},
93 return => {desc => 'The new invoice w/ entries and items attached', type => 'object', class => 'acqinv'}
98 sub fetch_invoice_api {
99 my($self, $conn, $auth, $invoice_id, $options) = @_;
101 my $e = new_editor(authtoken=>$auth);
102 return $e->event unless $e->checkauth;
104 my $invoice = fetch_invoice_impl($e, $invoice_id, $options) or
106 return $e->event unless $e->allowed(['VIEW_INVOICE', 'CREATE_INVOICE'], $invoice->receiver);
111 sub fetch_invoice_impl {
112 my ($e, $invoice_id, $options) = @_;
116 my $args = $options->{"no_flesh_misc"} ? $invoice_id : [
121 "acqinv" => ["entries", "items"],
122 "acqie" => ["lineitem", "purchase_order"],
123 "acqii" => ["fund_debit"],
124 "jub" => ["attributes"]
129 my $invoice = $e->retrieve_acq_invoice($args);
130 return $invoice if $options->{no_flesh_misc} or $options->{keep_li_marc};
132 $_->lineitem->clear_marc for @{$invoice->entries};
136 __PACKAGE__->register_method(
137 method => 'process_invoice',
138 api_name => 'open-ils.acq.invoice.process',
141 Process an invoice. This updates the related fund debits by applying the now known cost
142 and sets the encumbrance flag to false. It creates new debits for ad-hoc expenditures (invoice_item's).
143 For all invoice items that have the prorate flag set to true, this will create the necessary
144 additional invoice_item's to prorate the cost across all affected funds by percent spent for each fund.
147 {desc => 'Authentication token', type => 'string'},
148 {desc => q/Invoice Id/, type => 'number'},
150 return => {desc => 'The updated invoice w/ entries and items attached', type => 'object', class => 'acqinv'}
155 sub process_invoice {
156 my($self, $conn, $auth, $invoice_id) = @_;
158 my $e = new_editor(xact => 1, authtoken=>$auth);
159 return $e->die_event unless $e->checkauth;
161 my $invoice = fetch_invoice_impl($e, $invoice_id) or return $e->die_event;
162 return $e->die_event unless $e->allowed('CREATE_INVOICE', $invoice->receiver);
166 for my $entry (@{$invoice->entries}) {
168 my $debits = $e->json_query({
169 select => {acqfdeb => ['id']},
173 filter => {cancel_reason => undef, recv_time => {'!=' => undef}},
178 filter => {id => $entry->id}
186 where => {'+acqfdeb' => {encumbrance => 't'}}
189 next unless @$debits;
191 if($entry->phys_item_count > @$debits) {
193 # We can't invoice for more items than we have debits for
194 return OpenILS::Event->new('ACQ_INVOICE_ENTRY_COUNT_EXCEEDS_DEBITS', payload => {entry => $entry->id});
197 for my $debit_id (map { $_->{id} } @$debits) {
198 my $debit = $e->retrieve_acq_fund_debit($debit_id);
199 $debit->amount($entry->cost_billed);
200 $debit->encumbrance('f');
201 $e->update_acq_fund_debit($debit) or return $e->die_event;
202 $fund_totals{$debit->fund} ||= 0;
203 $fund_totals{$debit->fund} += $entry->cost_billed;
207 my $total_entry_cost = 0;
208 $total_entry_cost += $fund_totals{$_} for keys %fund_totals;
210 $logger->info("invoice: total bib cost for invoice = $total_entry_cost");
212 # collect amount spent per fund to get percents
214 for my $item (@{$invoice->items}) {
216 # prorate and create fund debits as appropriate