mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-05-19 09:28:47 +02:00
All checks were successful
Deploy to VPS / deploy (push) Successful in 14s
Budget/transactions/salarios totals summed Transaction.amount directly,
so USD/EUR entries were treated as CRC and effectively disappeared from
the dashboard (the analytics fix in 9a80f2a only covered analytics).
Adds a shared get_converted_amount_expr() helper driven by the full
Currency enum — USD/EUR via ExchangeRate-API, BTC/XMR via CoinGecko —
and wires it into every func.sum(Transaction.amount) site.
Also starts a background task in the FastAPI lifespan that force-refreshes
every currency 4x/day, persisting USD to the DB and updating in-memory
caches for the rest. Failures are swallowed per-currency so a CoinGecko
outage cannot take out USD/EUR, and the last-known rate is always retained.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
57 lines
1.7 KiB
Python
57 lines
1.7 KiB
Python
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
from fastapi import APIRouter, Depends, Query
|
|
from pydantic import BaseModel
|
|
from sqlmodel import Session, col, func, select
|
|
|
|
from app.auth import get_current_user
|
|
from app.db import get_session
|
|
from app.models.models import Transaction, TransactionRead, TransactionType
|
|
from app.services.exchange_rate import get_converted_amount_expr
|
|
|
|
router = APIRouter(prefix="/salarios", tags=["salarios"])
|
|
|
|
|
|
class SalariosSummary(BaseModel):
|
|
count: int
|
|
total_amount: float
|
|
latest_date: Optional[datetime] = None
|
|
|
|
|
|
@router.get("/", response_model=list[TransactionRead])
|
|
def list_salarios(
|
|
limit: int = Query(default=50, le=500),
|
|
offset: int = 0,
|
|
session: Session = Depends(get_session),
|
|
_user: str = Depends(get_current_user),
|
|
):
|
|
query = (
|
|
select(Transaction)
|
|
.where(Transaction.transaction_type == TransactionType.DEPOSITO)
|
|
.order_by(col(Transaction.date).desc())
|
|
.offset(offset)
|
|
.limit(limit)
|
|
)
|
|
return session.exec(query).all()
|
|
|
|
|
|
@router.get("/summary", response_model=SalariosSummary)
|
|
def salarios_summary(
|
|
session: Session = Depends(get_session),
|
|
_user: str = Depends(get_current_user),
|
|
):
|
|
amount_crc = get_converted_amount_expr(session)
|
|
result = session.exec(
|
|
select(
|
|
func.count(),
|
|
func.coalesce(func.sum(amount_crc), 0),
|
|
func.max(Transaction.date),
|
|
).where(Transaction.transaction_type == TransactionType.DEPOSITO)
|
|
).first()
|
|
return SalariosSummary(
|
|
count=result[0] if result else 0,
|
|
total_amount=float(result[1]) if result else 0.0,
|
|
latest_date=result[2] if result else None,
|
|
)
|