]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Acq/Invoice.pm
consolidated invoice attach and create API call into a single cud call
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Acq / Invoice.pm
1 package OpenILS::Application::Acq::Invoice;
2 use base qw/OpenILS::Application/;
3 use strict; use warnings;
4
5 use OpenSRF::Utils::Logger qw(:logger);
6 use OpenILS::Utils::Fieldmapper;
7 use OpenILS::Utils::CStoreEditor q/:funcs/;
8 use OpenILS::Application::AppUtils;
9 use OpenILS::Event;
10 my $U = 'OpenILS::Application::AppUtils';
11
12
13 __PACKAGE__->register_method(
14         method => 'build_invoice_api',
15         api_name        => 'open-ils.acq.invoice.update',
16         signature => {
17         desc => q/Creates, updates, and deletes invoices, and related invoice entries, and invoice items/,
18         params => [
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'},
23         ],
24         return => {desc => 'The invoice w/ entries and items attached', type => 'object', class => 'acqinv'}
25     }
26 );
27
28 sub build_invoice_api {
29     my($self, $conn, $auth, $invoice, $entries, $items) = @_;
30
31     my $e = new_editor(xact => 1, authtoken=>$auth);
32     return $e->die_event unless $e->checkauth;
33
34     if(ref $invoice) {
35         if($invoice->isnew) {
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;
42         } else {
43             $e->update_acq_invoice($invoice) or return $e->die_event;
44         }
45     } else {
46         # call only provided the ID
47         $invoice = $e->retrieve_acq_invoice($invoice) or return $e->die_event;
48     }
49
50     return $e->die_event unless $e->allowed('CREATE_INVOICE', $invoice->receiver);
51
52     if($entries) {
53         for my $entry (@$entries) {
54             $entry->invoice($invoice->id);
55             if($entry->isnew) {
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;
61             }
62         }
63     }
64
65     if($items) {
66         for my $item (@$items) {
67             $item->invoice($invoice->id);
68             if($item->isnew) {
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;
74             }
75         }
76     }
77
78     $invoice = fetch_invoice_impl($e, $invoice->id);
79     $e->commit;
80
81     return $invoice;
82 }
83
84 __PACKAGE__->register_method(
85         method => 'build_invoice_api',
86         api_name        => 'open-ils.acq.invoice.retrieve',
87         signature => {
88         desc => q/Creates a new stub invoice/,
89         params => [
90             {desc => 'Authentication token', type => 'string'},
91             {desc => q/Invoice Id/, type => 'number'},
92         ],
93         return => {desc => 'The new invoice w/ entries and items attached', type => 'object', class => 'acqinv'}
94     }
95 );
96
97
98 sub fetch_invoice_api {
99     my($self, $conn, $auth, $invoice_id, $options) = @_;
100
101     my $e = new_editor(authtoken=>$auth);
102     return $e->event unless $e->checkauth;
103
104     my $invoice = fetch_invoice_impl($e, $invoice_id, $options) or
105         return $e->event;
106     return $e->event unless $e->allowed(['VIEW_INVOICE', 'CREATE_INVOICE'], $invoice->receiver);
107
108     return $invoice;
109 }
110
111 sub fetch_invoice_impl {
112     my ($e, $invoice_id, $options) = @_;
113
114     $options ||= {};
115
116     my $args = $options->{"no_flesh_misc"} ? $invoice_id : [
117         $invoice_id,
118         {
119             "flesh" => 4,
120             "flesh_fields" => {
121                 "acqinv" => ["entries", "items"],
122                 "acqie" => ["lineitem", "purchase_order"],
123                 "acqii" => ["fund_debit"],
124                 "jub" => ["attributes"]
125             }
126         }
127     ];
128
129     my $invoice = $e->retrieve_acq_invoice($args);
130     return $invoice if $options->{no_flesh_misc} or $options->{keep_li_marc};
131
132     $_->lineitem->clear_marc for @{$invoice->entries};
133     return $invoice;
134 }
135
136 __PACKAGE__->register_method(
137         method => 'process_invoice',
138         api_name        => 'open-ils.acq.invoice.process',
139         signature => {
140         desc => q/
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.
145         /,
146         params => [
147             {desc => 'Authentication token', type => 'string'},
148             {desc => q/Invoice Id/, type => 'number'},
149         ],
150         return => {desc => 'The updated invoice w/ entries and items attached', type => 'object', class => 'acqinv'}
151     }
152 );
153
154
155 sub process_invoice {
156     my($self, $conn, $auth, $invoice_id) = @_;
157
158     my $e = new_editor(xact => 1, authtoken=>$auth);
159     return $e->die_event unless $e->checkauth;
160
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);
163
164     my %fund_totals;
165
166     for my $entry (@{$invoice->entries}) {
167         
168         my $debits = $e->json_query({
169             select => {acqfdeb => ['id']},
170             from => {
171                 acqfdeb => {
172                     acqlid => {
173                         filter => {cancel_reason => undef, recv_time => {'!=' => undef}},
174                         join => {
175                             jub =>  {
176                                 join => {
177                                     acqie => {
178                                         filter => {id => $entry->id}
179                                     }
180                                 }
181                             }
182                         }
183                     }
184                 }
185             },
186             where => {'+acqfdeb' => {encumbrance => 't'}}
187         });
188
189         next unless @$debits;
190
191         if($entry->phys_item_count > @$debits) {
192             $e->rollback;
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});
195         }
196
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;
204         }
205     }
206
207     my $total_entry_cost = 0;
208     $total_entry_cost += $fund_totals{$_} for keys %fund_totals;
209
210     $logger->info("invoice: total bib cost for invoice = $total_entry_cost");
211
212     # collect amount spent per fund to get percents
213
214     for my $item (@{$invoice->items}) {
215
216         # prorate and create fund debits as appropriate
217     }
218
219     $e->rollback;
220     return $invoice;
221
222 }
223
224
225 1;