From: Jeff Godin Date: Tue, 6 Dec 2011 18:27:09 +0000 (-0500) Subject: Support forgive-vs void on Lost/Lost-checkin X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=751fa542ce71db23b3e135bd2d3f78ca62047d77;p=evergreen%2Ftadl.git Support forgive-vs void on Lost/Lost-checkin Constants and strings for new OU settings: circ.forgive_overdue_on_lost circ.forgive_lost_on_checkin circ.forgive_lost_proc_fee_on_checkin These are intended for use as an alternative to their "void" counterparts in situations where the library does not refund fees and fines, and wishes funds taken in for one purpose or billing type to not be re-applied toward a different billing type as a result of voiding bills after they have been paid. 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. When circ.forgive_lost_on_checkin and/or circ.forgive_lost_proc_fee_on_checkin is set, make a Forgive payment on outstanding Lost and Lost Processing Fee bills on checkin of a lost item. New utility function: outstanding_bills_for_circ (in AppUtils) accepts an editor and a circ object, and will return a reference to an array of outstanding billing objects. this has been broken out of the forgive_overdues sub, as it will be useful elsewhere. Signed-off-by: Jeff Godin --- diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm index 792f53bb6f..5d8668f0f2 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm @@ -2128,5 +2128,57 @@ sub basic_opac_copy_query { }; } +# ----------------------------------------------------------------- +# Given an editor and a circ, return a reference to an array of +# billing objects which are outstanding (unpaid, not voided). +# If a bill is partially paid, change the amount of the bill +# to reflect the unpaid amount, not the original amount. +# ----------------------------------------------------------------- +sub outstanding_bills_for_circ { + my ($self, $e, $circ) = @_; + + # 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 * 100; + + while ($paybal > 0) { + # get next billing + my $bill = shift @{$bills}; + my $newbal = $paybal - $bill->amount*100; + if ($newbal < 0) { + $newbal = 0; + my $new_bill_amount = $bill->amount*100 - $paybal; + $bill->amount($new_bill_amount/100); + unshift(@{$bills}, $bill); # put the partially-paid bill back on top of the stack + } + + $paybal = $newbal; + + } + + } @$payments; + + return $bills; + +} + 1; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat/AssetCommon.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat/AssetCommon.pm index ff750991fe..4be2774984 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat/AssetCommon.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat/AssetCommon.pm @@ -651,6 +651,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; @@ -684,8 +686,12 @@ 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; } diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircCommon.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircCommon.pm index 613447fd59..e2b805d0fc 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircCommon.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CircCommon.pm @@ -17,6 +17,63 @@ 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); + + # get outstanding bills for the circ in question + my $bills = $U->outstanding_bills_for_circ($e, $circ); + + # 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 diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm index 5bdb1c349c..ea7dfdf818 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm @@ -3348,7 +3348,7 @@ sub checkin_handle_circ { # ------------------------------------------------------------------ -# See if we need to void billings for lost checkin +# See if we need to void or forgive billings for lost checkin # ------------------------------------------------------------------ sub checkin_handle_lost { my $self = shift; @@ -3373,6 +3373,10 @@ sub checkin_handle_lost { if (!$max_return){ # there's either no max time to accept returns defined or we're within that time + my $forgive_lost = $U->ou_ancestor_setting_value( + $circ_lib, OILS_SETTING_FORGIVE_LOST_ON_CHECKIN, $self->editor) || 0; + my $forgive_lost_fee = $U->ou_ancestor_setting_value( + $circ_lib, OILS_SETTING_FORGIVE_LOST_PROCESS_FEE_ON_CHECKIN, $self->editor) || 0; my $void_lost = $U->ou_ancestor_setting_value( $circ_lib, OILS_SETTING_VOID_LOST_ON_CHECKIN, $self->editor) || 0; my $void_lost_fee = $U->ou_ancestor_setting_value( @@ -3382,8 +3386,8 @@ sub checkin_handle_lost { $self->generate_lost_overdue(1) if $U->ou_ancestor_setting_value( $circ_lib, OILS_SETTING_GENERATE_OVERDUE_ON_LOST_RETURN, $self->editor); - $self->checkin_handle_lost_now_found(3) if $void_lost; - $self->checkin_handle_lost_now_found(4) if $void_lost_fee; + $self->checkin_handle_lost_now_found(3, $forgive_lost) if $forgive_lost || $void_lost; + $self->checkin_handle_lost_now_found(4, $forgive_lost_fee) if $forgive_lost_fee || $void_lost_fee; $self->checkin_handle_lost_now_found_restore_od($circ_lib) if $restore_od && ! $self->void_overdues; } @@ -3733,31 +3737,67 @@ sub make_trigger_events { sub checkin_handle_lost_now_found { - my ($self, $bill_type) = @_; + my ($self, $bill_type, $forgive) = @_; # ------------------------------------------------------------------ # remove charge from patron's account if lost item is returned # ------------------------------------------------------------------ - my $bills = $self->editor->search_money_billing( - { - xact => $self->circ->id, - btype => $bill_type - } - ); + if ($forgive) { + my $bills = $U->outstanding_bills_for_circ($self->editor, $self->circ); - $logger->debug("voiding lost item charge of ".scalar(@$bills)); - for my $bill (@$bills) { - if( !$U->is_true($bill->voided) ) { - $logger->info("lost item returned - voiding bill ".$bill->id); - $bill->voided('t'); - $bill->void_time('now'); - $bill->voider($self->editor->requestor->id); - my $note = ($bill->note) ? $bill->note . "\n" : ''; - $bill->note("${note}System: VOIDED FOR LOST ITEM RETURNED"); + for my $bill (@$bills) { + $logger->info("checkin_handle_lost_now_found found bill " . $bill->id . " type " . $bill->btype . " amount " . $bill->amount); + } + $logger->debug("forgiving lost item charge of ".scalar(@$bills)); + my $total_to_forgive = 0; + for my $bill (@$bills) { + if ($bill->btype == $bill_type) { + $logger->info("lost item returned - will make payment to forgive bill ".$bill->id); + # add this bill's amount to the total to forgive + $total_to_forgive = (($total_to_forgive*100) + ($bill->amount*100)) / 100; + } else { + last; + } + } + if ($total_to_forgive >= 0.01) { + # pay with forgive payment + my $payobj = Fieldmapper::money::forgive_payment->new; + $payobj->amount($total_to_forgive); + $payobj->amount_collected($total_to_forgive); + $payobj->xact($self->circ->id); + $payobj->note("System: FORGIVEN FOR LOST ITEM RETURNED"); + $payobj->accepting_usr($self->editor->requestor->id); + + $logger->info("checkin_handle_lost_now_found about to create forgive payment... "); $self->bail_on_events($self->editor->event) - unless $self->editor->update_money_billing($bill); + unless $self->editor->create_money_forgive_payment($payobj); + } else { + $logger->info("checkin_handle_lost_now_found found no billings of type $bill_type to forgive."); + } + + } else { + my $bills = $self->editor->search_money_billing( + { + xact => $self->circ->id, + btype => $bill_type + } + ); + + $logger->debug("voiding lost item charge of ".scalar(@$bills)); + for my $bill (@$bills) { + if( !$U->is_true($bill->voided) ) { + $logger->info("lost item returned - voiding bill ".$bill->id); + $bill->voided('t'); + $bill->void_time('now'); + $bill->voider($self->editor->requestor->id); + my $note = ($bill->note) ? $bill->note . "\n" : ''; + $bill->note("${note}System: VOIDED FOR LOST ITEM RETURNED"); + + $self->bail_on_events($self->editor->event) + unless $self->editor->update_money_billing($bill); + } } } } diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Const.pm b/Open-ILS/src/perlmods/lib/OpenILS/Const.pm index f86a801dc3..25479e2b3d 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Const.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Const.pm @@ -78,6 +78,7 @@ econst OILS_SETTING_LOST_PROCESSING_FEE => 'circ.lost_materials_processing_fee'; econst OILS_SETTING_DEF_ITEM_PRICE => 'cat.default_item_price'; econst OILS_SETTING_ORG_BOUNCED_EMAIL => 'org.bounced_emails'; econst OILS_SETTING_CHARGE_LOST_ON_ZERO => 'circ.charge_lost_on_zero'; +econst OILS_SETTING_FORGIVE_OVERDUE_ON_LOST => 'circ.forgive_overdue_on_lost'; econst OILS_SETTING_VOID_OVERDUE_ON_LOST => 'circ.void_overdue_on_lost'; econst OILS_SETTING_HOLD_SOFT_STALL => 'circ.hold_stalling.soft'; econst OILS_SETTING_HOLD_HARD_STALL => 'circ.hold_stalling.hard'; @@ -85,8 +86,10 @@ econst OILS_SETTING_HOLD_SOFT_BOUNDARY => 'circ.hold_boundary.soft'; econst OILS_SETTING_HOLD_HARD_BOUNDARY => 'circ.hold_boundary.hard'; econst OILS_SETTING_HOLD_EXPIRE => 'circ.hold_expire_interval'; econst OILS_SETTING_HOLD_ESIMATE_WAIT_INTERVAL => 'circ.holds.default_estimated_wait_interval'; +econst OILS_SETTING_FORGIVE_LOST_ON_CHECKIN => 'circ.forgive_lost_on_checkin'; econst OILS_SETTING_VOID_LOST_ON_CHECKIN => 'circ.void_lost_on_checkin'; econst OILS_SETTING_MAX_ACCEPT_RETURN_OF_LOST => 'circ.max_accept_return_of_lost'; +econst OILS_SETTING_FORGIVE_LOST_PROCESS_FEE_ON_CHECKIN => 'circ.forgive_lost_proc_fee_on_checkin'; econst OILS_SETTING_VOID_LOST_PROCESS_FEE_ON_CHECKIN => 'circ.void_lost_proc_fee_on_checkin'; econst OILS_SETTING_RESTORE_OVERDUE_ON_LOST_RETURN => 'circ.restore_overdue_on_lost_return'; econst OILS_SETTING_LOST_IMMEDIATELY_AVAILABLE => 'circ.lost_immediately_available'; diff --git a/Open-ILS/web/opac/locale/en-US/lang.dtd b/Open-ILS/web/opac/locale/en-US/lang.dtd index fd3e752b38..c6facac41e 100644 --- a/Open-ILS/web/opac/locale/en-US/lang.dtd +++ b/Open-ILS/web/opac/locale/en-US/lang.dtd @@ -2128,6 +2128,8 @@ + + @@ -2169,10 +2171,14 @@ + + + +