01 Refactor currency handling in client models and calculations: Update models to store values in GTQ instead of centavos, adjust cost basis calculations, and modify API responses and frontend currency formatting to reflect the new structure.
Some checks failed
CI / lint (push) Has been cancelled
CI / tests (3.10) (push) Has been cancelled
CI / tests (3.9) (push) Has been cancelled
/ release (push) Has been cancelled
/ pullrequest (push) Has been cancelled

This commit is contained in:
padreug 2025-07-06 00:00:48 +02:00
parent 8c3faeec3f
commit 8d442b7c6f
5 changed files with 16 additions and 69 deletions

View file

@ -103,7 +103,7 @@ async def get_client_dashboard_summary(user_id: str) -> Optional[ClientDashboard
# Calculate metrics # Calculate metrics
total_invested = confirmed_deposits # Total invested = all confirmed deposits total_invested = confirmed_deposits # Total invested = all confirmed deposits
remaining_balance = confirmed_deposits - dca_spent # Remaining = deposits - DCA spending remaining_balance = confirmed_deposits - dca_spent # Remaining = deposits - DCA spending
avg_cost_basis = total_sats / (dca_spent / 100) if dca_spent > 0 else 0 # Cost basis = sats / GTQ (convert centavos to GTQ) avg_cost_basis = total_sats / dca_spent if dca_spent > 0 else 0 # Cost basis = sats / GTQ
# Calculate current fiat value of total sats # Calculate current fiat value of total sats
current_sats_fiat_value = 0.0 current_sats_fiat_value = 0.0
@ -248,7 +248,7 @@ async def get_client_analytics(user_id: str, time_range: str = "30d") -> Optiona
# Build cost basis history # Build cost basis history
cost_basis_history = [] cost_basis_history = []
for record in cost_basis_data: for record in cost_basis_data:
avg_cost_basis = record["cumulative_sats"] / (record["cumulative_fiat"] / 100) if record["cumulative_fiat"] > 0 else 0 # Convert centavos to GTQ avg_cost_basis = record["cumulative_sats"] / record["cumulative_fiat"] if record["cumulative_fiat"] > 0 else 0 # Cost basis = sats / GTQ
# Use transaction_date (which is COALESCE(transaction_time, created_at)) # Use transaction_date (which is COALESCE(transaction_time, created_at))
date_to_use = record["transaction_date"] date_to_use = record["transaction_date"]
if date_to_use is None: if date_to_use is None:

View file

@ -1,53 +0,0 @@
# Currency conversion utilities for Client Extension 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 client API responses
def dashboard_summary_db_to_api(summary_db) -> dict:
"""Convert database dashboard summary to API response"""
return {
"user_id": summary_db.user_id,
"total_sats_accumulated": summary_db.total_sats_accumulated,
"total_fiat_invested_gtq": centavos_to_gtq(summary_db.total_fiat_invested),
"pending_fiat_deposits_gtq": centavos_to_gtq(summary_db.pending_fiat_deposits),
"current_sats_fiat_value_gtq": centavos_to_gtq(int(summary_db.current_sats_fiat_value)),
"average_cost_basis": summary_db.average_cost_basis,
"current_fiat_balance_gtq": centavos_to_gtq(summary_db.current_fiat_balance),
"total_transactions": summary_db.total_transactions,
"dca_mode": summary_db.dca_mode,
"dca_status": summary_db.dca_status,
"last_transaction_date": summary_db.last_transaction_date,
"currency": summary_db.currency
}
def transaction_db_to_api(transaction_db) -> dict:
"""Convert database transaction to API response"""
return {
"id": transaction_db.id,
"amount_sats": transaction_db.amount_sats,
"amount_fiat_gtq": centavos_to_gtq(transaction_db.amount_fiat),
"exchange_rate": transaction_db.exchange_rate,
"transaction_type": transaction_db.transaction_type,
"status": transaction_db.status,
"created_at": transaction_db.created_at,
"transaction_time": transaction_db.transaction_time,
"lamassu_transaction_id": transaction_db.lamassu_transaction_id
}

View file

@ -36,16 +36,16 @@ class ClientTransactionAPI(BaseModel):
lamassu_transaction_id: Optional[str] = None lamassu_transaction_id: Optional[str] = None
# Internal Models for Client Dashboard (Database storage in centavos) # Internal Models for Client Dashboard (Database storage in GTQ)
class ClientDashboardSummary(BaseModel): class ClientDashboardSummary(BaseModel):
"""Internal model - client dashboard summary stored in centavos""" """Internal model - client dashboard summary stored in GTQ"""
user_id: str user_id: str
total_sats_accumulated: int total_sats_accumulated: int
total_fiat_invested: int # Confirmed deposits (in centavos) total_fiat_invested: float # Confirmed deposits in GTQ
pending_fiat_deposits: int # Pending deposits awaiting confirmation (in centavos) pending_fiat_deposits: float # Pending deposits awaiting confirmation in GTQ
current_sats_fiat_value: float # Current fiat value of total sats (in centavos) current_sats_fiat_value: float # Current fiat value of total sats in GTQ
average_cost_basis: float # Average sats per fiat unit average_cost_basis: float # Average sats per GTQ
current_fiat_balance: int # Available balance for DCA (in centavos) current_fiat_balance: float # Available balance for DCA in GTQ
total_transactions: int total_transactions: int
dca_mode: str # 'flow' or 'fixed' dca_mode: str # 'flow' or 'fixed'
dca_status: str # 'active' or 'inactive' dca_status: str # 'active' or 'inactive'
@ -54,10 +54,10 @@ class ClientDashboardSummary(BaseModel):
class ClientTransaction(BaseModel): class ClientTransaction(BaseModel):
"""Internal model - client transaction stored in centavos""" """Internal model - client transaction stored in GTQ"""
id: str id: str
amount_sats: int 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 exchange_rate: float
transaction_type: str # 'flow', 'fixed', 'manual' transaction_type: str # 'flow', 'fixed', 'manual'
status: str status: str

View file

@ -183,8 +183,8 @@ window.app = Vue.createApp({
// Dashboard Methods // Dashboard Methods
formatCurrency(amount) { formatCurrency(amount) {
if (!amount) return 'Q 0.00'; if (!amount) return 'Q 0.00';
// Convert centavos to GTQ (divide by 100) for display // Amount is already in GTQ
const gtqAmount = amount / 100; const gtqAmount = amount;
return new Intl.NumberFormat('es-GT', { return new Intl.NumberFormat('es-GT', {
style: 'currency', style: 'currency',
currency: 'GTQ', currency: 'GTQ',
@ -193,8 +193,8 @@ window.app = Vue.createApp({
formatCurrencyWithCode(amount, currencyCode) { formatCurrencyWithCode(amount, currencyCode) {
if (!amount) return `${currencyCode} 0.00`; if (!amount) return `${currencyCode} 0.00`;
// Convert centavos to currency units (divide by 100) for display // Amount is already in GTQ
const currencyAmount = amount / 100; const currencyAmount = amount;
try { try {
return new Intl.NumberFormat('en-US', { return new Intl.NumberFormat('en-US', {
style: 'currency', style: 'currency',

View file

@ -199,7 +199,7 @@ async def api_export_transactions(
writer.writerow([ writer.writerow([
tx.created_at.isoformat(), tx.created_at.isoformat(),
tx.amount_sats, tx.amount_sats,
tx.amount_fiat / 100, # Convert centavos to GTQ for CSV export tx.amount_fiat, # Amount already in GTQ
tx.exchange_rate, tx.exchange_rate,
tx.transaction_type, tx.transaction_type,
tx.status tx.status