"forgive" overdues when marking an item lost
authorJeff Godin <jgodin@tadl.org>
Tue, 6 Dec 2011 20:20:44 +0000 (15:20 -0500)
committerJason Stephenson <jstephenson@mvlc.org>
Tue, 22 Jan 2013 19:38:35 +0000 (14:38 -0500)
New option for forgiving (as opposed to voiding) overdues on
a circ when marking the item lost:

When circ.forgive_overdue_on_lost is set, attempt to make a
payment of type "Forgive" on the transaction for the amount
of outstanding overdue billings.

Only outstanding bills of type 1 (Overdue materials) will
be paid, and only the first contiguous grouping. If the
system finds an outstanding billing of type other than 1,
it will pay what it has found up to that point.

circ.forgive_overdue_on_lost has priority over the "void"
version of the same setting, if both happen to be set.

Signed-off-by: Jeff Godin <jgodin@tadl.org>
Signed-off-by: Jason Stephenson <jstephenson@mvlc.org>
Open-ILS/src/perlmods/lib/OpenILS/Application/Cat/AssetCommon.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircCommon.pm

index c87eb28..1ae777c 100644 (file)
@@ -666,6 +666,8 @@ sub set_item_lost {
     # fetch the related org settings
     my $proc_fee = $U->ou_ancestor_setting_value(
         $owning_lib, OILS_SETTING_LOST_PROCESSING_FEE, $e) || 0;
+    my $forgive_overdue = $U->ou_ancestor_setting_value(
+        $owning_lib, OILS_SETTING_FORGIVE_OVERDUE_ON_LOST, $e) || 0;
     my $void_overdue = $U->ou_ancestor_setting_value(
         $owning_lib, OILS_SETTING_VOID_OVERDUE_ON_LOST, $e) || 0;
 
@@ -699,8 +701,13 @@ sub set_item_lost {
     $e->update_action_circulation($circ) or return $e->die_event;
 
     # ---------------------------------------------------------------------
-    # void all overdue fines on this circ if configured
-    if( $void_overdue ) {
+    # forgive outstanding overdue fines or void all overdue fines on this circ if configured
+    if( $forgive_overdue ) {
+        my $evt = OpenILS::Application::Circ::CircCommon->forgive_overdues($e, $circ, "System: OVERDUES FORGIVEN ON LOST");
+        return $evt if $evt;
+
+    } elsif( $void_overdue ) {
+
         my $evt = OpenILS::Application::Circ::CircCommon->void_overdues($e, $circ);
         return $evt if $evt;
     }
index 79df59d..5f266ad 100644 (file)
@@ -17,6 +17,99 @@ my $U = "OpenILS::Application::AppUtils";
 
 
 # -----------------------------------------------------------------
+# Forgive (don't void) unpaid overdue fines on the given circ.
+# This is different from void_overdues in a few ways:
+#   * only deals with 'unpaid' overdue billings
+#   * does not accept a backdate argument
+#   * only forgives if the first unpaid billing is of type 1,
+#     and stops when it gets to a billing type other than 1
+# -----------------------------------------------------------------
+sub forgive_overdues {
+    my ($class, $e, $circ, $note) = @_;
+
+
+    $logger->info("attempting to forgive overdues on circ " . $circ->id . " with note " . $note);
+
+    # find all unvoided bills in order
+    my $bill_search = [
+        { xact => $circ->id, voided=>'f' },
+        { order_by => { mb => { billing_ts => { direction => 'asc' } } } },
+    ];
+
+    # find all unvoided payments in order
+    my $payment_search = [
+        { xact => $circ->id, voided=>'f' },
+        { order_by => { mp => { payment_ts => { direction => 'asc' } } } },
+    ];
+
+    my $bills = $e->search_money_billing($bill_search);
+
+    my $payments = $e->search_money_payment($payment_search);
+
+    # "Pay" the bills, removing fully paid bills and
+    # adjusting the amount for partially paid bills
+    map {
+            my $payment = $_;
+            my $paybal = $payment->amount;
+
+            while ($paybal > 0) {
+                    # get next billing
+                    my $bill = shift @{$bills};
+                    my $newbal = (($paybal*100) - ($bill->amount*100))/100;
+                    if ($newbal < 0) {
+                            $newbal = 0;
+                            my $new_bill_amount = (($bill->amount*100) - ($paybal*100))/100;
+                            $bill->amount($new_bill_amount);
+                            unshift(@{$bills}, $bill); # put the partially-paid bill back on top of the stack
+                    }
+
+                    $paybal = $newbal;
+
+            }
+
+    } @$payments;
+
+    # Sum any outstanding overdue billings, stopping at the first non-overdue billing
+
+    my $outstanding_overdues = 0;
+
+    foreach (@$bills) {
+        my $bill = $_;
+        if ($bill->btype == 1) {
+            $logger->debug("forgive_overdues found a btype 1 bill id " . $bill->id . " amount " . $bill->amount);
+            $outstanding_overdues = ($outstanding_overdues*100 + $bill->amount*100)/100;
+        } else {
+            # We found a billing type other than 1 -- Overdue Fines
+            $logger->info("forgive_overdues found a bill id " . $bill->id . " with btype " . $bill->btype);
+            last; # stop looking for bills to forgive
+        }
+
+    }
+
+    $logger->debug("forgive_overdues outstanding balance to forgive is: " . $outstanding_overdues);
+    my $amount = $outstanding_overdues;
+
+    if ($amount >= 0.01) {
+        # pay with forgive payment
+        my $payobj = Fieldmapper::money::forgive_payment->new;
+        $payobj->amount($amount);
+        $payobj->amount_collected($amount);
+        $payobj->xact($circ->id);
+        $payobj->note($note);
+        # do we need an accepting user? who should be the accepting user?
+        $payobj->accepting_usr($e->requestor->id); # or 1?
+
+        $logger->info("forgive_overdues about to create the forgive payment... ");
+        $e->create_money_forgive_payment($payobj) or return $e->die_event;
+
+        return undef;
+    } else {
+        $logger->info("forgive_overdues found no outstanding overdues, or found outstanding billings of another type first. No forgive payment made.");
+        return undef;
+    }
+}
+
+# -----------------------------------------------------------------
 # Voids overdue fines on the given circ.  if a backdate is 
 # provided, then we only void back to the backdate, unless the
 # backdate is to within the grace period, in which case we void all