feat(cashier): add morning transfer validation & fix denomination SQL
- validateCashierHasMorningTransfer(): blocks all txns when no today's cash transfer in m_cash_transfer - validatePreviousDayClosed(): blocks when previous business day unsettled - Both validations called on deposit AND withdrawal in Savings, Current, FD, RD services - cashier-day-status endpoint now returns morningTransferReceived field - getCashierTotalCash SQL fixed: dm.denomination_value -> dm.denomination Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
eae5b03d7e
commit
656a0b4f13
+83
@@ -14,6 +14,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.denominationmaster.service.DenominationBreakdownWriter;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -25,6 +26,7 @@ public class CashReportApiResource {
|
|||||||
|
|
||||||
private final JdbcTemplate jdbcTemplate;
|
private final JdbcTemplate jdbcTemplate;
|
||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
|
private final DenominationBreakdownWriter denominationBreakdownWriter;
|
||||||
private final Gson gson = new Gson();
|
private final Gson gson = new Gson();
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@@ -182,6 +184,87 @@ public class CashReportApiResource {
|
|||||||
return gson.toJson(combined);
|
return gson.toJson(combined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /cashreports/cashier-day-status
|
||||||
|
* Returns whether the authenticated cashier has an unsettled previous business day.
|
||||||
|
* Used by the UI to show a warning banner and hard-block withdrawals.
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("cashier-day-status")
|
||||||
|
public String cashierDayStatus() {
|
||||||
|
final Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
Long appUserId;
|
||||||
|
try {
|
||||||
|
appUserId = context.authenticatedUser().getId();
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.put("cashierId", null);
|
||||||
|
result.put("previousDayUnsettled", false);
|
||||||
|
return gson.toJson(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Long cashierId;
|
||||||
|
try {
|
||||||
|
cashierId = jdbcTemplate.queryForObject(
|
||||||
|
"SELECT mc.id FROM m_cashiers mc JOIN m_staff ms ON ms.id = mc.staff_id "
|
||||||
|
+ "JOIN m_appuser au ON au.staff_id = ms.id WHERE au.id = ?",
|
||||||
|
Long.class, appUserId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Not a cashier user — no restriction applies
|
||||||
|
result.put("cashierId", null);
|
||||||
|
result.put("previousDayUnsettled", false);
|
||||||
|
return gson.toJson(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String today = LocalDate.now().toString();
|
||||||
|
|
||||||
|
// Find the earliest transfer date before today for which no settlement exists on or after that date
|
||||||
|
final String sql = "SELECT MIN(ct.transfer_date) AS earliest_unsettled "
|
||||||
|
+ "FROM m_cash_transfer ct "
|
||||||
|
+ "WHERE ct.to_cashier_id = ? "
|
||||||
|
+ " AND ct.transfer_date < CAST(? AS DATE) "
|
||||||
|
+ " AND (ct.is_reversed IS NULL OR ct.is_reversed = false) "
|
||||||
|
+ " AND NOT EXISTS ("
|
||||||
|
+ " SELECT 1 FROM m_cash_settlement cs "
|
||||||
|
+ " WHERE cs.from_cashier_id = ? "
|
||||||
|
+ " AND cs.settlement_date >= ct.transfer_date "
|
||||||
|
+ " AND (cs.is_reversed IS NULL OR cs.is_reversed = false)"
|
||||||
|
+ " )";
|
||||||
|
|
||||||
|
java.sql.Date earliestDate = null;
|
||||||
|
try {
|
||||||
|
earliestDate = jdbcTemplate.queryForObject(sql, java.sql.Date.class, cashierId, today, cashierId);
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
|
||||||
|
final boolean unsettled = earliestDate != null;
|
||||||
|
final String unsettledSince = earliestDate != null ? earliestDate.toString() : null;
|
||||||
|
|
||||||
|
BigDecimal pendingAmount = BigDecimal.ZERO;
|
||||||
|
if (unsettled) {
|
||||||
|
try {
|
||||||
|
pendingAmount = denominationBreakdownWriter.getCashierTotalCash(cashierId);
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether today's morning cash transfer has been received
|
||||||
|
final String morningTransferSql = "SELECT COUNT(*) FROM m_cash_transfer ct "
|
||||||
|
+ "WHERE ct.to_cashier_id = ? "
|
||||||
|
+ " AND ct.transfer_date = CAST(? AS DATE) "
|
||||||
|
+ " AND (ct.is_reversed IS NULL OR ct.is_reversed = false)";
|
||||||
|
Integer todayTransferCount = 0;
|
||||||
|
try {
|
||||||
|
todayTransferCount = jdbcTemplate.queryForObject(morningTransferSql, Integer.class, cashierId, today);
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
final boolean morningTransferReceived = todayTransferCount != null && todayTransferCount > 0;
|
||||||
|
|
||||||
|
result.put("cashierId", cashierId);
|
||||||
|
result.put("previousDayUnsettled", unsettled);
|
||||||
|
result.put("unsettledSince", unsettledSince);
|
||||||
|
result.put("pendingAmount", pendingAmount);
|
||||||
|
result.put("morningTransferReceived", morningTransferReceived);
|
||||||
|
return gson.toJson(result);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /cashreports/cashier-position?cashierId=X&asOfDate=yyyy-MM-dd
|
* GET /cashreports/cashier-position?cashierId=X&asOfDate=yyyy-MM-dd
|
||||||
* Returns denomination-wise net position per cashier grouped by denomination_id+kind.
|
* Returns denomination-wise net position per cashier grouped by denomination_id+kind.
|
||||||
|
|||||||
+170
@@ -143,6 +143,176 @@ public class DenominationBreakdownWriter {
|
|||||||
validateBreakdown(command, expectedAmount, "TRANSACTION");
|
validateBreakdown(command, expectedAmount, "TRANSACTION");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks any transaction (deposit or withdrawal) when the cashier has an unsettled
|
||||||
|
* previous business day — i.e., a morning cash transfer exists on a date before today
|
||||||
|
* with no corresponding settlement on or after that date.
|
||||||
|
* Admin / non-cashier users are silently skipped.
|
||||||
|
*/
|
||||||
|
public void validatePreviousDayClosed() {
|
||||||
|
Long cashierId = null;
|
||||||
|
try {
|
||||||
|
final AppUser user = context.authenticatedUser();
|
||||||
|
cashierId = jdbcTemplate.queryForObject(
|
||||||
|
"SELECT mc.id FROM m_cashiers mc JOIN m_staff ms ON ms.id = mc.staff_id "
|
||||||
|
+ "JOIN m_appuser au ON au.staff_id = ms.id WHERE au.id = ?",
|
||||||
|
Long.class, user.getId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return; // not a cashier user — skip
|
||||||
|
}
|
||||||
|
if (cashierId == null) return;
|
||||||
|
|
||||||
|
final String today = java.time.LocalDate.now().toString();
|
||||||
|
final String sql = "SELECT COUNT(*) FROM m_cash_transfer ct "
|
||||||
|
+ "WHERE ct.to_cashier_id = ? "
|
||||||
|
+ " AND ct.transfer_date < CAST(? AS DATE) "
|
||||||
|
+ " AND (ct.is_reversed IS NULL OR ct.is_reversed = false) "
|
||||||
|
+ " AND NOT EXISTS ("
|
||||||
|
+ " SELECT 1 FROM m_cash_settlement cs "
|
||||||
|
+ " WHERE cs.from_cashier_id = ? "
|
||||||
|
+ " AND cs.settlement_date >= ct.transfer_date "
|
||||||
|
+ " AND (cs.is_reversed IS NULL OR cs.is_reversed = false)"
|
||||||
|
+ " )";
|
||||||
|
|
||||||
|
final Integer count = jdbcTemplate.queryForObject(sql, Integer.class, cashierId, today, cashierId);
|
||||||
|
if (count != null && count > 0) {
|
||||||
|
throw new PlatformDataIntegrityException(
|
||||||
|
"error.msg.cashier.day.not.closed",
|
||||||
|
"Previous business day has not been closed. Please complete Cash Settlement and "
|
||||||
|
+ "Reconciliation before processing any new deposits or withdrawals.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks any transaction (deposit or withdrawal) when the cashier has not received
|
||||||
|
* a morning cash transfer for today (transfer_date = current date).
|
||||||
|
* Admin / non-cashier users are silently skipped.
|
||||||
|
*/
|
||||||
|
public void validateCashierHasMorningTransfer() {
|
||||||
|
Long cashierId = null;
|
||||||
|
try {
|
||||||
|
final AppUser user = context.authenticatedUser();
|
||||||
|
cashierId = jdbcTemplate.queryForObject(
|
||||||
|
"SELECT mc.id FROM m_cashiers mc JOIN m_staff ms ON ms.id = mc.staff_id "
|
||||||
|
+ "JOIN m_appuser au ON au.staff_id = ms.id WHERE au.id = ?",
|
||||||
|
Long.class, user.getId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return; // not a cashier user — skip
|
||||||
|
}
|
||||||
|
if (cashierId == null) return;
|
||||||
|
|
||||||
|
final String today = java.time.LocalDate.now().toString();
|
||||||
|
final String sql = "SELECT COUNT(*) FROM m_cash_transfer ct "
|
||||||
|
+ "WHERE ct.to_cashier_id = ? "
|
||||||
|
+ " AND ct.transfer_date = CAST(? AS DATE) "
|
||||||
|
+ " AND (ct.is_reversed IS NULL OR ct.is_reversed = false)";
|
||||||
|
|
||||||
|
final Integer count = jdbcTemplate.queryForObject(sql, Integer.class, cashierId, today);
|
||||||
|
if (count == null || count == 0) {
|
||||||
|
throw new PlatformDataIntegrityException(
|
||||||
|
"error.msg.cashier.no.morning.transfer",
|
||||||
|
"No cash transfer received for today. Please request a morning cash transfer "
|
||||||
|
+ "from the vault before processing any deposits or withdrawals.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the cashier's total available cash (sum of denomination_value × net_count
|
||||||
|
* across all denominations) is at least {@code requiredAmount}.
|
||||||
|
* <p>
|
||||||
|
* This is a holistic guard that fires even when no denomination breakdown is supplied —
|
||||||
|
* preventing any withdrawal when the cashier has received no morning cash transfer.
|
||||||
|
* Admin / non-cashier users are silently skipped.
|
||||||
|
*
|
||||||
|
* @param requiredAmount the withdrawal amount that must be covered by the cashier's stock
|
||||||
|
*/
|
||||||
|
public void validateCashierHasTotal(final BigDecimal requiredAmount) {
|
||||||
|
if (requiredAmount == null || requiredAmount.compareTo(BigDecimal.ZERO) <= 0) return;
|
||||||
|
|
||||||
|
Long cashierId = null;
|
||||||
|
try {
|
||||||
|
final AppUser user = context.authenticatedUser();
|
||||||
|
cashierId = jdbcTemplate.queryForObject(
|
||||||
|
"SELECT mc.id FROM m_cashiers mc JOIN m_staff ms ON ms.id = mc.staff_id "
|
||||||
|
+ "JOIN m_appuser au ON au.staff_id = ms.id WHERE au.id = ?",
|
||||||
|
Long.class, user.getId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return; // not a cashier user — skip
|
||||||
|
}
|
||||||
|
if (cashierId == null) return;
|
||||||
|
|
||||||
|
final BigDecimal available = getCashierTotalCash(cashierId);
|
||||||
|
if (available.compareTo(requiredAmount) < 0) {
|
||||||
|
throw new PlatformDataIntegrityException(
|
||||||
|
"error.msg.cashier.stock.insufficient",
|
||||||
|
"Cashier does not have sufficient cash. Available: ₹" + available
|
||||||
|
+ ", Required: ₹" + requiredAmount
|
||||||
|
+ ". Please request a cash transfer from the vault before processing withdrawals.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total rupee value of all denominations currently held by the cashier
|
||||||
|
* (vault transfers in + deposits received + exchanges received − settlements out
|
||||||
|
* − inter-cashier out − withdrawals paid − exchanges given).
|
||||||
|
*/
|
||||||
|
public BigDecimal getCashierTotalCash(final Long cashierId) {
|
||||||
|
final String sql = "SELECT COALESCE(SUM(dm.denomination * t.net_count), 0) "
|
||||||
|
+ "FROM ("
|
||||||
|
+ " SELECT denomination_id, SUM(net_count) AS net_count FROM ("
|
||||||
|
+ " SELECT ctd.denomination_id, CAST(SUM(ctd.count) AS BIGINT) AS net_count"
|
||||||
|
+ " FROM m_cash_transfer_detail ctd JOIN m_cash_transfer ct ON ct.id = ctd.transfer_id"
|
||||||
|
+ " WHERE ct.to_cashier_id = ? AND (ct.is_reversed IS NULL OR ct.is_reversed = false)"
|
||||||
|
+ " GROUP BY ctd.denomination_id"
|
||||||
|
+ " UNION ALL"
|
||||||
|
+ " SELECT icd.denomination_id, CAST(SUM(icd.count) AS BIGINT)"
|
||||||
|
+ " FROM m_cash_inter_cashier_detail icd JOIN m_cash_inter_cashier ic ON ic.id = icd.inter_cashier_id"
|
||||||
|
+ " WHERE ic.to_cashier_id = ? AND (ic.is_reversed IS NULL OR ic.is_reversed = false)"
|
||||||
|
+ " GROUP BY icd.denomination_id"
|
||||||
|
+ " UNION ALL"
|
||||||
|
+ " SELECT std.denomination_id, CAST(SUM(std.count) AS BIGINT)"
|
||||||
|
+ " FROM m_savings_txn_denomination std"
|
||||||
|
+ " WHERE std.txn_type = 'DEPOSIT'"
|
||||||
|
+ " AND std.officer_id IN (SELECT au.id FROM m_appuser au"
|
||||||
|
+ " JOIN m_cashiers mc ON mc.staff_id = au.staff_id WHERE mc.id = ?)"
|
||||||
|
+ " GROUP BY std.denomination_id"
|
||||||
|
+ " UNION ALL"
|
||||||
|
+ " SELECT ced.denomination_id, CAST(SUM(ced.count) AS BIGINT)"
|
||||||
|
+ " FROM m_cash_exchange_detail ced JOIN m_cash_exchange ce ON ce.id = ced.exchange_id"
|
||||||
|
+ " WHERE ce.cashier_id = ? AND ced.direction = 'RECEIVED'"
|
||||||
|
+ " AND (ce.is_reversed IS NULL OR ce.is_reversed = false)"
|
||||||
|
+ " GROUP BY ced.denomination_id"
|
||||||
|
+ " UNION ALL"
|
||||||
|
+ " SELECT csd.denomination_id, CAST(-SUM(csd.count) AS BIGINT)"
|
||||||
|
+ " FROM m_cash_settlement_detail csd JOIN m_cash_settlement cs ON cs.id = csd.settlement_id"
|
||||||
|
+ " WHERE cs.from_cashier_id = ? AND (cs.is_reversed IS NULL OR cs.is_reversed = false)"
|
||||||
|
+ " GROUP BY csd.denomination_id"
|
||||||
|
+ " UNION ALL"
|
||||||
|
+ " SELECT icd.denomination_id, CAST(-SUM(icd.count) AS BIGINT)"
|
||||||
|
+ " FROM m_cash_inter_cashier_detail icd JOIN m_cash_inter_cashier ic ON ic.id = icd.inter_cashier_id"
|
||||||
|
+ " WHERE ic.from_cashier_id = ? AND (ic.is_reversed IS NULL OR ic.is_reversed = false)"
|
||||||
|
+ " GROUP BY icd.denomination_id"
|
||||||
|
+ " UNION ALL"
|
||||||
|
+ " SELECT std.denomination_id, CAST(-SUM(std.count) AS BIGINT)"
|
||||||
|
+ " FROM m_savings_txn_denomination std"
|
||||||
|
+ " WHERE std.txn_type = 'WITHDRAWAL'"
|
||||||
|
+ " AND std.officer_id IN (SELECT au.id FROM m_appuser au"
|
||||||
|
+ " JOIN m_cashiers mc ON mc.staff_id = au.staff_id WHERE mc.id = ?)"
|
||||||
|
+ " GROUP BY std.denomination_id"
|
||||||
|
+ " UNION ALL"
|
||||||
|
+ " SELECT ced.denomination_id, CAST(-SUM(ced.count) AS BIGINT)"
|
||||||
|
+ " FROM m_cash_exchange_detail ced JOIN m_cash_exchange ce ON ce.id = ced.exchange_id"
|
||||||
|
+ " WHERE ce.cashier_id = ? AND ced.direction = 'GIVEN'"
|
||||||
|
+ " AND (ce.is_reversed IS NULL OR ce.is_reversed = false)"
|
||||||
|
+ " GROUP BY ced.denomination_id"
|
||||||
|
+ " ) raw GROUP BY denomination_id HAVING SUM(net_count) > 0"
|
||||||
|
+ ") t JOIN m_denomination_master dm ON dm.id = t.denomination_id";
|
||||||
|
|
||||||
|
final BigDecimal total = jdbcTemplate.queryForObject(sql, BigDecimal.class,
|
||||||
|
cashierId, cashierId, cashierId, cashierId, cashierId, cashierId, cashierId, cashierId);
|
||||||
|
return total != null ? total : BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates that the cashier currently holds enough of each denomination
|
* Validates that the cashier currently holds enough of each denomination
|
||||||
* in the withdrawal breakdown. Auto-detects the cashier from the authenticated
|
* in the withdrawal breakdown. Auto-detects the cashier from the authenticated
|
||||||
|
|||||||
+6
-1
@@ -76,6 +76,8 @@ public class CurrentAccountTransactionWritePlatformServiceImpl implements Curren
|
|||||||
|
|
||||||
final BigDecimal amount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
final BigDecimal amount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||||
validatePositiveAmount(amount, accountId, "deposit");
|
validatePositiveAmount(amount, accountId, "deposit");
|
||||||
|
this.denominationBreakdownWriter.validatePreviousDayClosed();
|
||||||
|
this.denominationBreakdownWriter.validateCashierHasMorningTransfer();
|
||||||
this.denominationBreakdownWriter.validateBreakdown(command, amount, "DEPOSIT");
|
this.denominationBreakdownWriter.validateBreakdown(command, amount, "DEPOSIT");
|
||||||
|
|
||||||
final String refNo = command.stringValueOfParameterNamed("accountNumber");
|
final String refNo = command.stringValueOfParameterNamed("accountNumber");
|
||||||
@@ -143,7 +145,10 @@ public class CurrentAccountTransactionWritePlatformServiceImpl implements Curren
|
|||||||
account.setTotalWithdrawalsDerived(safeBalance(account.getTotalWithdrawalsDerived()).add(amount));
|
account.setTotalWithdrawalsDerived(safeBalance(account.getTotalWithdrawalsDerived()).add(amount));
|
||||||
currentAccountRepository.save(account);
|
currentAccountRepository.save(account);
|
||||||
|
|
||||||
// Validate denomination stock availability then validate totals before saving breakdown
|
// Validate previous day closed, morning transfer received, total stock, per-denomination, breakdown totals
|
||||||
|
this.denominationBreakdownWriter.validatePreviousDayClosed();
|
||||||
|
this.denominationBreakdownWriter.validateCashierHasMorningTransfer();
|
||||||
|
this.denominationBreakdownWriter.validateCashierHasTotal(amount);
|
||||||
this.denominationBreakdownWriter.validateWithdrawalStock(command);
|
this.denominationBreakdownWriter.validateWithdrawalStock(command);
|
||||||
this.denominationBreakdownWriter.validateBreakdown(command, amount, "WITHDRAWAL");
|
this.denominationBreakdownWriter.validateBreakdown(command, amount, "WITHDRAWAL");
|
||||||
this.denominationBreakdownWriter.save(command, txn.getId(), account.getId(), account.getAccountNo(),
|
this.denominationBreakdownWriter.save(command, txn.getId(), account.getId(), account.getAccountNo(),
|
||||||
|
|||||||
+8
-1
@@ -362,6 +362,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
|
|||||||
|
|
||||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||||
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
|
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
|
||||||
|
this.denominationBreakdownWriter.validatePreviousDayClosed();
|
||||||
|
this.denominationBreakdownWriter.validateCashierHasMorningTransfer();
|
||||||
this.denominationBreakdownWriter.validateBreakdown(command, transactionAmount, "DEPOSIT");
|
this.denominationBreakdownWriter.validateBreakdown(command, transactionAmount, "DEPOSIT");
|
||||||
final SavingsAccountTransaction deposit = this.depositAccountDomainService.handleFDDeposit(account, fmt, transactionDate,
|
final SavingsAccountTransaction deposit = this.depositAccountDomainService.handleFDDeposit(account, fmt, transactionDate,
|
||||||
transactionAmount, paymentDetail);
|
transactionAmount, paymentDetail);
|
||||||
@@ -424,6 +426,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
|
|||||||
|
|
||||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||||
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
|
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
|
||||||
|
this.denominationBreakdownWriter.validatePreviousDayClosed();
|
||||||
|
this.denominationBreakdownWriter.validateCashierHasMorningTransfer();
|
||||||
this.denominationBreakdownWriter.validateBreakdown(command, transactionAmount, "DEPOSIT");
|
this.denominationBreakdownWriter.validateBreakdown(command, transactionAmount, "DEPOSIT");
|
||||||
final SavingsAccountTransaction deposit = this.depositAccountDomainService.handleRDDeposit(account, fmt, transactionDate,
|
final SavingsAccountTransaction deposit = this.depositAccountDomainService.handleRDDeposit(account, fmt, transactionDate,
|
||||||
transactionAmount, paymentDetail, isRegularTransaction);
|
transactionAmount, paymentDetail, isRegularTransaction);
|
||||||
@@ -472,7 +476,10 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
|
|||||||
final SavingsAccountTransaction withdrawal = this.depositAccountDomainService.handleWithdrawal(account, fmt, transactionDate,
|
final SavingsAccountTransaction withdrawal = this.depositAccountDomainService.handleWithdrawal(account, fmt, transactionDate,
|
||||||
transactionAmount, paymentDetail, true, isRegularTransaction);
|
transactionAmount, paymentDetail, true, isRegularTransaction);
|
||||||
|
|
||||||
// Validate denomination stock availability then validate totals before saving breakdown
|
// Validate previous day closed, morning transfer received, total stock, per-denomination, breakdown totals
|
||||||
|
this.denominationBreakdownWriter.validatePreviousDayClosed();
|
||||||
|
this.denominationBreakdownWriter.validateCashierHasMorningTransfer();
|
||||||
|
this.denominationBreakdownWriter.validateCashierHasTotal(transactionAmount);
|
||||||
this.denominationBreakdownWriter.validateWithdrawalStock(command);
|
this.denominationBreakdownWriter.validateWithdrawalStock(command);
|
||||||
this.denominationBreakdownWriter.validateBreakdown(command, transactionAmount, "WITHDRAWAL");
|
this.denominationBreakdownWriter.validateBreakdown(command, transactionAmount, "WITHDRAWAL");
|
||||||
this.denominationBreakdownWriter.save(command, withdrawal.getId(), account.getId(), account.getAccountNumber(),
|
this.denominationBreakdownWriter.save(command, withdrawal.getId(), account.getId(), account.getAccountNumber(),
|
||||||
|
|||||||
+6
-1
@@ -328,6 +328,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
|||||||
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||||
|
|
||||||
this.savingsAccountTransactionDataValidator.validateTransactionWithPivotDate(transactionDate, account);
|
this.savingsAccountTransactionDataValidator.validateTransactionWithPivotDate(transactionDate, account);
|
||||||
|
this.denominationBreakdownWriter.validatePreviousDayClosed();
|
||||||
|
this.denominationBreakdownWriter.validateCashierHasMorningTransfer();
|
||||||
this.denominationBreakdownWriter.validateBreakdown(command, transactionAmount, "DEPOSIT");
|
this.denominationBreakdownWriter.validateBreakdown(command, transactionAmount, "DEPOSIT");
|
||||||
|
|
||||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||||
@@ -424,7 +426,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
|||||||
gsimRepository.save(gsim);
|
gsimRepository.save(gsim);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate denomination stock availability then validate totals before saving breakdown
|
// Validate previous day closed, morning transfer received, total stock, per-denomination, breakdown totals
|
||||||
|
this.denominationBreakdownWriter.validatePreviousDayClosed();
|
||||||
|
this.denominationBreakdownWriter.validateCashierHasMorningTransfer();
|
||||||
|
this.denominationBreakdownWriter.validateCashierHasTotal(transactionAmount);
|
||||||
this.denominationBreakdownWriter.validateWithdrawalStock(command);
|
this.denominationBreakdownWriter.validateWithdrawalStock(command);
|
||||||
this.denominationBreakdownWriter.validateBreakdown(command, transactionAmount, "WITHDRAWAL");
|
this.denominationBreakdownWriter.validateBreakdown(command, transactionAmount, "WITHDRAWAL");
|
||||||
this.denominationBreakdownWriter.save(command, withdrawal.getId(), account.getId(), account.getAccountNumber(),
|
this.denominationBreakdownWriter.save(command, withdrawal.getId(), account.getId(), account.getAccountNumber(),
|
||||||
|
|||||||
Reference in New Issue
Block a user