Farooq Ayoade created FINERACT-2665:
---------------------------------------

             Summary: PUT /loans/{loanId} (modify loan application) fails with 
a NullPointerException (HTTP 500) when a loan product allows attribute 
overrides but the request omits the attribute
                 Key: FINERACT-2665
                 URL: https://issues.apache.org/jira/browse/FINERACT-2665
             Project: Apache Fineract
          Issue Type: Bug
          Components: Loan
            Reporter: Farooq Ayoade


h3. Observed behavior

Modifying a submitted-and-pending loan application via {{PUT 
/fineract-provider/api/v1/loans/\{loanId}}} — when the change triggers a 
schedule recalculation (e.g. changing {{{}productId{}}}) and the request body 
does not resend {{interestType}} — returns HTTP 500 with a raw NPE:
 
!http://localhost:63343/markdownPreview/1998094097/custom-guide/core-tickets!
 
{{java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" 
because "selectedMethod" is null
    at 
org.apache.fineract.portfolio.loanproduct.domain.InterestMethod.fromInt(InterestMethod.java:45)
    at 
org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler.assembleLoanApplicationTermsFrom(LoanScheduleAssembler.java:238)
    at 
org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler.assembleLoanTerms(LoanScheduleAssembler.java:178)
    at 
org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler.assembleLoanScheduleFrom(LoanScheduleAssembler.java:711)
    at 
org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformServiceImpl.calculateLoanSchedule(LoanScheduleCalculationPlatformServiceImpl.java:79)
    at 
org.apache.fineract.portfolio.loanaccount.service.LoanAssemblerImpl.updateFrom(LoanAssemblerImpl.java:866)
    at 
org.apache.fineract.portfolio.loanaccount.service.LoanApplicationWritePlatformServiceJpaRepositoryImpl.modifyApplication(LoanApplicationWritePlatformServiceJpaRepositoryImpl.java:277)}}

The same class of NPE occurs for {{amortizationType}} and 
{{{}interestCalculationPeriodType{}}}, and a null {{repaymentEvery}} is 
produced, when those overrides are enabled on the product and the field is 
omitted.
h3. Expected behavior

"Allow overriding X" on a loan product means the override is {*}optional{*}. 
When the modify request omits an override-enabled attribute, the schedule 
should be (re)built using the *product's configured value* for that attribute — 
exactly as it already does when the override is _not_ enabled — instead of 
dereferencing a null and throwing.
h3. Steps to reproduce
 # Have a loan product with one or more attribute overrides enabled — i.e. a 
row in {{m_product_loan_configurable_attributes}} with {{interest_method_enum = 
1}} (and/or {{{}amortization_method_enum{}}}, 
{{{}interest_calculated_in_period_enum{}}}, {{{}repay_every{}}}). A product 
with *no* row also reproduces, because 
{{LoanProductConfigurableAttributes.populateDefaultsForConfigurableAttributes()}}
 defaults every flag to {{{}true{}}}.

 # Create a loan application (Submitted and pending approval) on some product.

 # {{PUT /fineract-provider/api/v1/loans/\{loanId}}} changing {{productId}} to 
an override-enabled product, with a minimal body that omits 
{{{}interestType{}}}:
 {{{ "locale": "en", "dateFormat": "dd MMMM yyyy", "loanType": "individual", 
"productId": <overrideEnabledId> }}}
 # Observe HTTP 500 and the NPE above. (Re-sending the loan's _current_ product 
is a no-op — {{{}changes:{}{}}}, no recalculation — so it appears to "work"; 
the failure only manifests once a real change forces the schedule rebuild.)

h3. Root cause

{{LoanScheduleAssembler.assembleLoanApplicationTermsFrom(...)}} reads each 
override-gated attribute from the request JSON when the product allows 
overriding it, but passes the (possibly {{{}null{}}}) value straight into 
{{{}XxxMethod.fromInt(...){}}}:
final Boolean allowOverridingInterestMethod = 
loanProduct.getLoanConfigurableAttributes().getInterestMethodBoolean();

final Integer interestType = 
this.fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element); 
// null on modify when omitted
final InterestMethod interestMethod = allowOverridingInterestMethod
        ? InterestMethod.fromInt(interestType)   // line 238 — fromInt(null) → 
NPE
        : loanProduct.getLoanProductRelatedDetail().getInterestMethod();
{{}}



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

Reply via email to