[open-ils-commits] r16174 - trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq (erickson)
svn at svn.open-ils.org
svn at svn.open-ils.org
Thu Apr 8 12:45:47 EDT 2010
Author: erickson
Date: 2010-04-08 12:45:44 -0400 (Thu, 08 Apr 2010)
New Revision: 16174
Modified:
trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Invoice.pm
Log:
implemented invoice processing to updating encumbered funds (for invoice_entry's) and ad-hoc charges, and prorating fees/taxes, etc. (aka invoice_items)
Modified: trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Invoice.pm
===================================================================
--- trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Invoice.pm 2010-04-08 16:45:43 UTC (rev 16173)
+++ trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Invoice.pm 2010-04-08 16:45:44 UTC (rev 16174)
@@ -43,7 +43,7 @@
$e->update_acq_invoice($invoice) or return $e->die_event;
}
} else {
- # call only provided the ID
+ # caller only provided the ID
$invoice = $e->retrieve_acq_invoice($invoice) or return $e->die_event;
}
@@ -55,8 +55,10 @@
if($entry->isnew) {
$e->create_acq_invoice_entry($entry) or return $e->die_event;
} elsif($entry->isdeleted) {
+ # TODO set encumbrance=true for related fund_debit and revert back to estimated price
$e->delete_acq_invoice_entry($entry) or return $e->die_event;
} elsif($entry->ischanged) {
+ # TODO: update the related fund_debit
$e->update_acq_invoice_entry($entry) or return $e->die_event;
}
}
@@ -68,8 +70,14 @@
if($item->isnew) {
$e->create_acq_invoice_item($item) or return $e->die_event;
} elsif($item->isdeleted) {
+ if($item->fund_debit) {
+ $e->delete_acq_fund_debit(
+ $e->retrieve_acq_fund_debit($item->fund_debit)
+ ) or return $e->die_event;
+ }
$e->delete_acq_invoice_item($item) or return $e->die_event;
} elsif($item->ischanged) {
+ # TODO: update related fund debit
$e->update_acq_invoice_item($item) or return $e->die_event;
}
}
@@ -184,7 +192,9 @@
}
}
},
- where => {'+acqfdeb' => {encumbrance => 't'}}
+ where => {'+acqfdeb' => {encumbrance => 't'}},
+ order_by => {'acqlid' => ['recv_time']},
+ limit => $entry->phys_item_count
});
next unless @$debits;
@@ -192,16 +202,24 @@
if($entry->phys_item_count > @$debits) {
$e->rollback;
# We can't invoice for more items than we have debits for
- return OpenILS::Event->new('ACQ_INVOICE_ENTRY_COUNT_EXCEEDS_DEBITS', payload => {entry => $entry->id});
+ return OpenILS::Event->new(
+ 'ACQ_INVOICE_ENTRY_COUNT_EXCEEDS_DEBITS', payload => {entry => $entry->id});
}
+ my $item_cost = $entry->cost_billed;
+ unless($U->is_true($entry->billed_per_item)) {
+ # cost billed is for the whole set of items. Get the
+ # per-item cost by dividing the total cost by total invoiced
+ $item_cost = $item_cost / $entry->inv_item_count;
+ }
+
for my $debit_id (map { $_->{id} } @$debits) {
my $debit = $e->retrieve_acq_fund_debit($debit_id);
- $debit->amount($entry->cost_billed);
+ $debit->amount($item_cost);
$debit->encumbrance('f');
$e->update_acq_fund_debit($debit) or return $e->die_event;
$fund_totals{$debit->fund} ||= 0;
- $fund_totals{$debit->fund} += $entry->cost_billed;
+ $fund_totals{$debit->fund} += $item_cost;
}
}
@@ -210,14 +228,96 @@
$logger->info("invoice: total bib cost for invoice = $total_entry_cost");
- # collect amount spent per fund to get percents
-
for my $item (@{$invoice->items}) {
- # prorate and create fund debits as appropriate
+ # future: cache item types locally
+ my $item_type = $e->retrieve_acq_invoice_item_type($item->inv_item_type) or return $e->die_event;
+
+ if($U->is_true($item_type->prorate)) {
+
+ # Charge prorated across applicable funds
+ my $full_item_cost = $item->cost_billed;
+ my $first_round = 1;
+ my $largest_debit;
+ my $total_debited = 0;
+
+ for my $fund_id (keys %fund_totals) {
+
+ my $spent_for_fund = $fund_totals{$fund_id};
+ next unless $spent_for_fund > 0;
+
+ my $prorated_amount = ($spent_for_fund / $total_entry_cost) * $full_item_cost;
+ $logger->info("invoice: attaching prorated amount $prorated_amount to fund $fund_id for invoice $invoice_id");
+
+ my $debit = Fieldmapper::acq::fund_debit->new;
+ $debit->fund($fund_id);
+ $debit->amount($prorated_amount);
+ $debit->origin_amount($prorated_amount);
+ $debit->origin_currency_type($e->retrieve_acq_fund($fund_id)->currency_type); # future: cache funds locally
+ $debit->encumbrance('f');
+ $debit->debit_type('prorated_charge');
+ $e->create_acq_fund_debit($debit) or return $e->die_event;
+ $total_debited += $prorated_amount;
+ $largest_debit = $debit if !$largest_debit or $debit->amount > $largest_debit->amount;
+
+ if($first_round) {
+
+ # re-purpose the original invoice_item for the first prorated amount
+ $item->fund_debit($debit->id);
+ $item->cost_billed($prorated_amount);
+ $e->update_acq_invoice_item($item) or return $e->die_event;
+
+ } else {
+
+ # for subsequent prorated amounts, create a new invoice_item
+ my $new_item = $item->clone;
+ $new_item->clear_id;
+ $new_item->fund_debit($debit->id);
+ $new_item->cost_billed($prorated_amount);
+ $e->create_acq_invoice_item($new_item) or return $e->die_event;
+ }
+
+ $first_round = 0;
+ }
+
+ # make sure the percentages didn't leave a small sliver of money over/under-debited
+ if($total_debited != $full_item_cost) {
+ $logger->info("invoice: found prorate descrepency. total_debited=$total_debited; total_cost=$full_item_cost; difference ". ($full_item_cost - $total_debited));
+ # tweak the largest debit to smooth out the difference
+ $largest_debit = $e->retrieve_acq_fund_debit($largest_debit); # get latest copy
+ $largest_debit->amount( $largest_debit->amount + ($full_item_cost - $total_debited) );
+ $largest_debit->origin_amount($largest_debit->amount);
+ $e->update_acq_fund_debit($largest_debit) or return $e->die_event;
+ }
+
+ } else { # not prorated
+
+ # Direct charge against a fund
+
+ next if $item->fund_debit;
+
+ unless($item->fund) {
+ $e->rollback;
+ return OpenILS::Event->new('ACQ_INVOICE_ITEM_REQUIRES_FUND', payload => {item => $item->id});
+ }
+
+ my $debit = Fieldmapper::acq::fund_debit->new;
+ $debit->fund($item->fund);
+ $debit->amount($item->cost_billed);
+ $debit->origin_amount($item->cost_billed);
+ $debit->origin_currency_type($e->retrieve_acq_fund($item->fund)->currency_type); # future: cache funds locally
+ $debit->encumbrance('f');
+ $debit->debit_type('direct_charge');
+ $e->create_acq_fund_debit($debit) or return $e->die_event;
+
+ $item->fund_debit($debit->id);
+ $e->update_acq_invoice_item($item) or return $e->die_event;
+ }
}
- $e->rollback;
+ $invoice = fetch_invoice_impl($e, $invoice_id);
+ $e->commit;
+
return $invoice;
}
More information about the open-ils-commits
mailing list