fix(chequebook,current-account): current-account transaction fixes for cheque-book charge
- 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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
fde670e644
commit
5ed95c27e8
+2
@@ -26,6 +26,8 @@ public interface CurrentAccountReadPlatformService {
|
||||
|
||||
Map<String, Object> retrieveOne(Long accountId);
|
||||
|
||||
Map<String, Object> retrieveOneTransaction(Long accountId, Long transactionId);
|
||||
|
||||
List<Map<String, Object>> retrieveAll();
|
||||
|
||||
List<Map<String, Object>> retrieveAll(Integer offset, Integer limit, String accountNo, String clientName);
|
||||
|
||||
+1
@@ -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);
|
||||
|
||||
+11
-12
@@ -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<SavingsAccountTransactionData> 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<String, Object> 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");
|
||||
|
||||
+48
-34
@@ -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<Map<String, Object>> 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<String, Object> t = new LinkedHashMap<>();
|
||||
t.put("id", rs.getLong("id"));
|
||||
/** Backs {@code GET /currentaccounts/{accountId}/transactions/{transactionId}} — a single transaction's detail. */
|
||||
@Override
|
||||
public Map<String, Object> retrieveOneTransaction(Long accountId, Long transactionId) {
|
||||
final String sql = TRANSACTION_SELECT + "WHERE t.current_account_id = ? AND t.id = ?";
|
||||
final List<Map<String, Object>> 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<String, Object> mapTransactionRow(final java.sql.ResultSet rs) throws SQLException {
|
||||
final Map<String, Object> 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<String, Object> 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;
|
||||
|
||||
Reference in New Issue
Block a user