mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-05-19 13:48:48 +02:00
All checks were successful
Deploy to VPS / deploy (push) Successful in 45s
- Expand Account model with account_type (pension, savings, liability, crypto), new banks/currencies (BTC, XMR, FCL, ROP, VOL, MEMP, MPAT, MORTGAGE), and next_payment field - Add exchange rate endpoint (BCCR integration), analytics endpoint, paste-import for transactions, and API token management - Add PWA manifest, service worker, and app icons - Redesign dashboard, transactions, transfers, and login pages with theme support - Add billing cycle selector, confirm dialog, and paste import modal components - One-time DB reset in deploy workflow for schema migration Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
198 lines
4.4 KiB
Python
198 lines
4.4 KiB
Python
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
|