mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-07-17 13:28:46 +02:00
- FK rules via migration 7884505b16b3 (verified on dev with a rolled- back probe): transactions/recurring items SET NULL on category delete (this also fixes the 500 that DELETE /categories raised for in-use categories), water readings CASCADE with their receipt. Models declare ondelete to stay in sync. (ARCH-09, BE-11) - compute_actuals_by_source: 3 grouped queries replace 10 single- aggregate round-trips; behavior pinned by the existing cycle suite. (BE-21) - compute_cc_by_category prefetches category names (ARCH-15); agent pension latest-per-fund resolved in SQL (BE-04); municipal water consumption batched into one grouped query (BE-05). - Deterministic pagination: date DESC, id DESC on all transaction listings. (ARCH-12) - New agent-tool tests (session binding, JSON-safe output, batched queries): 64 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
59 lines
1.8 KiB
Python
59 lines
1.8 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"])
|
|
|
|
SALARIO_TYPES = (TransactionType.SALARY, TransactionType.DEPOSITO)
|
|
|
|
|
|
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(col(Transaction.transaction_type).in_(SALARIO_TYPES))
|
|
.order_by(col(Transaction.date).desc(), col(Transaction.id).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(col(Transaction.transaction_type).in_(SALARIO_TYPES))
|
|
).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,
|
|
)
|