mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-05-19 10:28:48 +02:00
Add budget module: FastAPI backend + React frontend
Some checks failed
Deploy to VPS / deploy (push) Failing after 7s
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>
This commit is contained in:
0
backend/app/models/__init__.py
Normal file
0
backend/app/models/__init__.py
Normal file
135
backend/app/models/models.py
Normal file
135
backend/app/models/models.py
Normal file
@@ -0,0 +1,135 @@
|
||||
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
|
||||
Reference in New Issue
Block a user