[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