Cocoa-Puffs commented on PR #6369:
URL: https://github.com/apache/fineract/pull/6369#issuecomment-5569355098
I've addressed all 3 concerns:
**1. Claim: persisted rate changes silently dropped, then persisted away**
This was unfortunately true. Currently there is no similar mechanism for WC
loans that exists for term loans in regards to regenerating loans with outdated
model versions. I have implemented the same functionality that we have for
progressive loans.
|# | Piece | Location|
|-- | -- | --|
1 | Version queries | ProjectedAmortizationLoanModelRepository ->
existsByLoanIdAndJsonModelVersionNot, findLoanIdsRequiringModelRecalculation
2 | Processing service | WorkingCapitalLoanModelProcessingService (new) ->
requiresModelRecalculation, findLoanIdsRequiringModelRecalculation,
recalculateModelAndSave in REQUIRES_NEW
3 | Rebuild entry point | rebuildScheduleModelFromRecordedHistory on the
write service
4 | COB hook | AbstractWorkingCapitalLoanCOBWorkerItemProcessor.process,
rebuilding before business steps; constructors threaded through both concrete
processors and both configs
5 | API filter | WorkingCapitalLoanModelCheckerFilter +
WorkingCapitalLoanModelCheckerHelper (new), wired in SecurityConfig
**2. Claim: closed schedules grow a zero row per elapsed day and move the
maturity date**
This was a real bug that is now fixed.
Where it failed: _ProjectedAmortizationScheduleModel.minimumScheduleDays()_
```java
int minimum = elapsedPeriodCount() + 1; // the calendar, not recorded facts
```
That floor only ever bit on a loan whose balance closes before today. While
a loan still owes something the walk reaches today anyway, it bills until the
balance closes and an unpaid day doesn't bring that closer.
The fix:
```java
int minimum = 1;
final int offset = currentFirstPeriodDayOffset();
for (final ActualPayment payment : actualPayments) {
minimum = Math.max(minimum, resolvePaymentIndex(payment.date(), offset)
+ 1);
}
for (final PrincipalAdjustment adjustment : principalAdjustments) {
minimum = Math.max(minimum, resolvePaymentIndex(adjustment.date(),
offset) + 1);
}
```
Not "exit when closed": a payment dated after the closing day currently
reaches the schedule only because the elapsed floor drags the walk out to it. A
naive closed-check would silently drop it. scheduleTerm() still carries
elapsedPeriodCount() + 1 and is untouched, it governs the valid date range for
a payment or rate change, which should extend to today.
New unit test validating the behaviour:
aClosedScheduleGrowsToReachMoneyButNotMerelyToReachToday
**3. Claim: residual gap in the forward projection**
This is now fixed. Three details, each load-bearing:
1. Split the rates. dailyPayment = TPV × rate / npvDayCount / 100 has no
balance term, so re-solving at the same period rate returns the same instalment
— only eir and term move. The projection can therefore have its own rate while
PlanCursor keeps the contractual one. Recognised income is untouched by
construction.
2. Pair exact with exact. Using discountFee − aggregatedHighPrecisionActual
makes the sum exactly net + fee − collected, so solving from a position the
plan already predicted returns the rate it already had: the re-solve is a no-op
for an on-time payer.
3. Re-solve lazily. Days with a record are always a run from the start, so
one solve from the last of them re-prices the whole tail:
```java
if (projectionStale && !settled) {
final BigDecimal unearnedFee =
discountFee.subtract(aggregatedHighPrecisionActual, mc);
if (balance.signum() > 0 && unearnedFee.signum() > 0) {
try {
projection = AmortizationParams.solve(balance, unearnedFee,
totalPaymentVolume, rateInForce, ...);
} catch (final IllegalArgumentException | IllegalStateException |
ArithmeticException e) {
log.debug("Could not re-price the projection from balance {}
with {} of fee unearned", balance, unearnedFee, e);
}
}
projectionStale = false;
}
```
5,705 → 470 solves, output bit-for-bit identical to solving on every settled
day, and the cost problem of resolving for every day disappears.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]