mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-05-19 13:48:48 +02:00
Some checks failed
Deploy to VPS / deploy (push) Failing after 7s
Backend: FastAPI + PostgreSQL with models for accounts, transactions, and categories. Auto-categorization from merchant patterns, token auth, CRUD endpoints, and seed data for 16 categories and 4 bank accounts. Frontend: Login, Dashboard (account balances + recent charges), Transactions (full CRUD table with search/filter), Cash & Transfers view. Dark theme with emerald/cyan accents, responsive layout. Infrastructure: Updated docker-compose for backend + db services, nginx proxy config for API routing, deploy workflow with secrets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
136 lines
3.1 KiB
Python
136 lines
3.1 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"
|
|
|
|
|
|
class Bank(str, enum.Enum):
|
|
BAC = "BAC"
|
|
BCR = "BCR"
|
|
DAVIVIENDA = "DAVIVIENDA"
|
|
|
|
|
|
# --- 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 = Field(description="e.g. 'BAC Colones', 'BAC Dólares'")
|
|
balance: float = 0.0
|
|
|
|
|
|
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
|
|
|
|
|
|
# --- 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] = None
|
|
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
|