# 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;
$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;
}
# -----------------------------------------------------------------
+# 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
# -----------------------------------------------------------------