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)