oleksii-novikov-onix commented on PR #6343:
URL: https://github.com/apache/fineract/pull/6343#issuecomment-5490977558

   > Replacing the hardcoded `pow(365)` with `npvDayCount` is a genuine 
correctness fix, and I like the legacy-model fallback in 
`annualEffectiveInterestRate()` for schedules persisted before this change. But 
there's a scope-collision problem that needs sorting out before anything else 
here.
   > 
   > **This directly overlaps with #6264 ("payment rate change EIR history"), 
on the same JIRA ticket.** Both PRs touch 9 of the same files, including 
`ProjectedAmortizationScheduleModel.java` and the WC account Avro schema. Both 
replace the exact same hardcoded `pow(365)` call in 
`WorkingCapitalLoanApplicationReadPlatformServiceImpl`, but with two different 
helper methods living in two different classes. And the two PRs make mutually 
exclusive contract changes: #6264 keeps `dailyEir` on the response/event and 
documents it in a new Avro record; this PR deletes `dailyEir` from the 
response, both mappers, and the existing Avro schema. Whichever of these two 
merges second is going to break the other. This needs to be resolved between 
the two PRs - either merge them, or have one explicitly build on top of the 
other, before either goes further.
   > 
   > Two issues on the substance, both of which also apply to #6264 (already 
flagged there) but are worse here since this PR feeds the rounded value back 
into the schedule math:
   > 
   > **Tenant money rounding mode applied to a rate, and that rate then drives 
the schedule.** `normalizedAnnualRate` rounds with `mc.getRoundingMode()`, 
which is the tenant-configurable money rounding mode - and the result is fed 
straight back through `deannualize` to become `effectiveInterestRate`, the 
value the whole amortization schedule discounts on. A tenant configured `FLOOR` 
and one configured `CEILING` will get materially different WC schedules for 
identical loan inputs. A rate isn't money; this should use a fixed rounding 
mode (#6264 has already been corrected to `RoundingMode.HALF_EVEN` for the same 
issue - worth reusing that constant if the two PRs get reconciled).
   > 
   > **The rate-segment's annual EIR is computed and then thrown away.** 
`computeScheduleParams` returns `annualEir` in `ScheduleParams`, but 
`applyRateChange` only pulls `segment.eir()` and drops `segment.annualEir()` on 
the floor - so after a payment rate change, the reported annual rate goes stale 
while the schedule itself is segment-aware. That directly contradicts the new 
Swagger text ("annual effective rate the schedule runs on").
   > 
   > One more concrete bug: `deannualize`'s Newton-Raphson seed 
(`Math.pow(...)`) can produce `Infinity` for an extreme daily IRR (reachable on 
a short schedule with a large discount fee relative to net disbursement), which 
then throws `NumberFormatException` out of `BigDecimal.valueOf`. The one 
existing caller of this path swallows it by accident (`NumberFormatException 
extends IllegalArgumentException`), but `applyRateChange`'s new call site has 
no such guard and would surface this as a 500. Also: zero unit tests for the 
new `TvmFunctions` numerical kernel (hand-rolled Newton-Raphson, no test class 
at all).
   > 
   > Recommendation: CHANGES_REQUESTED
   
   1. Scope collision with #6264. We touch 10 of the same files, and a test 
merge gives 3 conflicts: the shared feature file, the Swagger block around 
dailyEir, and enrichWithRateAndTerm, where we both replace the hardcoded 365. 
The helpers differ - annualiseEir does not round, normalizedAnnualRate rounds 
to six decimals and feeds the result back into the daily rate - and 
ProjectedAmortizationScheduleModel merges with no conflict at all, ending up 
with both. The rest is not exclusive: we drop dailyEir from the account, #6264 
adds a daily rate to each history row.
   
   2. Tenant money rounding mode on a rate. Fixed: normalizedAnnualRate now 
rounds with a fixed HALF_EVEN constant, the same value #6264 uses.
   
   3. Segment annual EIR dropped. The annual rate is not dropped - 
computeScheduleParams derives the segment's daily rate from it, so every 
segment does run on its own normalised rate. The wrong part was the text: the 
value on the account is the rate the loan was priced at, matching the period 
payment rate next to it, which is also left as booked because what is in force 
on a date is read from the rate-change history. Swagger, the Avro doc and the 
javadoc now say that; the segment's own annual rate belongs on a history row, 
which is #6264's subject.
   
   4. Seed can overflow to Infinity. Fixed.
   
   5. No unit tests. Added TvmFunctionsTest.


-- 
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]

Reply via email to