import enum from datetime import datetime from typing import Optional from sqlmodel import Field, Relationship, SQLModel class TransactionType(str, enum.Enum): COMPRA = "COMPRA" DEVOLUCION = "DEVOLUCION" class TransactionSource(str, enum.Enum): CREDIT_CARD = "CREDIT_CARD" CASH = "CASH" TRANSFER = "TRANSFER" class Currency(str, enum.Enum): CRC = "CRC" USD = "USD" BTC = "BTC" XMR = "XMR" class Bank(str, enum.Enum): BAC = "BAC" BCR = "BCR" DAVIVIENDA = "DAVIVIENDA" FCL = "FCL" ROP = "ROP" VOL = "VOL" MEMP = "MEMP" MPAT = "MPAT" MORTGAGE = "MORTGAGE" class AccountType(str, enum.Enum): BANK = "BANK" PENSION = "PENSION" CRYPTO = "CRYPTO" SAVINGS = "SAVINGS" LIABILITY = "LIABILITY" # --- Category --- class CategoryBase(SQLModel): name: str = Field(index=True, unique=True) icon: str = "tag" auto_match_patterns: Optional[str] = Field( default=None, description="Comma-separated merchant substrings for auto-matching", ) class Category(CategoryBase, table=True): id: Optional[int] = Field(default=None, primary_key=True) transactions: list["Transaction"] = Relationship(back_populates="category") class CategoryCreate(CategoryBase): pass class CategoryRead(CategoryBase): id: int class CategoryUpdate(SQLModel): name: Optional[str] = None icon: Optional[str] = None auto_match_patterns: Optional[str] = None # --- Account --- class AccountBase(SQLModel): bank: Bank currency: Currency label: str balance: float = 0.0 account_type: AccountType = AccountType.BANK next_payment: Optional[float] = None class Account(AccountBase, table=True): id: Optional[int] = Field(default=None, primary_key=True) updated_at: datetime = Field(default_factory=datetime.utcnow) class AccountCreate(AccountBase): pass class AccountRead(AccountBase): id: int updated_at: datetime class AccountUpdate(SQLModel): balance: Optional[float] = None label: Optional[str] = None next_payment: Optional[float] = None # --- Transaction --- class TransactionBase(SQLModel): amount: float currency: Currency = Currency.CRC merchant: str city: Optional[str] = None date: datetime card_type: Optional[str] = None card_last4: Optional[str] = None authorization_code: Optional[str] = None reference: Optional[str] = Field(default=None, index=True) transaction_type: TransactionType = TransactionType.COMPRA source: TransactionSource = TransactionSource.CREDIT_CARD bank: Bank = Bank.BAC notes: Optional[str] = None category_id: Optional[int] = Field(default=None, foreign_key="category.id") class Transaction(TransactionBase, table=True): id: Optional[int] = Field(default=None, primary_key=True) created_at: datetime = Field(default_factory=datetime.utcnow) category: Optional[Category] = Relationship(back_populates="transactions") class TransactionCreate(TransactionBase): pass class TransactionRead(TransactionBase): id: int created_at: datetime category: Optional[CategoryRead] = None class TransactionUpdate(SQLModel): amount: Optional[float] = None currency: Optional[Currency] = None merchant: Optional[str] = None city: Optional[str] = None date: Optional[datetime] = None transaction_type: Optional[TransactionType] = None source: Optional[TransactionSource] = None notes: Optional[str] = None category_id: Optional[int] = None # --- Exchange Rate --- class ExchangeRate(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) date: datetime buy_rate: float sell_rate: float fetched_at: datetime = Field(default_factory=datetime.utcnow) class ExchangeRateRead(SQLModel): buy_rate: float sell_rate: float date: datetime fetched_at: datetime # --- API Token --- class APIToken(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) name: str token_hash: str = Field(index=True) created_at: datetime = Field(default_factory=datetime.utcnow) expires_at: Optional[datetime] = None is_active: bool = True class APITokenCreate(SQLModel): name: str expires_days: Optional[int] = None class APITokenRead(SQLModel): id: int name: str created_at: datetime expires_at: Optional[datetime] is_active: bool