Skip to content

feat(genai): multi-bank chat context and summary - #128

Closed
azzabaatout wants to merge 9 commits into
mainfrom
feature/genai-multibank-context
Closed

feat(genai): multi-bank chat context and summary#128
azzabaatout wants to merge 9 commits into
mainfrom
feature/genai-multibank-context

Conversation

@azzabaatout

@azzabaatout azzabaatout commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Closes #127

Aligns the genai service with the multi-bank dashboard (#126) and grounds it in the aggregated data.

  • Context schema: single connectionconnections list (per-bank account name/balance/currency); new transactions, monthlyFlow and spendByBank models; AccountSummary drops the removed credit fields — fixes /summarize rejecting the new orchestrator payload
  • /summarize accepts optional connections + monthlyFlow; the summary now mentions the linked-bank count and this month's in/out/net
  • Offline fallback answers balance questions from the actual context (total + per-bank balances) instead of canned text
  • Context lists tolerate explicit nulls — the orchestrator serializes absent lists as null, which previously caused a 422 and a canned reply
  • Orchestrator: connections are fetched before the summarize call so they can be included; spendByBank is forwarded in the chat context (client sends it too)
  • docker-compose.dev.yml: MODEL_PROVIDER / LOGOS_* pass through from the gitignored .env so the Logos gateway can be tested locally
  • genai exposes an app_version Prometheus metric
  • Tests: 3 → 7 (grounded fallback, multibank summary, legacy summary shape, null-lists regression)

Verified locally against the dev stack with a Logos key: the dashboard summary shows the linked-bank count and monthly flow, and chat replies are grounded in real dashboard data (with model reasoning shown in the UI).

Merge after #126 — this branch includes a merge of feature/real-multibank-dashboard, so the extra commits in the diff disappear once #126 lands.

@azzabaatout azzabaatout linked an issue Jul 14, 2026 that may be closed by this pull request
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds end-to-end multi-bank dashboard support: connection and transaction synchronization, aggregated analytics, expanded API and GenAI context models, demo/live account switching, filtered bank selection, richer dashboard views, database migrations, and a local Compose development stack.

Changes

Multi-bank dashboard

Layer / File(s) Summary
Contracts and persistence
client/src/api.ts, server/.../DashboardModels.java, server/.../model/*, infra/docker/*, infra/helm/...
Dashboard, connection, transaction, and database schemas now expose multi-bank metadata, balances, transaction linkage, and analytics fields.
Connection synchronization and aggregation
server/banking-service/...
Active connections are synchronized, transactions are replaced per connection, and account aggregates are recomputed across linked banks.
Dashboard analytics API
server/orchestrator-service/..., server/transaction-service/...
The dashboard response now includes connections, recent transactions, monthly flow, and spending grouped by bank.
Client mode and dashboard experience
client/src/App.tsx, client/src/api.ts, client/src/styles/app.css
The client adds persistent Demo/Live switching, filtered bank discovery, multi-bank dashboard sections, transaction formatting, and updated chat context wiring.
GenAI dashboard grounding
genai/main.py, genai/tests/test_main.py
GenAI models, prompts, fallback replies, and tests now use plural connections, transactions, and monthly flow data.
Validation and development stack
client/src/App.test.tsx, docker-compose.dev.yml
UI tests cover multi-bank rendering and mode switching, while Compose provisions application, database, and monitoring services.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: feature

Suggested reviewers: wardstonex, yaylymov

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also changes client, banking, orchestrator, transaction, infra, and compose files that are not required by the genai alignment issue. Split the unrelated dashboard, backend, and infra changes into separate PRs, and keep this one focused on the genai context update.
Docstring Coverage ⚠️ Warning Docstring coverage is 23.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The genai service now drops credit fields, accepts multi-bank connections, transactions, and monthly flow, and updates prompts/tests as required.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the genai-focused multi-bank context and summary changes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/genai-multibank-context

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🧹 Nitpick comments (2)
server/orchestrator-service/src/main/java/com/team/bank/orchestrator/DashboardController.java (2)

68-145: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Parallelize independent downstream requests to reduce latency.

The dashboard endpoint fetches data from multiple downstream services using sequential blocking calls (account, trend, expenses, allTx, summary, and connections). Since these requests do not depend on each other, waiting for each to complete sequentially significantly increases the total response time and degrades the dashboard's load speed.

Consider leveraging WebFlux to fetch these independent resources concurrently (e.g., using Mono.zip()) before returning the aggregated response.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@server/orchestrator-service/src/main/java/com/team/bank/orchestrator/DashboardController.java`
around lines 68 - 145, The dashboard aggregation flow should issue the
independent account, trend, expenses, allTx, summary, and banking connection
requests concurrently instead of blocking after each request. Refactor the
request orchestration around the visible WebClient calls in DashboardController
to compose them with WebFlux operators such as Mono.zip, then extract their
results while preserving the existing null handling and optional genai fallback
behavior.

103-104: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Use Arrays.stream(allTx) here to avoid creating an intermediate immutable list just to stream and limit it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@server/orchestrator-service/src/main/java/com/team/bank/orchestrator/DashboardController.java`
around lines 103 - 104, Update the transaction conversion in DashboardController
to use Arrays.stream(allTx).limit(RECENT_TX_LIMIT).toList() when allTx is
non-null, while preserving the existing empty-list result for null values.
Remove the intermediate List.of(allTx) creation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@client/src/App.tsx`:
- Around line 50-59: Update formatBankBalance to validate the selected currency
code before constructing Intl.NumberFormat, falling back to EUR for malformed or
unsupported non-empty values. Preserve the existing em-dash behavior for null
balances and normal currency formatting for valid codes, ensuring invalid bank
payloads cannot throw during Dashboard rendering.
- Around line 847-868: Update bankShare in the App component to avoid
calculating percentages from mixed-currency values: either convert each bank
balance and data.account.totalBalance to the same currency before division, or
return null when a common-currency conversion is unavailable. Preserve the
existing null and non-positive-total guards and only display a share when the
values are comparable.

In `@docker-compose.dev.yml`:
- Around line 79-82: Add an extra_hosts entry to the genai-service configuration
so host.docker.internal resolves to the Docker host gateway on Linux, while
preserving the existing Ollama environment variables and URL.
- Around line 105-113: Update the client service configuration to reflect that
the prebuilt Nginx image cannot consume VITE_API_BASE_URL or VITE_ACCOUNT_ID at
runtime: either add the appropriate build context so these values are applied
during the client build, or remove the unused VITE_* environment entries if this
service is not intended for local UI development.
- Around line 11-13: Update the PostgreSQL service volumes in
docker-compose.dev.yml to mount infra/docker/migrate-multibank.sql into
/docker-entrypoint-initdb.d alongside init.sql, preserving read-only access so
fresh postgres-data volumes apply the multi-bank schema and seed updates.

In
`@server/banking-service/src/main/java/com/team/bank/banking/controller/BankingController.java`:
- Around line 223-235: Update ensureAnchorAccount to make anchor creation
atomic: replace the existsById-then-save sequence with an insert-on-conflict
repository operation, or catch and safely ignore the duplicate-key exception
from a concurrent insert. Preserve the existing account field values and
successful no-op behavior when the anchor already exists.
- Around line 184-190: Update the sync flow in BankingController around
active.forEach(syncService::syncAccount) to process each BankingConnection
independently, catching failures per connection so later banks still
synchronize. Track whether any connection failed and return an explicit
partial-failure response after attempting all syncs, while preserving the
existing empty-active response and successful response behavior.

In
`@server/banking-service/src/main/java/com/team/bank/banking/service/BankingSyncService.java`:
- Around line 140-164: Define a single reporting-currency contract across all
affected sites: in
server/banking-service/src/main/java/com/team/bank/banking/service/BankingSyncService.java:140-164,
retain each transaction’s source currency or convert its amount to EUR before
persistence; if retaining it, add the transaction currency column in
infra/docker/init.sql:13-19 and map that field in
server/banking-service/src/main/java/com/team/bank/banking/model/Transaction.java:29-37.
In
server/banking-service/src/main/java/com/team/bank/banking/service/BankingSyncService.java:243-265,
convert non-EUR balances to EUR or preserve their aggregate currency instead of
labeling raw values as euros.
- Around line 146-151: Update the direction classification in BankingSyncService
to recognize supported credit and debit indicator values explicitly; do not use
DEBIT as the fallback. When credit_debit_indicator is missing or unsupported,
skip or reject the transaction according to the service’s existing handling
conventions, while preserving known CREDIT and DEBIT behavior.
- Around line 134-138: Make syncAccount() transactional so
deleteByConnectionId(), transaction saves, and the aggregate recompute execute
within one transaction. Ensure any failure rolls back the entire
replace-by-connection operation, preserving the existing synchronization
behavior while preventing empty or partially reinserted connection data.

---

Nitpick comments:
In
`@server/orchestrator-service/src/main/java/com/team/bank/orchestrator/DashboardController.java`:
- Around line 68-145: The dashboard aggregation flow should issue the
independent account, trend, expenses, allTx, summary, and banking connection
requests concurrently instead of blocking after each request. Refactor the
request orchestration around the visible WebClient calls in DashboardController
to compose them with WebFlux operators such as Mono.zip, then extract their
results while preserving the existing null handling and optional genai fallback
behavior.
- Around line 103-104: Update the transaction conversion in DashboardController
to use Arrays.stream(allTx).limit(RECENT_TX_LIMIT).toList() when allTx is
non-null, while preserving the existing empty-list result for null values.
Remove the intermediate List.of(allTx) creation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6a505ad4-2a0b-488a-bb55-aac3fc950df9

📥 Commits

Reviewing files that changed from the base of the PR and between 5aec5a6 and 3dc86b7.

📒 Files selected for processing (23)
  • client/src/App.test.tsx
  • client/src/App.tsx
  • client/src/api.ts
  • client/src/styles/app.css
  • docker-compose.dev.yml
  • genai/main.py
  • genai/tests/test_main.py
  • infra/docker/init.sql
  • infra/docker/migrate-multibank.sql
  • infra/helm/banking-app/templates/init-sql-configmap.yaml
  • server/banking-service/src/main/java/com/team/bank/banking/controller/BankingController.java
  • server/banking-service/src/main/java/com/team/bank/banking/dto/ConnectionInfo.java
  • server/banking-service/src/main/java/com/team/bank/banking/model/BankingConnection.java
  • server/banking-service/src/main/java/com/team/bank/banking/model/BankingConnectionRepository.java
  • server/banking-service/src/main/java/com/team/bank/banking/model/Transaction.java
  • server/banking-service/src/main/java/com/team/bank/banking/model/TransactionRepository.java
  • server/banking-service/src/main/java/com/team/bank/banking/service/BankingSyncService.java
  • server/banking-service/src/test/java/com/team/bank/banking/service/BankingSyncServiceTest.java
  • server/orchestrator-service/src/main/java/com/team/bank/orchestrator/DashboardController.java
  • server/orchestrator-service/src/main/java/com/team/bank/orchestrator/DashboardModels.java
  • server/transaction-service/src/main/java/com/team/bank/transaction/Transaction.java
  • server/transaction-service/src/main/java/com/team/bank/transaction/TransactionController.java
  • server/transaction-service/src/main/java/com/team/bank/transaction/TransactionItem.java

Comment thread client/src/App.tsx
Comment on lines +50 to +59
function formatBankBalance(value: number | null, currency: string | null): string {
if (value === null) {
return "—";
}
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: currency || "EUR",
maximumFractionDigits: 0,
}).format(value);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== App.tsx around formatBankBalance =="
ast-grep outline client/src/App.tsx --view expanded || true
echo
sed -n '1,120p' client/src/App.tsx | cat -n

echo
echo "== Search for error boundaries / Dashboard render =="
rg -n "ErrorBoundary|error boundary|Dashboard" client/src/App.tsx client/src -g '!**/*.map' || true

echo
echo "== Node runtime probe for Intl.NumberFormat invalid currency =="
node - <<'JS'
for (const currency of ["EUR", "USD", "", null, "not-a-code", "123", "€"]) {
  try {
    const opts = { style: "currency", currency: currency || "EUR", maximumFractionDigits: 0 };
    const out = new Intl.NumberFormat("en-US", opts).format(1234);
    console.log(JSON.stringify({ currency, ok: true, out }));
  } catch (e) {
    console.log(JSON.stringify({ currency, ok: false, name: e.name, message: e.message }));
  }
}
JS

Repository: AET-DevOps26/team-team

Length of output: 11345


Guard formatBankBalance against invalid currency codes

currency || "EUR" only covers falsy values; malformed non-empty codes still make Intl.NumberFormat throw a RangeError. Since this runs during Dashboard render, one bad bank payload can take down the whole view. Validate or fall back before constructing the formatter.

🧰 Tools
🪛 GitHub Check: build-test

[warning] 54-54:
Expected blank line before this statement

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/App.tsx` around lines 50 - 59, Update formatBankBalance to
validate the selected currency code before constructing Intl.NumberFormat,
falling back to EUR for malformed or unsupported non-empty values. Preserve the
existing em-dash behavior for null balances and normal currency formatting for
valid codes, ensuring invalid bank payloads cannot throw during Dashboard
rendering.

Comment thread client/src/App.tsx
Comment on lines +847 to +868
const bankCount = data.connections.length;
const bankLabel = bankCount === 1 ? "bank" : "banks";
const total = data.account.totalBalance;
const flow = data.monthlyFlow;
const maxBankSpend = data.spendByBank.reduce((m, b) => Math.max(m, b.spending), 0);

const bankShare = (balance: number | null): string | null => {
if (balance === null || total <= 0) {
return null;
}
return `${Math.round((balance / total) * 100)}%`;
};

return (
<main className="term">
<header className="statusbar">
<span className="live">
<span className="dot" />1 bank linked
<span className="dot" />
{bankCount} {bankLabel} linked
</span>
<span className="right">
<ModeToggle mode={mode} onChange={onModeChange} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and nearby symbols
ast-grep outline client/src/App.tsx --view expanded > /tmp/app_outline.txt
sed -n '1,220p' /tmp/app_outline.txt

printf '\n--- App.tsx excerpt around the cited lines ---\n'
sed -n '820,960p' client/src/App.tsx | cat -n

printf '\n--- Search for bankShare usages and currency fields ---\n'
rg -n "bankShare\\(|currency|totalBalance|monthlyFlow|spendByBank|balance" client/src/App.tsx client/src -g '!**/*.map'

printf '\n--- Relevant type/data definitions ---\n'
git ls-files | rg 'client/src/.*\\.(ts|tsx)$' | xargs -r rg -n "type .*currency|interface .*currency|totalBalance|spendByBank|connections|monthlyFlow|balance:"

Repository: AET-DevOps26/team-team

Length of output: 14357


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '840,940p' client/src/App.tsx | cat -n

Repository: AET-DevOps26/team-team

Length of output: 4149


bankShare should not compare mixed-currency balances
bankShare divides a per-bank balance by data.account.totalBalance, but the UI already formats individual banks in their own currencies. For mixed-currency accounts, that percentage is not meaningful; convert both values to a common currency first or omit the share.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/App.tsx` around lines 847 - 868, Update bankShare in the App
component to avoid calculating percentages from mixed-currency values: either
convert each bank balance and data.account.totalBalance to the same currency
before division, or return null when a common-currency conversion is
unavailable. Preserve the existing null and non-positive-total guards and only
display a share when the values are comparable.

Comment thread docker-compose.dev.yml
Comment on lines +11 to +13
volumes:
- postgres-data:/var/lib/postgresql/data
- ./infra/docker/init.sql:/docker-entrypoint-initdb.d/init.sql:ro

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if migrate-multibank.sql is automatically sourced by init.sql
grep -i "migrate" infra/docker/init.sql || echo "Migration script is not sourced internally. It needs to be mounted."

Repository: AET-DevOps26/team-team

Length of output: 228


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## Files\n'
git ls-files 'docker-compose.dev.yml' 'infra/docker/init.sql' 'infra/docker/migrate-multibank.sql' || true

printf '\n## docker-compose.dev.yml excerpt\n'
cat -n docker-compose.dev.yml | sed -n '1,40p'

printf '\n## infra/docker/init.sql excerpt\n'
cat -n infra/docker/init.sql | sed -n '1,200p'

printf '\n## migrate-multibank.sql presence\n'
if [ -f infra/docker/migrate-multibank.sql ]; then
  wc -l infra/docker/migrate-multibank.sql
  cat -n infra/docker/migrate-multibank.sql | sed -n '1,80p'
else
  echo 'infra/docker/migrate-multibank.sql not found'
fi

Repository: AET-DevOps26/team-team

Length of output: 7176


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## docker-compose.dev.yml around postgres service\n'
sed -n '1,30p' docker-compose.dev.yml

printf '\n## search for migrate-multibank references\n'
rg -n "migrate-multibank|docker-entrypoint-initdb.d|init.sql" -S .

Repository: AET-DevOps26/team-team

Length of output: 2614


Mount infra/docker/migrate-multibank.sql with the init scripts. infra/docker/init.sql doesn’t source it, so fresh postgres-data volumes will miss the multi-bank schema changes and seed updates.

Suggested fix
     volumes:
       - postgres-data:/var/lib/postgresql/data
       - ./infra/docker/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
+      - ./infra/docker/migrate-multibank.sql:/docker-entrypoint-initdb.d/migrate-multibank.sql:ro
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
volumes:
- postgres-data:/var/lib/postgresql/data
- ./infra/docker/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
volumes:
- postgres-data:/var/lib/postgresql/data
- ./infra/docker/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
- ./infra/docker/migrate-multibank.sql:/docker-entrypoint-initdb.d/migrate-multibank.sql:ro
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.dev.yml` around lines 11 - 13, Update the PostgreSQL service
volumes in docker-compose.dev.yml to mount infra/docker/migrate-multibank.sql
into /docker-entrypoint-initdb.d alongside init.sql, preserving read-only access
so fresh postgres-data volumes apply the multi-bank schema and seed updates.

Comment thread docker-compose.dev.yml
Comment on lines +79 to +82
environment:
MODEL_PROVIDER: local
OLLAMA_BASE_URL: http://host.docker.internal:11434
OLLAMA_MODEL: llama3.1:8b

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Ensure host.docker.internal resolves on Linux.

The genai-service connects to a local Ollama instance using http://host.docker.internal:11434. While this works natively on Docker Desktop (Mac/Windows), it often fails to resolve on Linux Docker Engine. Add an extra_hosts configuration to the service to ensure consistent cross-platform functionality for all developers.

💻 Proposed fix
     environment:
       MODEL_PROVIDER: local
       OLLAMA_BASE_URL: http://host.docker.internal:11434
       OLLAMA_MODEL: llama3.1:8b
+    extra_hosts:
+      - "host.docker.internal:host-gateway"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
environment:
MODEL_PROVIDER: local
OLLAMA_BASE_URL: http://host.docker.internal:11434
OLLAMA_MODEL: llama3.1:8b
environment:
MODEL_PROVIDER: local
OLLAMA_BASE_URL: http://host.docker.internal:11434
OLLAMA_MODEL: llama3.1:8b
extra_hosts:
- "host.docker.internal:host-gateway"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.dev.yml` around lines 79 - 82, Add an extra_hosts entry to the
genai-service configuration so host.docker.internal resolves to the Docker host
gateway on Linux, while preserving the existing Ollama environment variables and
URL.

Comment thread docker-compose.dev.yml
Comment on lines +105 to +113
client:
image: ghcr.io/aet-devops26/client:latest
environment:
VITE_API_BASE_URL: http://localhost:8083
VITE_ACCOUNT_ID: 111-222
depends_on:
- orchestrator-service
ports:
- "3000:80"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the client Dockerfile performs runtime env substitution, or if it needs to be built locally
cat client/Dockerfile

Repository: AET-DevOps26/team-team

Length of output: 509


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== docker-compose.dev.yml: client service =="
sed -n '95,125p' docker-compose.dev.yml

echo
echo "== repository files related to client images/builds =="
git ls-files | rg '(^|/)(client|docker-compose.*|Dockerfile|nginx\.conf)$'

echo
echo "== client Dockerfile =="
cat client/Dockerfile

Repository: AET-DevOps26/team-team

Length of output: 1621


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== docker-compose.yml: client service =="
sed -n '1,140p' docker-compose.yml

echo
echo "== client/nginx.conf =="
cat client/nginx.conf

Repository: AET-DevOps26/team-team

Length of output: 4333


VITE_* env vars won’t affect this client image
This service runs a prebuilt Nginx image, so environment values won’t be injected into the Vite bundle at runtime. If this compose file is meant for local UI work, add a build context or use a dev server; otherwise remove the unused VITE_* vars.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.dev.yml` around lines 105 - 113, Update the client service
configuration to reflect that the prebuilt Nginx image cannot consume
VITE_API_BASE_URL or VITE_ACCOUNT_ID at runtime: either add the appropriate
build context so these values are applied during the client build, or remove the
unused VITE_* environment entries if this service is not intended for local UI
development.

Comment on lines +184 to +190
List<BankingConnection> active =
connectionRepository.findByAccountIdAndStatus(accountId, "ACTIVE");
if (optConnection.isEmpty()) {
if (active.isEmpty()) {
return ResponseEntity.badRequest().build();
}
BankingConnection connection = optConnection.get();
syncService.syncAccount(connection);
// Re-sync every linked bank; each syncAccount recomputes the shared aggregate.
active.forEach(syncService::syncAccount);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Isolate failures between linked banks.

An exception from one bank aborts the loop, prevents later connections from syncing, and returns a blanket 500 after partial work. Handle each connection independently and report partial failure explicitly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@server/banking-service/src/main/java/com/team/bank/banking/controller/BankingController.java`
around lines 184 - 190, Update the sync flow in BankingController around
active.forEach(syncService::syncAccount) to process each BankingConnection
independently, catching failures per connection so later banks still
synchronize. Track whether any connection failed and return an explicit
partial-failure response after attempting all syncs, while preserving the
existing empty-active response and successful response behavior.

Comment on lines +223 to +235
/** Creates the zeroed profile/anchor account row if it does not already exist. */
private void ensureAnchorAccount(UUID accountId, LocalDateTime now) {
if (accountRepository.existsById(accountId)) {
return;
}
Account account = new Account();
account.setId(accountId);
account.setCustomerName("My accounts");
account.setAccountType("AGGREGATE");
account.setBalance(BigDecimal.ZERO);
account.setCreditLimit(BigDecimal.ZERO);
account.setUpdatedAt(now);
accountRepository.save(account);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Make anchor creation atomic.

Concurrent first-connect requests can both pass existsById() and race to insert the same account ID, causing one request to fail. Use an insert-on-conflict operation or safely handle the duplicate-key race.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@server/banking-service/src/main/java/com/team/bank/banking/controller/BankingController.java`
around lines 223 - 235, Update ensureAnchorAccount to make anchor creation
atomic: replace the existsById-then-save sequence with an insert-on-conflict
repository operation, or catch and safely ignore the duplicate-key exception
from a concurrent insert. Preserve the existing account field values and
successful no-op behavior when the anchor already exists.

Comment on lines +134 to +138
// Replace-by-connection: Enable Banking returns the full recent window on every call, so
// clearing this connection's rows and re-inserting keeps repeated syncs idempotent without a
// stable external transaction id (and never drops genuinely repeated payments).
transactionRepository.deleteByConnectionId(connection.getId());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C3 '`@Transactional`|deleteByConnectionId|void syncAccount|transactionRepository\.save' \
  server/banking-service/src/main/java

Repository: AET-DevOps26/team-team

Length of output: 4004


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== BankingSyncService annotations and method body ==\n'
sed -n '1,220p' server/banking-service/src/main/java/com/team/bank/banking/service/BankingSyncService.java | cat -n

printf '\n== TransactionRepository ==\n'
sed -n '1,120p' server/banking-service/src/main/java/com/team/bank/banking/model/TransactionRepository.java | cat -n

Repository: AET-DevOps26/team-team

Length of output: 11386


Make the replace-by-connection sync atomic.
deleteByConnectionId() and the later save() calls each run in their own transactions, so a failure mid-sync can leave this connection empty or only partially reinserted. Wrap syncAccount() in one transaction, including the aggregate recompute, so the replacement is all-or-nothing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@server/banking-service/src/main/java/com/team/bank/banking/service/BankingSyncService.java`
around lines 134 - 138, Make syncAccount() transactional so
deleteByConnectionId(), transaction saves, and the aggregate recompute execute
within one transaction. Ensure any failure rolls back the entire
replace-by-connection operation, preserving the existing synchronization
behavior while preventing empty or partially reinserted connection data.

Comment on lines +140 to +164
// transaction_amount is a nested object ({amount, currency}) just like balance_amount, so
// read the inner "amount" rather than stringifying the whole map.
BigDecimal txAmount = parseAmount(field(tx, "transaction_amount"));
if (txAmount == null) {
continue;
}
String creditDebitIndicator = String.valueOf(field(tx, "credit_debit_indicator"));
String direction =
"CRDT".equalsIgnoreCase(creditDebitIndicator)
|| "CREDIT".equalsIgnoreCase(creditDebitIndicator)
? "CREDIT"
: "DEBIT";

String counterparty = counterpartyName(tx, direction);

Transaction newTx = new Transaction();
newTx.setId(UUID.randomUUID());
newTx.setAccountId(connection.getAccountId());
newTx.setConnectionId(connection.getId());
newTx.setBankName(connection.getBankName());
newTx.setCounterparty(counterparty);
newTx.setCategory(describe(tx, counterparty));
newTx.setAmount(txAmount);
newTx.setDirection(direction);
newTx.setCreatedAt(parseTransactionDate(tx));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Define one end-to-end reporting-currency contract.

Source currencies are discarded or raw non-EUR values are presented as euros, making multi-currency balances and analytics numerically incorrect.

  • server/banking-service/src/main/java/com/team/bank/banking/service/BankingSyncService.java#L140-L164: retain transaction currency or convert amounts to EUR before persistence.
  • infra/docker/init.sql#L13-L19: add a transaction currency column if source amounts remain unconverted.
  • server/banking-service/src/main/java/com/team/bank/banking/model/Transaction.java#L29-L37: map the currency field through the JPA entity.
  • server/banking-service/src/main/java/com/team/bank/banking/service/BankingSyncService.java#L243-L265: convert non-EUR balances or preserve aggregate currency rather than labeling raw values as euros.
📍 Affects 3 files
  • server/banking-service/src/main/java/com/team/bank/banking/service/BankingSyncService.java#L140-L164 (this comment)
  • infra/docker/init.sql#L13-L19
  • server/banking-service/src/main/java/com/team/bank/banking/model/Transaction.java#L29-L37
  • server/banking-service/src/main/java/com/team/bank/banking/service/BankingSyncService.java#L243-L265
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@server/banking-service/src/main/java/com/team/bank/banking/service/BankingSyncService.java`
around lines 140 - 164, Define a single reporting-currency contract across all
affected sites: in
server/banking-service/src/main/java/com/team/bank/banking/service/BankingSyncService.java:140-164,
retain each transaction’s source currency or convert its amount to EUR before
persistence; if retaining it, add the transaction currency column in
infra/docker/init.sql:13-19 and map that field in
server/banking-service/src/main/java/com/team/bank/banking/model/Transaction.java:29-37.
In
server/banking-service/src/main/java/com/team/bank/banking/service/BankingSyncService.java:243-265,
convert non-EUR balances to EUR or preserve their aggregate currency instead of
labeling raw values as euros.

Comment on lines +146 to +151
String creditDebitIndicator = String.valueOf(field(tx, "credit_debit_indicator"));
String direction =
"CRDT".equalsIgnoreCase(creditDebitIndicator)
|| "CREDIT".equalsIgnoreCase(creditDebitIndicator)
? "CREDIT"
: "DEBIT";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not classify unknown indicators as debits.

A missing or unsupported credit_debit_indicator currently becomes DEBIT, incorrectly inflating spending. Accept known debit values explicitly and skip or reject unknown values.

Proposed fix
       String direction =
           "CRDT".equalsIgnoreCase(creditDebitIndicator)
                   || "CREDIT".equalsIgnoreCase(creditDebitIndicator)
               ? "CREDIT"
-              : "DEBIT";
+              : "DBIT".equalsIgnoreCase(creditDebitIndicator)
+                      || "DEBIT".equalsIgnoreCase(creditDebitIndicator)
+                  ? "DEBIT"
+                  : null;
+      if (direction == null) {
+        log.warn("Skipping transaction with unknown direction '{}'", creditDebitIndicator);
+        continue;
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
String creditDebitIndicator = String.valueOf(field(tx, "credit_debit_indicator"));
String direction =
"CRDT".equalsIgnoreCase(creditDebitIndicator)
|| "CREDIT".equalsIgnoreCase(creditDebitIndicator)
? "CREDIT"
: "DEBIT";
String creditDebitIndicator = String.valueOf(field(tx, "credit_debit_indicator"));
String direction =
"CRDT".equalsIgnoreCase(creditDebitIndicator)
|| "CREDIT".equalsIgnoreCase(creditDebitIndicator)
? "CREDIT"
: "DBIT".equalsIgnoreCase(creditDebitIndicator)
|| "DEBIT".equalsIgnoreCase(creditDebitIndicator)
? "DEBIT"
: null;
if (direction == null) {
log.warn("Skipping transaction with unknown direction '{}'", creditDebitIndicator);
continue;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@server/banking-service/src/main/java/com/team/bank/banking/service/BankingSyncService.java`
around lines 146 - 151, Update the direction classification in
BankingSyncService to recognize supported credit and debit indicator values
explicitly; do not use DEBIT as the fallback. When credit_debit_indicator is
missing or unsupported, skip or reject the transaction according to the
service’s existing handling conventions, while preserving known CREDIT and DEBIT
behavior.

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.

Align genai assistant with the multi-bank dashboard context

1 participant