FINERACT-2665: Fix NPE modifying a loan application when schedule fields are omitted#6059
Conversation
4394787 to
50ab7b0
Compare
adamsaghy
left a comment
There was a problem hiding this comment.
I dont like this approach. No backfilling please!
If something is not provided, it should not be touched / updated!
Thanks for the review. I agree with the principle — an omitted field must not be modified — and that's exactly what this preserves. "Backfill" is probably a misleading word for it; let me clarify. The field updates already honor that rule: updateLoanApplicationAttributes only mutates a field when its parameter is present in the request. Nothing omitted is written or reset. The NPE isn't in the field update — it's in the schedule recalculation. When a modify triggers a recalc, the schedule assembler rebuilds the loan terms from the request JSON. For a schedule-driving field the request omitted (interestType, The assembler needs a complete set of inputs to recalc. This change supplies the omitted ones from the loan's own existing values (loan.getLoanProductRelatedDetail()) — not the product default, not a hardcoded default. So the regenerated schedule Concretely, on an overridden loan (interest method Declining; product default Flat): a modify omitting interestType returns 200, the header stays Declining, and the regenerated schedule equals an explicit Declining run — not the product's Flat. If This also continues FINERACT-2389, which merged the same "omitted ⇒ keep existing value" idiom for the grace/tolerance fields; this closes the gap for the four schedule attributes it didn't cover. If your concern is the mechanism (adding to the parsed JSON), I'm glad to instead read the loan's existing LoanProductRelatedDetail directly at recalc time — same outcome, no touching of the request. Which would you prefer? |
The principle to manipulate the json object which is the incoming request is wrong and should not be followed. If something was not provided, just ignore it... we are doing it in many situations already: |
50ab7b0 to
072f87a
Compare
Hi @adamsaghy |
|
@oluexpert99 Please check the failing test cases. |
072f87a to
112c815
Compare
adamsaghy
left a comment
There was a problem hiding this comment.
I was wondering whether we could cover this changes with integration test / E2E test to ensure correct results?
…lds are omitted
- PUT /v1/loans/{loanId} (modify loan application) returned HTTP 500 with a NullPointerException whenever the
request omitted a schedule-driving field on a product that allows overriding it. The modify recalculation
rebuilt the loan terms from the request JSON via LoanScheduleAssembler.assembleLoanApplicationTermsFrom, so an
omitted override-enabled field (e.g. interestType) was read as null and InterestMethod.fromInt(null) threw.
validateForModify treats these fields as optional, so a partial modify body is valid and reaches the recalc.
- Recalculate the schedule from the loan's own state instead of re-parsing the request JSON. loan.modifyApplication
has already applied the fields the request provided and left omitted ones unchanged, so LoanAssemblerImpl.updateFrom
now regenerates via LoanUtilService.buildScheduleGeneratorDTO(loan, null) + LoanScheduleService.
regenerateRepaymentSchedule(loan, dto) - exactly like the approval, reschedule and apply-holidays flows already
do. An omitted field is simply ignored and its existing value flows through; nothing is written back to the
request. This also completes FINERACT-2389, which fixed the same class of NPE only for the grace/tolerance fields.
- Remove the unused LoanScheduleCalculationPlatformService dependency from LoanAssemblerImpl (the request-JSON
recalc path it served is gone) and wire in LoanScheduleService + LoanUtilService.
- Cover the modify path with two e2e scenarios in LoanOverrideFileds.feature, extending the FINERACT-2389
suite to LP1_WITH_OVERRIDES. Both create a loan whose interestType is a genuine override (FLAT, where the
product defaults to DECLINING BALANCE) and then modify the principal, which forces the schedule recalc that
used to throw: omitting interestType now returns 200 and keeps the loan's own value (and leaves
amortizationType / interestCalculationPeriodType / repaymentEvery untouched), while an interestType that is
supplied on modify is still honoured.
Signed-off-by: oluexpert99 <farooq@techservicehub.io>
112c815 to
9ceb65f
Compare
JSON. A modify request may legitimately send only the fields being changed (LoanApplicationValidator
.validateForModify already treats the rest as optional), but the recalculation read each schedule-driving
field straight from the request: an omitted override-enabled enum hit XxxMethod.fromInt(null) and an omitted
expectedDisbursementDate hit deriveFirstRepaymentDate(null), both throwing a NullPointerException (HTTP 500)
once the change triggered a schedule recalculation. This completes FINERACT-2389, which applied null-override
handling only to the grace/tolerance fields.
(LoanAssemblerImpl.updateFrom -> backfillOmittedScheduleTerms) so the regenerated schedule matches what the
loan records rather than a product default or null: interestType, amortizationType,
interestCalculationPeriodType, repaymentEvery, numberOfRepayments, repaymentFrequencyType, loanTermFrequency,
loanTermFrequencyType, interestRateFrequencyType, graceOnPrincipalPayment, graceOnInterestPayment,
graceOnArrearsAgeing, principal, interestRatePerPeriod, expectedDisbursementDate, submittedOnDate and
interestChargedFromDate. This mirrors validateForModify, so validation and recalculation stay consistent.
them; if a date must be backfilled and either parameter is missing or malformed, fail with a clear
validation error rather than defaulting.
schedule-term backfill from a loan.
Description
Describe the changes made and why they were made. (Ignore if these details are present on the associated Apache Fineract JIRA ticket.)
Checklist
Please make sure these boxes are checked before submitting your pull request - thanks!
Your assigned reviewer(s) will follow our guidelines for code reviews.