Farooq Ayoade created FINERACT-2689:
---------------------------------------

             Summary: Closing (or posting interest on) a withholding-tax 
deposit account fails with a 500 NullPointerException whenever the total 
interest posted on the account is zero. The withholding-tax step dereferences a 
null amount:
                 Key: FINERACT-2689
                 URL: https://issues.apache.org/jira/browse/FINERACT-2689
             Project: Apache Fineract
          Issue Type: Bug
            Reporter: Farooq Ayoade


java.lang.NullPointerException: Cannot invoke 
"java.math.BigDecimal.compareTo(java.math.BigDecimal)"
  because "amount" is null
    at ...SavingsAccount.createWithHoldTransaction(SavingsAccount.java:719)
    at ...SavingsAccount.applyWithholdTaxForDepositAccounts(...)
    at ...FixedDepositAccount.postPreMaturityInterest(...)
    at ...DepositAccountDomainServiceJpa.handleFDAccountPreMatureClosure(...)

 

The most common trigger is premature-closing a short-term fixed deposit whose 
{*}pre-closure penalty rate exceeds the chart rate{*}: the effective interest 
is clamped to zero, so no interest-posting transaction is created, and the 
withholding-tax pass then runs with a null interest total.
h3. Steps to reproduce
 # Create a Fixed Deposit product with *withholding tax enabled* (assign a tax 
group) and a pre-closure penalty configured so the penalty rate is *greater 
than or equal to* the applicable chart rate (e.g. chart 5%, pre-closure penalty 
20%, "Till Premature Withdrawal").
 # Open and activate an FD account ({{{}withHoldTax = true{}}}, tax group 
inherited).
 # Premature-close it a few days into the term: {{POST 
/fixeddepositaccounts/\{id}?command=prematureClose}} with 
{{onAccountClosureId}} = withdraw (100) or transfer-to-savings (200).
 # *500* {{{}NullPointerException … because "amount" is null{}}}. The command 
rolls back; the account cannot be closed.

Because the penalty (20%) exceeds the chart rate (5%), the effective premature 
rate clamps to 0 
({{{}FixedDepositAccount.getEffectiveInterestRateAsFraction{}}} → {{{}max(rate 
− penalty, 0){}}}), so {{postPreMaturityInterest}} posts *no* interest 
transaction and the interest total is null at the withholding step.

 
h3. Root cause

{{FixedDepositAccount.postPreMaturityInterest(...)}} (and the normal 
{{postInterest}} path) calls 
{{{}SavingsAccount.applyWithholdTaxForDepositAccounts(...){}}}, which computes:
 
!http://localhost:63342/markdownPreview/1258363713/custom-guide/core-tickets!
 
{{final BigDecimal totalInterestPosted =
    
this.savingsAccountTransactionSummaryWrapper.calculateTotalInterestPosted(this.currency,
 this.transactions);}}

{{SavingsAccountTransactionSummaryWrapper.calculateTotalInterestPosted(...)}} 
returns {{total.getAmountDefaultedToNullIfZero()}} — i.e. *{{null}}* when there 
are no (net) interest-posting transactions. That null is passed straight into 
the withholding helpers, both of which guard only on the tax group and then 
dereference {{{}amount{}}}:
 
!http://localhost:63342/markdownPreview/1258363713/custom-guide/core-tickets!
 
{{// SavingsAccount.createWithHoldTransaction(amount, date, 
backdatedTxnsAllowedTill)
if (this.taxGroup != null && amount.compareTo(BigDecimal.ZERO) > 0) {   // NPE: 
amount == null

// SavingsAccount.updateWithHoldTransaction(amount, withholdTransaction)
if (this.taxGroup != null && amount.compareTo(BigDecimal.ZERO) > 0) {   // same 
flaw}}

For a withholding-tax account the tax group is non-null, so the first clause 
passes and {{amount.compareTo}} throws. The bug surfaces on any withholding-tax 
deposit account whose net interest posted is zero — premature close where the 
penalty wipes out interest is the easy real-world path, but it also applies to 
a zero-interest posting cycle.
h3. Fix

Null-guard {{amount}} in both {{SavingsAccount.createWithHoldTransaction(...)}} 
and {{SavingsAccount.updateWithHoldTransaction(...)}} — a null (or 
non-positive) interest total means there is nothing to withhold tax on, so both 
should no-op and return {{{}false{}}}. No behavior change when interest is 
actually posted: a non-null positive amount takes the exact same path as before.
h3. Impact

Withholding-tax fixed/recurring deposit accounts can be premature-closed (and 
post interest) when net interest is zero, instead of 500-ing and rolling back. 
No change for accounts with a positive interest total or with no tax group.



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

Reply via email to