Foo Developer created FINERACT-2946:
---------------------------------------

             Summary: Loan reschedule preview with a grace on principal 
randomly returns a wrong schedule with a balloon last installment
                 Key: FINERACT-2946
                 URL: https://issues.apache.org/jira/browse/FINERACT-2946
             Project: Apache Fineract
          Issue Type: Bug
          Components: Loan
            Reporter: Foo Developer
            Assignee: Foo Developer


h3. Summary

The loan reschedule preview ({{GET 
/rescheduleloans/\{id\}?command=previewLoanReschedule}}) for a request with a 
grace on principal returns one of two different schedules for the same request. 
Which one comes back varies from call to call. One of them is wrong: it 
amortises the remaining principal over the grace periods too and ends in a 
large balloon installment, so the preview can disagree with the schedule that 
approval then applies.

h3. Steps to reproduce

# Create and disburse a loan: 12,000 principal, 1% interest per month, 12 
monthly installments, declining balance, equal installments.
# Pay the first installment.
# Create a reschedule request from the next unpaid installment with a grace on 
principal of 2:
{code:json}
{
  "loanId": <loanId>,
  "rescheduleFromDate": "<due date of an unpaid installment>",
  "graceOnPrincipal": 2,
  "rescheduleReasonId": <code value id>,
  "submittedOnDate": "<business date>",
  "dateFormat": "dd MMMM yyyy",
  "locale": "en"
}
{code}
# {{GET /rescheduleloans/\{requestId\}?command=previewLoanReschedule}}, then 
reject the request.
# Repeat steps 3 and 4 several times on the same running server.

h3. Expected

Every preview returns the same schedule, the one approval produces: the 
remaining principal is amortised over the installments after the grace plus the 
extension (about 1,066.18 per installment in this example).

h3. Actual

The installment amount after the grace alternates between the correct annuity 
and a smaller one (about 897.21 in this example), followed by a balloon last 
installment (about 2,665.08).

h3. Root cause

A grace on principal creates two term variations with the same applicable date: 
{{GRACE_ON_PRINCIPAL}} and its child {{EXTEND_REPAYMENT_PERIOD}}.

* {{LoanTermVariationsData.compareTo}} orders only by applicable date (plus 
due-date/insert-installment first), so it returns 0 for these two. The sort is 
stable, so the generator sees them in input order.
* For a preview, the input order is the iteration order of 
{{LoanRescheduleRequest.loanRescheduleRequestToTermVariationMappings}}, a 
{{HashSet}} whose element type does not override {{hashCode}}, so the order is 
effectively random.
* In 
{{AbstractCumulativeLoanScheduleGenerator.applyExceptionLoanTermVariations}}, 
{{EXTEND_REPAYMENT_PERIOD}} calls {{adjustInstallmentOrPrincipalAmount}}, which 
recalculates the fixed installment from 
{{calculateNumberOfRemainingPrincipalPaymentPeriods}}. That only discounts the 
principal-grace periods registered so far. When the extension is processed 
before its grace, the grace periods are not discounted and the installment is 
amortised over too many periods.

Approval builds its variations from {{Loan.loanTermVariations}} 
({{@OrderBy("termApplicableFrom, id")}}), where the grace is always first, so 
approval is deterministic and correct. Only the preview is affected.

h3. Proposed fix

Give same-date ties in {{LoanTermVariationsData.compareTo}} a deterministic 
order:

# due-date and insert-installment variations (keep their existing first place);
# grace on principal and grace on interest;
# extend repayment period;
# everything else.

This is the order approval already gets from the id ordering, so preview and 
approval agree.

h3. Tests

A new unit test, {{LoanTermVariationsDataTest}}, covering the same-date 
tie-breaks (grace before extend in either input order, due-date and 
insert-installment still first, different dates still ordered by date).




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to