From 5ed95c27e8ca67f22fa4462fc4f81d3445d70c37 Mon Sep 17 00:00:00 2001 From: Jaanu423 Date: Thu, 16 Jul 2026 15:06:35 +0530 Subject: [PATCH] fix(chequebook,current-account): current-account transaction fixes for cheque-book charge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CurrentAccountTransactionsApiResource.retrieveOne() was calling the savings read-service to fetch a single transaction, which always queries m_savings_account_transaction and can never find a current-account row — crashed with EmptyResultDataAccessException. Added a dedicated CurrentAccountReadPlatformService.retrieveOneTransaction() backed by m_current_account_transaction instead. - CurrentAccountReadPlatformServiceImpl.buildTransactionType() (and the search-transactions endpoint) had no cases for hold (8) / release (9) transaction types, so they displayed as "Deposit"/"Other" instead of "Amount on hold"/"Release Amount". - ChequeBookIssuanceChargeService.deductFromCurrent() never set runningBalanceDerived on the charge-deduction transaction, leaving the Balance column blank for that row in the transactions list. Co-Authored-By: Claude Sonnet 5 --- .../CurrentAccountReadPlatformService.java | 2 + .../ChequeBookIssuanceChargeService.java | 1 + ...CurrentAccountTransactionsApiResource.java | 23 +++--- ...CurrentAccountReadPlatformServiceImpl.java | 82 +++++++++++-------- 4 files changed, 62 insertions(+), 46 deletions(-) diff --git a/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentaccount/service/CurrentAccountReadPlatformService.java b/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentaccount/service/CurrentAccountReadPlatformService.java index a350be55d..18e466395 100644 --- a/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentaccount/service/CurrentAccountReadPlatformService.java +++ b/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentaccount/service/CurrentAccountReadPlatformService.java @@ -26,6 +26,8 @@ public interface CurrentAccountReadPlatformService { Map retrieveOne(Long accountId); + Map retrieveOneTransaction(Long accountId, Long transactionId); + List> retrieveAll(); List> retrieveAll(Integer offset, Integer limit, String accountNo, String clientName); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/chequebook/service/ChequeBookIssuanceChargeService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/chequebook/service/ChequeBookIssuanceChargeService.java index 1ce792e7a..1a27facd6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/chequebook/service/ChequeBookIssuanceChargeService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/chequebook/service/ChequeBookIssuanceChargeService.java @@ -274,6 +274,7 @@ public class ChequeBookIssuanceChargeService { final BigDecimal newBalance = safeBalance(account.getAccountBalanceDerived()).subtract(chargeAmount); final CurrentAccountTransaction txn = CurrentAccountTransaction.chargeDeduction(account, office, chargeAmount, today); + txn.setRunningBalanceDerived(newBalance); this.currentAccountTransactionRepository.saveAndFlush(txn); account.setAccountBalanceDerived(newBalance); this.currentAccountRepository.saveAndFlush(account); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/CurrentAccountTransactionsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/CurrentAccountTransactionsApiResource.java index 796562228..5addf6078 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/CurrentAccountTransactionsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/CurrentAccountTransactionsApiResource.java @@ -43,19 +43,16 @@ import org.apache.commons.lang3.StringUtils; import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; -import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException; -import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountNotFoundException; +import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountReadPlatformService; import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData; import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadService; import org.apache.fineract.portfolio.savings.DepositsApiConstants; -import org.apache.fineract.portfolio.savings.DepositAccountType; import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData; -import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; @@ -70,8 +67,7 @@ public class CurrentAccountTransactionsApiResource { private final PlatformSecurityContext context; private final DefaultToApiJsonSerializer toApiJsonSerializer; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; - private final ApiRequestParameterHelper apiRequestParameterHelper; - private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; + private final CurrentAccountReadPlatformService currentAccountReadPlatformService; private final PaymentTypeReadService paymentTypeReadService; private final JdbcTemplate jdbcTemplate; @@ -128,11 +124,11 @@ public class CurrentAccountTransactionsApiResource { @PathParam("transactionId") final Long transactionId, @Context final UriInfo uriInfo) { context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME); - final SavingsAccountTransactionData transactionData = savingsAccountReadPlatformService.retrieveSavingsTransaction(accountId, - transactionId, DepositAccountType.CURRENT_DEPOSIT); - final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return toApiJsonSerializer.serialize(settings, transactionData, - SavingsApiSetConstants.SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS); + // current accounts live in their own table (m_current_account_transaction), not m_savings_account_transaction — + // the savings read-service's retrieveSavingsTransaction() can never find a row here regardless of + // DepositAccountType, since it always queries the savings schema. + final Map transactionData = currentAccountReadPlatformService.retrieveOneTransaction(accountId, transactionId); + return GSON.toJson(transactionData); } @POST @@ -252,8 +248,11 @@ public class CurrentAccountTransactionsApiResource { txnType.put("id", typeEnum); txnType.put("deposit", typeEnum == 1); txnType.put("withdrawal", typeEnum == 2); + txnType.put("amountHold", typeEnum == 8); + txnType.put("amountRelease", typeEnum == 9); txnType.put("value", typeEnum == 1 ? "Deposit" : typeEnum == 2 ? "Withdrawal" - : typeEnum == 7 ? "Charge Deduction" : "Other"); + : typeEnum == 7 ? "Charge Deduction" : typeEnum == 8 ? "Amount on hold" + : typeEnum == 9 ? "Release Amount" : "Other"); row.put("transactionType", txnType); final java.sql.Date d = rs.getDate("transaction_date"); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/CurrentAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/CurrentAccountReadPlatformServiceImpl.java index 42752b631..de44fac30 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/CurrentAccountReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/CurrentAccountReadPlatformServiceImpl.java @@ -113,44 +113,56 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead }, accountId); } + private static final String TRANSACTION_SELECT = + "SELECT t.id, t.transaction_type_enum, t.transaction_date, t.amount, " + + "t.running_balance_derived, t.is_reversed, t.is_reversal, t.ref_no, t.remarks, " + + "t.submitted_on_date " + + "FROM m_current_account_transaction t "; + private List> retrieveTransactions(Long accountId) { - final String sql = - "SELECT t.id, t.transaction_type_enum, t.transaction_date, t.amount, " - + "t.running_balance_derived, t.is_reversed, t.is_reversal, t.ref_no, t.remarks, " - + "t.submitted_on_date " - + "FROM m_current_account_transaction t " - + "WHERE t.current_account_id = ? " - + "ORDER BY t.transaction_date ASC, t.id ASC"; + final String sql = TRANSACTION_SELECT + "WHERE t.current_account_id = ? ORDER BY t.transaction_date ASC, t.id ASC"; + return jdbcTemplate.query(sql, (rs, rowNum) -> mapTransactionRow(rs), accountId); + } - return jdbcTemplate.query(sql, (rs, rowNum) -> { - final Map t = new LinkedHashMap<>(); - t.put("id", rs.getLong("id")); + /** Backs {@code GET /currentaccounts/{accountId}/transactions/{transactionId}} — a single transaction's detail. */ + @Override + public Map retrieveOneTransaction(Long accountId, Long transactionId) { + final String sql = TRANSACTION_SELECT + "WHERE t.current_account_id = ? AND t.id = ?"; + final List> rows = jdbcTemplate.query(sql, (rs, rowNum) -> mapTransactionRow(rs), accountId, transactionId); + if (rows.isEmpty()) { + throw new CurrentAccountNotFoundException(accountId); + } + return rows.get(0); + } - final int txnTypeEnum = rs.getInt("transaction_type_enum"); - t.put("transactionType", buildTransactionType(txnTypeEnum)); + private Map mapTransactionRow(final java.sql.ResultSet rs) throws SQLException { + final Map t = new LinkedHashMap<>(); + t.put("id", rs.getLong("id")); - final java.time.LocalDate txnDate = rs.getObject("transaction_date", java.time.LocalDate.class); - if (txnDate != null) { - t.put("date", new int[]{txnDate.getYear(), txnDate.getMonthValue(), txnDate.getDayOfMonth()}); - } else { - t.put("date", null); - } + final int txnTypeEnum = rs.getInt("transaction_type_enum"); + t.put("transactionType", buildTransactionType(txnTypeEnum)); - t.put("amount", rs.getBigDecimal("amount")); - t.put("runningBalance", rs.getBigDecimal("running_balance_derived")); - t.put("reversed", rs.getBoolean("is_reversed")); - t.put("isReversal", rs.getBoolean("is_reversal")); - t.put("refNo", rs.getString("ref_no")); - t.put("remarks", rs.getString("remarks")); + final java.time.LocalDate txnDate = rs.getObject("transaction_date", java.time.LocalDate.class); + if (txnDate != null) { + t.put("date", new int[]{txnDate.getYear(), txnDate.getMonthValue(), txnDate.getDayOfMonth()}); + } else { + t.put("date", null); + } - final java.time.LocalDate submittedOn = rs.getObject("submitted_on_date", java.time.LocalDate.class); - if (submittedOn != null) { - t.put("submittedOnDate", new int[]{submittedOn.getYear(), submittedOn.getMonthValue(), submittedOn.getDayOfMonth()}); - } else { - t.put("submittedOnDate", null); - } - return t; - }, accountId); + t.put("amount", rs.getBigDecimal("amount")); + t.put("runningBalance", rs.getBigDecimal("running_balance_derived")); + t.put("reversed", rs.getBoolean("is_reversed")); + t.put("isReversal", rs.getBoolean("is_reversal")); + t.put("refNo", rs.getString("ref_no")); + t.put("remarks", rs.getString("remarks")); + + final java.time.LocalDate submittedOn = rs.getObject("submitted_on_date", java.time.LocalDate.class); + if (submittedOn != null) { + t.put("submittedOnDate", new int[]{submittedOn.getYear(), submittedOn.getMonthValue(), submittedOn.getDayOfMonth()}); + } else { + t.put("submittedOnDate", null); + } + return t; } private Map buildTransactionType(int typeEnum) { @@ -169,8 +181,8 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead tt.put("writtenoff", false); tt.put("overdraftFee", false); tt.put("escheat", false); - tt.put("amountHold", false); - tt.put("amountRelease", false); + tt.put("amountHold", typeEnum == 8); + tt.put("amountRelease", typeEnum == 9); tt.put("accrual", false); switch (typeEnum) { case 1 -> { tt.put("code", "savingsTransactionType.deposit"); tt.put("value", "Deposit"); } @@ -178,6 +190,8 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead case 3 -> { tt.put("code", "savingsTransactionType.interestPosting"); tt.put("value", "Interest Posting"); } case 4 -> { tt.put("code", "savingsTransactionType.feeDeduction"); tt.put("value", "Fee Deduction"); } case 7 -> { tt.put("code", "savingsTransactionType.payCharge"); tt.put("value", "Charge Deduction"); } + case 8 -> { tt.put("code", "savingsTransactionType.onHold"); tt.put("value", "Amount on hold"); } + case 9 -> { tt.put("code", "savingsTransactionType.release"); tt.put("value", "Release Amount"); } default -> { tt.put("code", "savingsTransactionType.deposit"); tt.put("value", "Deposit"); } } return tt;