[ 
https://issues.apache.org/jira/browse/FINERACT-2665?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Farooq Ayoade updated FINERACT-2665:
------------------------------------
    Description: 
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:
 

 
{{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();
{{}}

  was:
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();
{{}}


> 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
>            Priority: Minor
>              Labels: bug, loan
>
> 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:
>  
>  
> {{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