Foo Developer created FINERACT-2863:
---------------------------------------
Summary: Retrieving a savings account with template=true fails
with NullPointerException on productOptions accountingRule
Key: FINERACT-2863
URL: https://issues.apache.org/jira/browse/FINERACT-2863
Project: Apache Fineract
Issue Type: Bug
Components: Savings
Reporter: Foo Developer
Assignee: Foo Developer
h3. Steps to reproduce
# Create a savings product (any accounting rule), a client, and submit a
savings account application for that client.
# Load the account with its template, as a UI does for the "modify application"
form:
{code}GET
/fineract-provider/api/v1/savingsaccounts/{accountId}?template=true&associations=charges
{code}
h3. Expected
HTTP 200 with the account, including {{productOptions}} for the product
dropdown.
h3. Actual
The request fails while the response is serialized: a {{NullPointerException}}
inside a bean getter, returned as a raw error (HTTP 400 on current {{develop}},
with the exception text as the body), so the account cannot be edited:
{code}Cannot invoke
"org.apache.fineract.infrastructure.core.data.EnumOptionData.getValue()"
because "this.accountingRule" is null
(through reference chain:
SavingsAccountData["productOptions"]->ArrayList[0]->SavingsProductData["accrualBasedAccountingEnabled"])
{code}
{{GET /savingsaccounts/{accountId}}} without {{template=true}} works, and so
does {{GET /savingsaccounts/template?clientId=...}} (that endpoint serializes
through {{ToApiJsonSerializer}}). A successful {{PUT
/savingsaccounts/{accountId}}} cannot be read back with {{template=true}}
either.
h3. Root cause
With {{template=true}}, {{SavingsAccountTemplateReadPlatformServiceImpl}} fills
{{productOptions}} from
{{SavingsProductReadPlatformService.retrieveAllForLookup()}}, whose row mapper
returns {{SavingsProductData.lookup(id, name)}}. That factory deliberately
leaves every field except id and name {{null}}, including {{accountingRule}}.
{{SavingsAccountsApiResource.retrieveOne}} returns {{SavingsAccountData}}
itself, so the JAX-RS Jackson provider serializes it and calls every public
bean getter it finds. {{SavingsProductData.isCashBasedAccountingEnabled()}},
{{isAccrualBasedAccountingEnabled()}}, {{isUpfrontAccrualAccounting()}} and
{{isPeriodicAccrualAccounting()}} are picked up as properties, and they
dereference {{accountingRule}} without a null check. {{hasAccountingEnabled()}}
and {{accountingRuleTypeId()}} have the same unguarded dereference.
h3. Proposed fix
Null-guard the {{accountingRule}}-dependent methods in {{SavingsProductData}}
(fineract-core), treating a missing accounting rule as "no accounting": the
{{is...}}/{{has...}} methods return {{false}} and {{accountingRuleTypeId()}}
returns {{AccountingRuleType.NONE}}. Behaviour for products that do carry an
accounting rule is unchanged.
h3. Test
A unit test {{SavingsProductDataTest}} covering a lookup-only instance (all
methods return "no accounting", and Jackson serialization no longer throws) and
an instance with an accounting rule (still evaluated).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)