From c83ebf43ab676b068ed6714e0762ed3347916c65 Mon Sep 17 00:00:00 2001 From: padreug Date: Sun, 6 Jul 2025 00:00:30 +0200 Subject: [PATCH] 01 Refactor currency handling to store amounts in GTQ: Removed currency conversion utilities, updated models and API endpoints to directly handle GTQ amounts, and modified transaction processing logic for consistency. Enhanced frontend to reflect these changes, ensuring accurate display and submission of GTQ values across the application. Refactor GTQ storage migration: Moved the conversion logic for centavo amounts to GTQ into a new migration function, m004_convert_to_gtq_storage, ensuring proper data type changes and updates across relevant tables. This enhances clarity and maintains the integrity of the migration process. --- crud.py | 2 +- currency_utils.py | 45 --------------- migrations.py | 84 ++++++++++++++++++++++++++++ models.py | 82 ++++++++++----------------- static/js/index.js | 12 ++-- templates/satmachineadmin/index.html | 8 +-- transaction_processor.py | 36 ++++++------ views_api.py | 50 +++++------------ 8 files changed, 157 insertions(+), 162 deletions(-) delete mode 100644 currency_utils.py diff --git a/crud.py b/crud.py index 60bad39..94c1d20 100644 --- a/crud.py +++ b/crud.py @@ -267,7 +267,7 @@ async def get_client_balance_summary(client_id: str, as_of_time: Optional[dateti from loguru import logger # Verify timezone consistency for temporal filtering tz_info = "UTC" if as_of_time.tzinfo == timezone.utc else f"TZ: {as_of_time.tzinfo}" - logger.info(f"Client {client_id[:8]}... balance as of {as_of_time} ({tz_info}): deposits.confirmed_at <= cutoff, payments.transaction_time <= cutoff → {total_deposits - total_payments} centavos remaining") + logger.info(f"Client {client_id[:8]}... balance as of {as_of_time} ({tz_info}): deposits.confirmed_at <= cutoff, payments.transaction_time <= cutoff → {total_deposits - total_payments:.2f} GTQ remaining") return ClientBalanceSummary( client_id=client_id, diff --git a/currency_utils.py b/currency_utils.py deleted file mode 100644 index 3241134..0000000 --- a/currency_utils.py +++ /dev/null @@ -1,45 +0,0 @@ -# Currency conversion utilities for API boundary -from decimal import Decimal -from typing import Union - - -def gtq_to_centavos(gtq_amount: Union[float, int, str]) -> int: - """Convert GTQ to centavos for database storage""" - return int(Decimal(str(gtq_amount)) * 100) - - -def centavos_to_gtq(centavos: int) -> float: - """Convert centavos to GTQ for API responses""" - return float(centavos) / 100 - - -def format_gtq_currency(centavos: int) -> str: - """Format centavos as GTQ currency string""" - gtq_amount = centavos_to_gtq(centavos) - return f"Q{gtq_amount:.2f}" - - -# Conversion helpers for API responses -def deposit_db_to_api(deposit_db) -> dict: - """Convert database deposit model to API response""" - return { - "id": deposit_db.id, - "client_id": deposit_db.client_id, - "amount_gtq": centavos_to_gtq(deposit_db.amount), - "currency": deposit_db.currency, - "status": deposit_db.status, - "notes": deposit_db.notes, - "created_at": deposit_db.created_at, - "confirmed_at": deposit_db.confirmed_at - } - - -def balance_summary_db_to_api(balance_db) -> dict: - """Convert database balance summary to API response""" - return { - "client_id": balance_db.client_id, - "total_deposits_gtq": centavos_to_gtq(balance_db.total_deposits), - "total_payments_gtq": centavos_to_gtq(balance_db.total_payments), - "remaining_balance_gtq": centavos_to_gtq(balance_db.remaining_balance), - "currency": balance_db.currency - } \ No newline at end of file diff --git a/migrations.py b/migrations.py index 0456f43..6654c71 100644 --- a/migrations.py +++ b/migrations.py @@ -134,4 +134,88 @@ async def m003_add_max_daily_limit_config(db): ALTER TABLE satoshimachine.lamassu_config ADD COLUMN max_daily_limit_gtq INTEGER NOT NULL DEFAULT 2000 """ + ) + + +async def m004_convert_to_gtq_storage(db): + """ + Convert centavo storage to GTQ storage by changing data types and converting existing data. + """ + # Convert dca_deposits amounts from centavos to GTQ + await db.execute( + """ + UPDATE satoshimachine.dca_deposits + SET amount = CAST(amount AS DECIMAL(10,2)) / 100.0 + WHERE currency = 'GTQ' + """ + ) + + # Convert dca_payments amounts from centavos to GTQ + await db.execute( + """ + UPDATE satoshimachine.dca_payments + SET amount_fiat = CAST(amount_fiat AS DECIMAL(10,2)) / 100.0 + """ + ) + + # Convert lamassu_transactions amounts from centavos to GTQ + await db.execute( + """ + UPDATE satoshimachine.lamassu_transactions + SET fiat_amount = CAST(fiat_amount AS DECIMAL(10,2)) / 100.0 + """ + ) + + # Convert fixed_mode_daily_limit from centavos to GTQ + await db.execute( + """ + UPDATE satoshimachine.dca_clients + SET fixed_mode_daily_limit = CAST(fixed_mode_daily_limit AS DECIMAL(10,2)) / 100.0 + WHERE fixed_mode_daily_limit IS NOT NULL + """ + ) + + # Convert max_daily_limit_gtq in config (if already in centavos) + await db.execute( + """ + UPDATE satoshimachine.lamassu_config + SET max_daily_limit_gtq = CAST(max_daily_limit_gtq AS DECIMAL(10,2)) / 100.0 + WHERE max_daily_limit_gtq > 1000 + """ + ) + + # Change column types to DECIMAL + await db.execute( + """ + ALTER TABLE satoshimachine.dca_deposits + ALTER COLUMN amount TYPE DECIMAL(10,2) + """ + ) + + await db.execute( + """ + ALTER TABLE satoshimachine.dca_payments + ALTER COLUMN amount_fiat TYPE DECIMAL(10,2) + """ + ) + + await db.execute( + """ + ALTER TABLE satoshimachine.lamassu_transactions + ALTER COLUMN fiat_amount TYPE DECIMAL(10,2) + """ + ) + + await db.execute( + """ + ALTER TABLE satoshimachine.dca_clients + ALTER COLUMN fixed_mode_daily_limit TYPE DECIMAL(10,2) + """ + ) + + await db.execute( + """ + ALTER TABLE satoshimachine.lamassu_config + ALTER COLUMN max_daily_limit_gtq TYPE DECIMAL(10,2) + """ ) \ No newline at end of file diff --git a/models.py b/models.py index 7727bee..e4bf64d 100644 --- a/models.py +++ b/models.py @@ -3,7 +3,7 @@ from datetime import datetime from typing import Optional -from pydantic import BaseModel +from pydantic import BaseModel, validator # DCA Client Models @@ -12,7 +12,7 @@ class CreateDcaClientData(BaseModel): wallet_id: str username: str dca_mode: str = "flow" # 'flow' or 'fixed' - fixed_mode_daily_limit: Optional[int] = None + fixed_mode_daily_limit: Optional[float] = None class DcaClient(BaseModel): @@ -30,45 +30,29 @@ class DcaClient(BaseModel): class UpdateDcaClientData(BaseModel): username: Optional[str] = None dca_mode: Optional[str] = None - fixed_mode_daily_limit: Optional[int] = None + fixed_mode_daily_limit: Optional[float] = None status: Optional[str] = None -# API Models for Deposits (Frontend <-> Backend communication) -class CreateDepositAPI(BaseModel): - """API model - frontend sends GTQ amounts""" - client_id: str - amount_gtq: float # Amount in GTQ (e.g., 150.75) - currency: str = "GTQ" - notes: Optional[str] = None - - -class DepositAPI(BaseModel): - """API model - backend returns GTQ amounts""" - id: str - client_id: str - amount_gtq: float # Amount in GTQ (e.g., 150.75) - currency: str - status: str # 'pending' or 'confirmed' - notes: Optional[str] - created_at: datetime - confirmed_at: Optional[datetime] - - -# Database Models for Deposits (Internal storage in centavos) +# Deposit Models (Now storing GTQ directly) class CreateDepositData(BaseModel): - """Internal model - database stores centavos""" client_id: str - amount: int # Amount in smallest currency unit (centavos for GTQ) + amount: float # Amount in GTQ (e.g., 150.75) currency: str = "GTQ" notes: Optional[str] = None + + @validator('amount') + def round_amount_to_cents(cls, v): + """Ensure amount is rounded to 2 decimal places for DECIMAL(10,2) storage""" + if v is not None: + return round(float(v), 2) + return v class DcaDeposit(BaseModel): - """Internal model - database stores centavos""" id: str client_id: str - amount: int + amount: float # Amount in GTQ (e.g., 150.75) currency: str status: str # 'pending' or 'confirmed' notes: Optional[str] @@ -85,7 +69,7 @@ class UpdateDepositStatusData(BaseModel): class CreateDcaPaymentData(BaseModel): client_id: str amount_sats: int - amount_fiat: int # Stored in centavos (GTQ * 100) for precision + amount_fiat: float # Amount in GTQ (e.g., 150.75) exchange_rate: float transaction_type: str # 'flow', 'fixed', 'manual', 'commission' lamassu_transaction_id: Optional[str] = None @@ -97,7 +81,7 @@ class DcaPayment(BaseModel): id: str client_id: str amount_sats: int - amount_fiat: int # Stored in centavos (GTQ * 100) for precision + amount_fiat: float # Amount in GTQ (e.g., 150.75) exchange_rate: float transaction_type: str lamassu_transaction_id: Optional[str] @@ -107,30 +91,19 @@ class DcaPayment(BaseModel): transaction_time: Optional[datetime] = None # Original ATM transaction time -# API Models for Client Balance Summary -class ClientBalanceSummaryAPI(BaseModel): - """API model - returns GTQ amounts""" - client_id: str - total_deposits_gtq: float # Total confirmed deposits in GTQ - total_payments_gtq: float # Total payments made in GTQ - remaining_balance_gtq: float # Available balance for DCA in GTQ - currency: str - - -# Internal Models for Client Balance Summary +# Client Balance Summary (Now storing GTQ directly) class ClientBalanceSummary(BaseModel): - """Internal model - stores centavos""" client_id: str - total_deposits: int # Total confirmed deposits - total_payments: int # Total payments made - remaining_balance: int # Available balance for DCA + total_deposits: float # Total confirmed deposits in GTQ + total_payments: float # Total payments made in GTQ + remaining_balance: float # Available balance for DCA in GTQ currency: str # Transaction Processing Models class LamassuTransaction(BaseModel): transaction_id: str - amount_fiat: int # Stored in centavos (GTQ * 100) for precision + amount_fiat: float # Amount in GTQ (e.g., 150.75) amount_crypto: int exchange_rate: float transaction_type: str # 'cash_in' or 'cash_out' @@ -141,7 +114,7 @@ class LamassuTransaction(BaseModel): # Lamassu Transaction Storage Models class CreateLamassuTransactionData(BaseModel): lamassu_transaction_id: str - fiat_amount: int # Stored in centavos (GTQ * 100) for precision + fiat_amount: float # Amount in GTQ (e.g., 150.75) crypto_amount: int commission_percentage: float discount: float = 0.0 @@ -158,7 +131,7 @@ class CreateLamassuTransactionData(BaseModel): class StoredLamassuTransaction(BaseModel): id: str lamassu_transaction_id: str - fiat_amount: int + fiat_amount: float # Amount in GTQ (e.g., 150.75) crypto_amount: int commission_percentage: float discount: float @@ -194,7 +167,14 @@ class CreateLamassuConfigData(BaseModel): ssh_password: Optional[str] = None ssh_private_key: Optional[str] = None # Path to private key file or key content # DCA Client Limits - max_daily_limit_gtq: int = 2000 # Maximum daily limit for Fixed mode clients + max_daily_limit_gtq: float = 2000.0 # Maximum daily limit for Fixed mode clients + + @validator('max_daily_limit_gtq') + def round_max_daily_limit(cls, v): + """Ensure max daily limit is rounded to 2 decimal places""" + if v is not None: + return round(float(v), 2) + return v class LamassuConfig(BaseModel): @@ -224,7 +204,7 @@ class LamassuConfig(BaseModel): last_poll_time: Optional[datetime] = None last_successful_poll: Optional[datetime] = None # DCA Client Limits - max_daily_limit_gtq: int = 2000 # Maximum daily limit for Fixed mode clients + max_daily_limit_gtq: float = 2000.0 # Maximum daily limit for Fixed mode clients class UpdateLamassuConfigData(BaseModel): diff --git a/static/js/index.js b/static/js/index.js index 6178a02..1989e9b 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -27,7 +27,7 @@ window.app = Vue.createApp({ depositsTable: { columns: [ { name: 'client_id', align: 'left', label: 'Client', field: 'client_id' }, - { name: 'amount_gtq', align: 'left', label: 'Amount', field: 'amount_gtq' }, + { name: 'amount', align: 'left', label: 'Amount', field: 'amount' }, { name: 'currency', align: 'left', label: 'Currency', field: 'currency' }, { name: 'status', align: 'left', label: 'Status', field: 'status' }, { name: 'created_at', align: 'left', label: 'Created', field: 'created_at' }, @@ -130,11 +130,11 @@ window.app = Vue.createApp({ /////////////////////////////////////////////////// methods: { - // Utility Methods - Simplified since API handles conversion + // Utility Methods formatCurrency(amount) { if (!amount) return 'Q 0.00'; - // Amount is already in GTQ from API + // Amount is now stored as GTQ directly in database return new Intl.NumberFormat('es-GT', { style: 'currency', currency: 'GTQ', @@ -279,7 +279,7 @@ window.app = Vue.createApp({ ) return { ...client, - remaining_balance: balance.remaining_balance_gtq + remaining_balance: balance.remaining_balance } } catch (error) { console.error(`Error fetching balance for client ${client.id}:`, error) @@ -303,7 +303,7 @@ window.app = Vue.createApp({ try { const data = { client_id: this.quickDepositForm.selectedClient?.value, - amount_gtq: this.quickDepositForm.amount, // Send GTQ directly - API handles conversion + amount: this.quickDepositForm.amount, // Send GTQ directly - now stored as GTQ currency: 'GTQ', notes: this.quickDepositForm.notes } @@ -376,7 +376,7 @@ window.app = Vue.createApp({ try { const data = { client_id: this.depositFormDialog.data.client_id, - amount_gtq: this.depositFormDialog.data.amount, // Send GTQ directly - API handles conversion + amount: this.depositFormDialog.data.amount, // Send GTQ directly - now stored as GTQ currency: this.depositFormDialog.data.currency, notes: this.depositFormDialog.data.notes } diff --git a/templates/satmachineadmin/index.html b/templates/satmachineadmin/index.html index 8901460..61099ce 100644 --- a/templates/satmachineadmin/index.html +++ b/templates/satmachineadmin/index.html @@ -173,7 +173,7 @@
${ getClientUsername(col.value) }
-
${ formatCurrency(col.value) }
+
${ formatCurrency(col.value) }
${ col.value } @@ -470,9 +470,9 @@ Balance Summary - Deposits: ${ formatCurrency(clientDetailsDialog.balance.total_deposits_gtq) } | - Payments: ${ formatCurrency(clientDetailsDialog.balance.total_payments_gtq) } | - Remaining: ${ formatCurrency(clientDetailsDialog.balance.remaining_balance_gtq) } + Deposits: ${ formatCurrency(clientDetailsDialog.balance.total_deposits) } | + Payments: ${ formatCurrency(clientDetailsDialog.balance.total_payments) } | + Remaining: ${ formatCurrency(clientDetailsDialog.balance.remaining_balance) } diff --git a/transaction_processor.py b/transaction_processor.py index c9c4f84..895f8d7 100644 --- a/transaction_processor.py +++ b/transaction_processor.py @@ -671,7 +671,7 @@ class LamassuTransactionProcessor: # Since crypto_atoms already includes commission, we need to extract the base amount # Formula: crypto_atoms = base_amount * (1 + effective_commission) # Therefore: base_amount = crypto_atoms / (1 + effective_commission) - base_crypto_atoms = int(crypto_atoms / (1 + effective_commission)) + base_crypto_atoms = round(crypto_atoms / (1 + effective_commission)) commission_amount_sats = crypto_atoms - base_crypto_atoms else: effective_commission = 0.0 @@ -755,9 +755,8 @@ class LamassuTransactionProcessor: client_sats_amount = calc['allocated_sats'] proportion = calc['proportion'] - # Calculate equivalent fiat value in centavos for tracking purposes (industry standard) - # Store as centavos to maintain precision and avoid floating-point errors - client_fiat_amount = round(client_sats_amount * 100 / exchange_rate) if exchange_rate > 0 else 0 + # Calculate equivalent fiat value in GTQ for tracking purposes + client_fiat_amount = round(client_sats_amount / exchange_rate, 2) if exchange_rate > 0 else 0.0 distributions[client_id] = { "fiat_amount": client_fiat_amount, @@ -765,7 +764,7 @@ class LamassuTransactionProcessor: "exchange_rate": exchange_rate } - logger.info(f"Client {client_id[:8]}... gets {client_sats_amount} sats (≈{client_fiat_amount/100:.2f} GTQ, {proportion:.2%} share)") + logger.info(f"Client {client_id[:8]}... gets {client_sats_amount} sats (≈{client_fiat_amount:.2f} GTQ, {proportion:.2%} share)") # Verification: ensure total distribution equals base amount total_distributed = sum(dist["sats_amount"] for dist in distributions.values()) @@ -781,9 +780,9 @@ class LamassuTransactionProcessor: current_balance = await get_client_balance_summary(client_id) if current_balance.remaining_balance > 0: final_distributions[client_id] = distribution - logger.info(f"Client {client_id[:8]}... final balance check: {current_balance.remaining_balance} centavos - APPROVED for {distribution['sats_amount']} sats") + logger.info(f"Client {client_id[:8]}... final balance check: {current_balance.remaining_balance:.2f} GTQ - APPROVED for {distribution['sats_amount']} sats") else: - logger.warning(f"Client {client_id[:8]}... final balance check: {current_balance.remaining_balance} centavos - REJECTED (negative balance)") + logger.warning(f"Client {client_id[:8]}... final balance check: {current_balance.remaining_balance:.2f} GTQ - REJECTED (negative balance)") if len(final_distributions) != len(distributions): logger.warning(f"Rejected {len(distributions) - len(final_distributions)} clients due to negative balances during final check") @@ -830,22 +829,22 @@ class LamassuTransactionProcessor: # Final safety check: Verify client still has positive balance before payment current_balance = await get_client_balance_summary(client_id) if current_balance.remaining_balance <= 0: - logger.error(f"CRITICAL: Client {client_id[:8]}... has negative balance ({current_balance.remaining_balance} centavos) - REFUSING payment of {distribution['sats_amount']} sats") + logger.error(f"CRITICAL: Client {client_id[:8]}... has negative balance ({current_balance.remaining_balance:.2f} GTQ) - REFUSING payment of {distribution['sats_amount']} sats") continue # Verify balance is sufficient for this distribution - fiat_equivalent = distribution["fiat_amount"] # Already in centavos + fiat_equivalent = distribution["fiat_amount"] # Amount in GTQ if current_balance.remaining_balance < fiat_equivalent: - logger.error(f"CRITICAL: Client {client_id[:8]}... insufficient balance ({current_balance.remaining_balance} < {fiat_equivalent} centavos) - REFUSING payment") + logger.error(f"CRITICAL: Client {client_id[:8]}... insufficient balance ({current_balance.remaining_balance:.2f} < {fiat_equivalent:.2f} GTQ) - REFUSING payment") continue - logger.info(f"Client {client_id[:8]}... pre-payment balance check: {current_balance.remaining_balance} centavos - SUFFICIENT for {fiat_equivalent} centavos payment") + logger.info(f"Client {client_id[:8]}... pre-payment balance check: {current_balance.remaining_balance:.2f} GTQ - SUFFICIENT for {fiat_equivalent:.2f} GTQ payment") # Create DCA payment record payment_data = CreateDcaPaymentData( client_id=client_id, amount_sats=distribution["sats_amount"], - amount_fiat=distribution["fiat_amount"], # Still store centavos in DB + amount_fiat=distribution["fiat_amount"], # Amount in GTQ exchange_rate=distribution["exchange_rate"], transaction_type="flow", lamassu_transaction_id=transaction_id, @@ -888,12 +887,9 @@ class LamassuTransactionProcessor: return False # Create descriptive memo with DCA metrics - fiat_amount_centavos = distribution.get("fiat_amount", 0) + fiat_amount_gtq = distribution.get("fiat_amount", 0.0) exchange_rate = distribution.get("exchange_rate", 0) - # Convert centavos to GTQ for display - fiat_amount_gtq = fiat_amount_centavos / 100 - # Calculate cost basis (fiat per BTC) if exchange_rate > 0: # exchange_rate is sats per fiat unit, so convert to fiat per BTC @@ -1011,7 +1007,7 @@ class LamassuTransactionProcessor: # Calculate commission metrics if commission_percentage > 0: effective_commission = commission_percentage * (100 - discount) / 100 - base_crypto_atoms = int(crypto_atoms / (1 + effective_commission)) + base_crypto_atoms = round(crypto_atoms / (1 + effective_commission)) commission_amount_sats = crypto_atoms - base_crypto_atoms else: effective_commission = 0.0 @@ -1021,10 +1017,10 @@ class LamassuTransactionProcessor: # Calculate exchange rate exchange_rate = base_crypto_atoms / fiat_amount if fiat_amount > 0 else 0 - # Create transaction data (store fiat_amount in centavos for consistency) + # Create transaction data with GTQ amounts transaction_data = CreateLamassuTransactionData( lamassu_transaction_id=transaction["transaction_id"], - fiat_amount=int(fiat_amount * 100), # Convert GTQ to centavos + fiat_amount=round(fiat_amount, 2), # Store GTQ with 2 decimal places crypto_amount=crypto_atoms, commission_percentage=commission_percentage, discount=discount, @@ -1137,7 +1133,7 @@ class LamassuTransactionProcessor: if commission_percentage and commission_percentage > 0: effective_commission = commission_percentage * (100 - discount) / 100 - base_crypto_atoms = int(crypto_atoms / (1 + effective_commission)) + base_crypto_atoms = round(crypto_atoms / (1 + effective_commission)) commission_amount_sats = crypto_atoms - base_crypto_atoms else: commission_amount_sats = 0 diff --git a/views_api.py b/views_api.py index 9d3c41a..3700497 100644 --- a/views_api.py +++ b/views_api.py @@ -38,23 +38,14 @@ from .models import ( DcaClient, UpdateDcaClientData, CreateDepositData, - CreateDepositAPI, - DepositAPI, DcaDeposit, UpdateDepositStatusData, ClientBalanceSummary, - ClientBalanceSummaryAPI, CreateLamassuConfigData, LamassuConfig, UpdateLamassuConfigData, StoredLamassuTransaction, ) -from .currency_utils import ( - gtq_to_centavos, - centavos_to_gtq, - deposit_db_to_api, - balance_summary_db_to_api, -) satmachineadmin_api_router = APIRouter() @@ -96,7 +87,7 @@ async def api_get_dca_client( async def api_get_client_balance( client_id: str, wallet: WalletTypeInfo = Depends(check_super_user), -) -> ClientBalanceSummaryAPI: +) -> ClientBalanceSummary: """Get client balance summary""" client = await get_dca_client(client_id) if not client: @@ -104,8 +95,7 @@ async def api_get_client_balance( status_code=HTTPStatus.NOT_FOUND, detail="DCA client not found." ) - balance_db = await get_client_balance_summary(client_id) - return ClientBalanceSummaryAPI(**balance_summary_db_to_api(balance_db)) + return await get_client_balance_summary(client_id) # DCA Deposit Endpoints @@ -114,31 +104,30 @@ async def api_get_client_balance( @satmachineadmin_api_router.get("/api/v1/dca/deposits") async def api_get_deposits( wallet: WalletTypeInfo = Depends(check_super_user), -) -> list[DepositAPI]: +) -> list[DcaDeposit]: """Get all deposits""" - deposits_db = await get_all_deposits() - return [DepositAPI(**deposit_db_to_api(deposit)) for deposit in deposits_db] + return await get_all_deposits() @satmachineadmin_api_router.get("/api/v1/dca/deposits/{deposit_id}") async def api_get_deposit( deposit_id: str, wallet: WalletTypeInfo = Depends(check_super_user), -) -> DepositAPI: +) -> DcaDeposit: """Get a specific deposit""" - deposit_db = await get_deposit(deposit_id) - if not deposit_db: + deposit = await get_deposit(deposit_id) + if not deposit: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="Deposit not found." ) - return DepositAPI(**deposit_db_to_api(deposit_db)) + return deposit @satmachineadmin_api_router.post("/api/v1/dca/deposits", status_code=HTTPStatus.CREATED) async def api_create_deposit( - data: CreateDepositAPI, + data: CreateDepositData, user: User = Depends(check_super_user), -) -> DepositAPI: +) -> DcaDeposit: """Create a new deposit""" # Verify client exists client = await get_dca_client(data.client_id) @@ -147,16 +136,7 @@ async def api_create_deposit( status_code=HTTPStatus.NOT_FOUND, detail="DCA client not found." ) - # Convert GTQ to centavos at API boundary - deposit_data = CreateDepositData( - client_id=data.client_id, - amount=gtq_to_centavos(data.amount_gtq), - currency=data.currency, - notes=data.notes - ) - - deposit_db = await create_deposit(deposit_data) - return DepositAPI(**deposit_db_to_api(deposit_db)) + return await create_deposit(data) @satmachineadmin_api_router.put("/api/v1/dca/deposits/{deposit_id}/status") @@ -164,7 +144,7 @@ async def api_update_deposit_status( deposit_id: str, data: UpdateDepositStatusData, user: User = Depends(check_super_user), -) -> DepositAPI: +) -> DcaDeposit: """Update deposit status (e.g., confirm deposit)""" deposit = await get_deposit(deposit_id) if not deposit: @@ -172,13 +152,13 @@ async def api_update_deposit_status( status_code=HTTPStatus.NOT_FOUND, detail="Deposit not found." ) - updated_deposit_db = await update_deposit_status(deposit_id, data) - if not updated_deposit_db: + updated_deposit = await update_deposit_status(deposit_id, data) + if not updated_deposit: raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Failed to update deposit.", ) - return DepositAPI(**deposit_db_to_api(updated_deposit_db)) + return updated_deposit # Transaction Polling Endpoints