Skip to content

feature: enforce configurable $5,000 daily withdrawal limit - #377

Open
devin-ai-integration[bot] wants to merge 2 commits into
DevOpsfrom
devin/1794001798-daily-withdrawal-limit
Open

devin-ai-integration[bot] wants to merge 2 commits into
DevOpsfrom
devin/1794001798-daily-withdrawal-limit

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Summary

Caps each account's withdrawals at a configurable total per calendar day (default $5,000). A violation throws a RuntimeException from AccountService.withdraw, so it reaches the dashboard through the same path as "Insufficient funds" (BankController.withdraw catch → error model attribute → .error paragraph in dashboard.html). No controller or template changes.

// AccountService.withdraw, runs before the existing insufficient-funds check
if (getWithdrawnToday(account).add(amount).compareTo(dailyWithdrawalLimit) > 0)
    throw new RuntimeException("Daily withdrawal limit of $" + dailyWithdrawalLimit.toPlainString() + " exceeded");
// getWithdrawnToday: transactionRepository.findByAccountIdAndTimestampBetween(id, today 00:00, tomorrow 00:00)
//                    → keep type == "Withdrawal" → sum amount
  • application.properties: bankapp.daily-withdrawal-limit=5000, injected through @Value as a BigDecimal.
  • TransactionRepository: new derived query findByAccountIdAndTimestampBetween(Long, LocalDateTime, LocalDateTime). The field name timestamp was checked against the Transaction entity.
  • Only rows with type "Withdrawal" count toward the limit. Deposits and "Transfer Out to …" rows don't count. Withdrawing exactly up to the limit is allowed (the check is >, not >=).
  • withdraw reads the clock once into now. That value sets the "today" window (getWithdrawnOn(account, now.toLocalDate())) and also stamps the new Transaction, so a request that crosses midnight is checked and recorded against the same day. The window uses the JVM default timezone.
  • withdraw now rejects null, zero and negative amounts with "Withdrawal amount must be positive". Without this, a negative "Withdrawal" row would lower the day's total and reopen the allowance. This error reaches the dashboard by the same path as the others.
  • Known gap, not addressed here: concurrent withdrawals can each pass the limit check, because there's no row lock or transaction around read-check-write. The existing balance check has the same race.
  • The insufficient-funds check and the code that records transactions are unchanged. When a withdrawal breaks both rules, the limit error is shown.

Testing

  • New AccountServiceDailyWithdrawalLimitTest (Mockito, 6 cases). It covers a withdrawal up to the limit, a cumulative overage (balance and transactions left unsaved), a single withdrawal over the limit, ignoring non-Withdrawal types, the insufficient-funds check still working, and rejection of non-positive amounts. ./mvnw clean test: 7 tests, 0 failures. ./mvnw clean compile also passes.
  • Not tested end to end. The Cypress suite and the running app were skipped at the requester's direction, and no Cypress spec was added.

Sources

Wiki answers: none used. A DeepWiki query for COG-GTM/Springboot-BankApp returned "Repository not found" because the repo isn't indexed. Everything below comes from reading files on DevOps.

Devin-Org: engineering

Link to Devin session: https://devin-gtm.devinenterprise.com/sessions/6f971cc34dc74af9ba938ccf24cdac22
Open in Devin Desktop: https://devin-gtm.devinenterprise.com/desktop/session/6f971cc34dc74af9ba938ccf24cdac22?variant=devin
Requested by: @gracejuan-star


Note

Devin errored when opening this Pull Request as gracejuan-star.
As a fallback, Devin opened this PR as itself.


Devin Review

Status Commit
⚪ Not started —

Run Devin Review

Enable auto-review to review your pull requests automatically.

Devin Review (Staging)


Devin Review

Co-Authored-By: Grace Juan <grace.juan@cognition.ai>
@devin-ai-integration

Copy link
Copy Markdown
Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

Devin Review

Comment on lines +70 to +72
if (getWithdrawnToday(account).add(amount).compareTo(dailyWithdrawalLimit) > 0) {
throw new RuntimeException("Daily withdrawal limit of $" + dailyWithdrawalLimit.toPlainString() + " exceeded");
}

@devin-ai-integration devin-ai-integration Bot Nov 6, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟥 Parallel withdrawals bypass daily limit

Concurrent withdraw requests can read the same daily total and each pass the limit check. Their transactions then record a combined amount above the configured cap.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This race is real. The existing "Insufficient funds" check already has it: withdraw isn't @Transactional, and it works on an Account the controller loaded earlier, with no lock. Fixing it properly means locking the account row for the whole read-check-write, for example a @Lock(PESSIMISTIC_WRITE) lookup inside a @Transactional withdraw, and that would also change the balance and transfer paths. That's outside what this PR was asked to do, so I'm leaving it open for the requester to decide whether it goes here or in a separate PR.

…ithdrawal

Co-Authored-By: Grace Juan <grace.juan@cognition.ai>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants