From d9c50d129825529e0fb6477e5b62c0f990523bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elisi=C3=A1rio=20Couto?= Date: Wed, 10 Sep 2025 21:48:07 +0100 Subject: [PATCH] feat(api): add currency extraction and account name updates - Extract currency from balances and populate account currency field - Add PUT /api/v1/accounts/{account_id} endpoint for updating account names - Add AccountUpdate Pydantic model for request validation - Modify sync service to enrich account details with balance currency This resolves the issue where account currency and name fields were NULL by extracting currency from GoCardless balance data and providing an API endpoint for manual account name updates. --- leggend/api/models/accounts.py | 9 ++++++++ leggend/api/routes/accounts.py | 38 ++++++++++++++++++++++++++++++++ leggend/services/sync_service.py | 37 +++++++++++++++++++++++-------- 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/leggend/api/models/accounts.py b/leggend/api/models/accounts.py index 39c5e2c..edbc6cb 100644 --- a/leggend/api/models/accounts.py +++ b/leggend/api/models/accounts.py @@ -33,6 +33,15 @@ class AccountDetails(BaseModel): json_encoders = {datetime: lambda v: v.isoformat() if v else None} +class AccountUpdate(BaseModel): + """Account update model""" + + name: Optional[str] = None + + class Config: + json_encoders = {datetime: lambda v: v.isoformat() if v else None} + + class Transaction(BaseModel): """Transaction model""" diff --git a/leggend/api/routes/accounts.py b/leggend/api/routes/accounts.py index 63e9af2..af60031 100644 --- a/leggend/api/routes/accounts.py +++ b/leggend/api/routes/accounts.py @@ -8,6 +8,7 @@ from leggend.api.models.accounts import ( AccountBalance, Transaction, TransactionSummary, + AccountUpdate, ) from leggend.services.database_service import DatabaseService @@ -287,3 +288,40 @@ async def get_account_transactions( raise HTTPException( status_code=404, detail=f"Failed to get transactions: {str(e)}" ) from e + + +@router.put("/accounts/{account_id}", response_model=APIResponse) +async def update_account_details( + account_id: str, update_data: AccountUpdate +) -> APIResponse: + """Update account details (currently only name)""" + try: + # Get current account details + current_account = await database_service.get_account_details_from_db(account_id) + + if not current_account: + raise HTTPException( + status_code=404, detail=f"Account {account_id} not found" + ) + + # Prepare updated account data + updated_account_data = current_account.copy() + if update_data.name is not None: + updated_account_data["name"] = update_data.name + + # Persist updated account details + await database_service.persist_account_details(updated_account_data) + + return APIResponse( + success=True, + data={"id": account_id, "name": update_data.name}, + message=f"Account {account_id} name updated successfully", + ) + + except HTTPException: + raise + except Exception as e: + logger.error(f"Failed to update account {account_id}: {e}") + raise HTTPException( + status_code=500, detail=f"Failed to update account: {str(e)}" + ) from e diff --git a/leggend/services/sync_service.py b/leggend/services/sync_service.py index 9b87a6d..cd9e69c 100644 --- a/leggend/services/sync_service.py +++ b/leggend/services/sync_service.py @@ -55,26 +55,45 @@ class SyncService: account_id ) - # Persist account details to database - if account_details: - await self.database.persist_account_details(account_details) - - # Get and save balances + # Get balances to extract currency information balances = await self.gocardless.get_account_balances(account_id) - if balances and account_details: + + # Enrich account details with currency and persist + if account_details and balances: + enriched_account_details = account_details.copy() + + # Extract currency from first balance + balances_list = balances.get("balances", []) + if balances_list: + first_balance = balances_list[0] + balance_amount = first_balance.get("balanceAmount", {}) + currency = balance_amount.get("currency") + if currency: + enriched_account_details["currency"] = currency + + # Persist enriched account details to database + await self.database.persist_account_details( + enriched_account_details + ) + # Merge account details into balances data for proper persistence balances_with_account_info = balances.copy() balances_with_account_info["institution_id"] = ( - account_details.get("institution_id") + enriched_account_details.get("institution_id") + ) + balances_with_account_info["iban"] = ( + enriched_account_details.get("iban") ) - balances_with_account_info["iban"] = account_details.get("iban") balances_with_account_info["account_status"] = ( - account_details.get("status") + enriched_account_details.get("status") ) await self.database.persist_balance( account_id, balances_with_account_info ) balances_updated += len(balances.get("balances", [])) + elif account_details: + # Fallback: persist account details without currency if balances failed + await self.database.persist_account_details(account_details) # Get and save transactions transactions = await self.gocardless.get_account_transactions(