feat(frontend): adapt to composite key transaction structure

- Update Transaction interface to include stable transaction_id field
- Modify TransactionsList to use stable transaction_id for React keys
- Update API models to handle new transactionId field from database
- Fix API routes to properly map transaction_id in responses
- Update test mocks to include transactionId field
- Ensure backward compatibility with internal_transaction_id

This adapts the frontend to work with the new composite primary key
(accountId, transactionId) structure that prevents duplicate transactions.
This commit is contained in:
Elisiário Couto
2025-09-10 21:11:26 +01:00
parent 13e92ccd34
commit 61fafecb78
6 changed files with 24 additions and 15 deletions

View File

@@ -267,10 +267,7 @@ export default function TransactionsList() {
return ( return (
<div <div
key={ key={`${transaction.account_id}-${transaction.transaction_id}`}
transaction.internal_transaction_id ||
`${transaction.account_id}-${transaction.transaction_date}-${transaction.transaction_value}`
}
className="p-6 hover:bg-gray-50 transition-colors" className="p-6 hover:bg-gray-50 transition-colors"
> >
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
@@ -365,7 +362,7 @@ export default function TransactionsList() {
isOpen={showRawModal} isOpen={showRawModal}
onClose={handleCloseModal} onClose={handleCloseModal}
rawTransaction={selectedTransaction?.raw_transaction} rawTransaction={selectedTransaction?.raw_transaction}
transactionId={selectedTransaction?.internal_transaction_id || "unknown"} transactionId={selectedTransaction?.transaction_id || "unknown"}
/> />
</div> </div>
); );

View File

@@ -56,7 +56,8 @@ export interface RawTransactionData {
} }
export interface Transaction { export interface Transaction {
internal_transaction_id: string | null; transaction_id: string; // NEW: stable bank-provided transaction ID
internal_transaction_id: string | null; // OLD: unstable GoCardless ID
account_id: string; account_id: string;
transaction_value: number; transaction_value: number;
transaction_currency: string; transaction_currency: string;

View File

@@ -36,7 +36,8 @@ class AccountDetails(BaseModel):
class Transaction(BaseModel): class Transaction(BaseModel):
"""Transaction model""" """Transaction model"""
internal_transaction_id: Optional[str] = None transaction_id: str # NEW: stable bank-provided transaction ID
internal_transaction_id: Optional[str] = None # OLD: unstable GoCardless ID
institution_id: str institution_id: str
iban: Optional[str] = None iban: Optional[str] = None
account_id: str account_id: str
@@ -54,6 +55,7 @@ class Transaction(BaseModel):
class TransactionSummary(BaseModel): class TransactionSummary(BaseModel):
"""Transaction summary for lists""" """Transaction summary for lists"""
transaction_id: str # NEW: stable bank-provided transaction ID
internal_transaction_id: Optional[str] = None internal_transaction_id: Optional[str] = None
date: datetime date: datetime
description: str description: str

View File

@@ -243,7 +243,8 @@ async def get_account_transactions(
# Return simplified transaction summaries # Return simplified transaction summaries
data = [ data = [
TransactionSummary( TransactionSummary(
internal_transaction_id=txn["internalTransactionId"], transaction_id=txn["transactionId"], # NEW: stable bank-provided ID
internal_transaction_id=txn.get("internalTransactionId"),
date=txn["transactionDate"], date=txn["transactionDate"],
description=txn["description"], description=txn["description"],
amount=txn["transactionValue"], amount=txn["transactionValue"],
@@ -257,7 +258,8 @@ async def get_account_transactions(
# Return full transaction details # Return full transaction details
data = [ data = [
Transaction( Transaction(
internal_transaction_id=txn["internalTransactionId"], transaction_id=txn["transactionId"], # NEW: stable bank-provided ID
internal_transaction_id=txn.get("internalTransactionId"),
institution_id=txn["institutionId"], institution_id=txn["institutionId"],
iban=txn["iban"], iban=txn["iban"],
account_id=txn["accountId"], account_id=txn["accountId"],

View File

@@ -75,7 +75,8 @@ async def get_all_transactions(
# Return simplified transaction summaries # Return simplified transaction summaries
data = [ data = [
TransactionSummary( TransactionSummary(
internal_transaction_id=txn["internalTransactionId"], transaction_id=txn["transactionId"], # NEW: stable bank-provided ID
internal_transaction_id=txn.get("internalTransactionId"),
date=txn["transactionDate"], date=txn["transactionDate"],
description=txn["description"], description=txn["description"],
amount=txn["transactionValue"], amount=txn["transactionValue"],
@@ -89,7 +90,8 @@ async def get_all_transactions(
# Return full transaction details # Return full transaction details
data = [ data = [
Transaction( Transaction(
internal_transaction_id=txn["internalTransactionId"], transaction_id=txn["transactionId"], # NEW: stable bank-provided ID
internal_transaction_id=txn.get("internalTransactionId"),
institution_id=txn["institutionId"], institution_id=txn["institutionId"],
iban=txn["iban"], iban=txn["iban"],
account_id=txn["accountId"], account_id=txn["accountId"],

View File

@@ -15,6 +15,7 @@ class TestTransactionsAPI:
"""Test successful retrieval of all transactions from database.""" """Test successful retrieval of all transactions from database."""
mock_transactions = [ mock_transactions = [
{ {
"transactionId": "bank-txn-001", # NEW: stable bank-provided ID
"internalTransactionId": "txn-001", "internalTransactionId": "txn-001",
"institutionId": "REVOLUT_REVOLT21", "institutionId": "REVOLUT_REVOLT21",
"iban": "LT313250081177977789", "iban": "LT313250081177977789",
@@ -24,9 +25,10 @@ class TestTransactionsAPI:
"transactionCurrency": "EUR", "transactionCurrency": "EUR",
"transactionStatus": "booked", "transactionStatus": "booked",
"accountId": "test-account-123", "accountId": "test-account-123",
"rawTransaction": {"some": "data"}, "rawTransaction": {"transactionId": "bank-txn-001", "some": "data"},
}, },
{ {
"transactionId": "bank-txn-002", # NEW: stable bank-provided ID
"internalTransactionId": "txn-002", "internalTransactionId": "txn-002",
"institutionId": "REVOLUT_REVOLT21", "institutionId": "REVOLUT_REVOLT21",
"iban": "LT313250081177977789", "iban": "LT313250081177977789",
@@ -36,7 +38,7 @@ class TestTransactionsAPI:
"transactionCurrency": "EUR", "transactionCurrency": "EUR",
"transactionStatus": "booked", "transactionStatus": "booked",
"accountId": "test-account-123", "accountId": "test-account-123",
"rawTransaction": {"other": "data"}, "rawTransaction": {"transactionId": "bank-txn-002", "other": "data"},
}, },
] ]
@@ -73,6 +75,7 @@ class TestTransactionsAPI:
"""Test retrieval of full transaction details from database.""" """Test retrieval of full transaction details from database."""
mock_transactions = [ mock_transactions = [
{ {
"transactionId": "bank-txn-001", # NEW: stable bank-provided ID
"internalTransactionId": "txn-001", "internalTransactionId": "txn-001",
"institutionId": "REVOLUT_REVOLT21", "institutionId": "REVOLUT_REVOLT21",
"iban": "LT313250081177977789", "iban": "LT313250081177977789",
@@ -82,7 +85,7 @@ class TestTransactionsAPI:
"transactionCurrency": "EUR", "transactionCurrency": "EUR",
"transactionStatus": "booked", "transactionStatus": "booked",
"accountId": "test-account-123", "accountId": "test-account-123",
"rawTransaction": {"some": "raw_data"}, "rawTransaction": {"transactionId": "bank-txn-001", "some": "raw_data"},
} }
] ]
@@ -105,6 +108,7 @@ class TestTransactionsAPI:
assert len(data["data"]) == 1 assert len(data["data"]) == 1
transaction = data["data"][0] transaction = data["data"][0]
assert transaction["transaction_id"] == "bank-txn-001" # NEW: check stable ID
assert transaction["internal_transaction_id"] == "txn-001" assert transaction["internal_transaction_id"] == "txn-001"
assert transaction["institution_id"] == "REVOLUT_REVOLT21" assert transaction["institution_id"] == "REVOLUT_REVOLT21"
assert transaction["iban"] == "LT313250081177977789" assert transaction["iban"] == "LT313250081177977789"
@@ -116,6 +120,7 @@ class TestTransactionsAPI:
"""Test getting transactions with various filters.""" """Test getting transactions with various filters."""
mock_transactions = [ mock_transactions = [
{ {
"transactionId": "bank-txn-001", # NEW: stable bank-provided ID
"internalTransactionId": "txn-001", "internalTransactionId": "txn-001",
"institutionId": "REVOLUT_REVOLT21", "institutionId": "REVOLUT_REVOLT21",
"iban": "LT313250081177977789", "iban": "LT313250081177977789",
@@ -125,7 +130,7 @@ class TestTransactionsAPI:
"transactionCurrency": "EUR", "transactionCurrency": "EUR",
"transactionStatus": "booked", "transactionStatus": "booked",
"accountId": "test-account-123", "accountId": "test-account-123",
"rawTransaction": {"some": "data"}, "rawTransaction": {"transactionId": "bank-txn-001", "some": "data"},
} }
] ]