mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-05-19 09:48:49 +02:00
Add budget module and push notifications for transactions
All checks were successful
Deploy to VPS / deploy (push) Successful in 34s
All checks were successful
Deploy to VPS / deploy (push) Successful in 34s
Budget: recurring items CRUD, yearly/monthly projections with no-double-count logic, and full UI (overview, monthly detail, recurring items manager). Push notifications: Web Push via VAPID keys, triggered on transaction creation from n8n. Includes service worker handlers, frontend subscription flow, and a test button on the Dashboard (temporary). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,11 +2,24 @@ import enum
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import Column
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy import JSON, Column
|
||||
from sqlmodel import Field, Relationship, SQLModel
|
||||
|
||||
|
||||
class RecurringItemType(str, enum.Enum):
|
||||
INCOME = "INCOME"
|
||||
EXPENSE = "EXPENSE"
|
||||
SAVINGS = "SAVINGS"
|
||||
|
||||
|
||||
class RecurringFrequency(str, enum.Enum):
|
||||
WEEKLY = "WEEKLY"
|
||||
MONTHLY = "MONTHLY"
|
||||
QUARTERLY = "QUARTERLY"
|
||||
BIANNUAL = "BIANNUAL"
|
||||
YEARLY = "YEARLY"
|
||||
|
||||
|
||||
class TransactionType(str, enum.Enum):
|
||||
COMPRA = "COMPRA"
|
||||
DEVOLUCION = "DEVOLUCION"
|
||||
@@ -207,7 +220,7 @@ class UserSettings(SQLModel, table=True):
|
||||
key: str = Field(index=True, unique=True, default="default")
|
||||
data: dict = Field(
|
||||
default_factory=dict,
|
||||
sa_column=Column(JSONB, nullable=False, server_default="{}"),
|
||||
sa_column=Column(JSON, nullable=False, server_default="{}"),
|
||||
)
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
@@ -220,3 +233,69 @@ class UserSettingsRead(SQLModel):
|
||||
|
||||
class UserSettingsUpdate(SQLModel):
|
||||
data: dict
|
||||
|
||||
|
||||
# --- Recurring Item ---
|
||||
|
||||
|
||||
class RecurringItemBase(SQLModel):
|
||||
name: str
|
||||
amount: float
|
||||
currency: Currency = Currency.CRC
|
||||
item_type: RecurringItemType
|
||||
frequency: RecurringFrequency = RecurringFrequency.MONTHLY
|
||||
day_of_month: Optional[int] = None
|
||||
month_of_year: Optional[int] = None
|
||||
override_amounts: Optional[dict] = Field(
|
||||
default=None,
|
||||
sa_column=Column(JSON, nullable=True),
|
||||
)
|
||||
category_id: Optional[int] = Field(default=None, foreign_key="category.id")
|
||||
is_active: bool = True
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
class RecurringItem(RecurringItemBase, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
category: Optional[Category] = Relationship()
|
||||
|
||||
|
||||
class RecurringItemCreate(RecurringItemBase):
|
||||
pass
|
||||
|
||||
|
||||
class RecurringItemRead(RecurringItemBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
category: Optional[CategoryRead] = None
|
||||
|
||||
|
||||
class RecurringItemUpdate(SQLModel):
|
||||
name: Optional[str] = None
|
||||
amount: Optional[float] = None
|
||||
currency: Optional[Currency] = None
|
||||
item_type: Optional[RecurringItemType] = None
|
||||
frequency: Optional[RecurringFrequency] = None
|
||||
day_of_month: Optional[int] = None
|
||||
month_of_year: Optional[int] = None
|
||||
override_amounts: Optional[dict] = None
|
||||
category_id: Optional[int] = None
|
||||
is_active: Optional[bool] = None
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
# --- Push Subscription ---
|
||||
|
||||
|
||||
class PushSubscription(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
endpoint: str = Field(unique=True)
|
||||
p256dh: str
|
||||
auth: str
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
|
||||
class PushSubscriptionCreate(SQLModel):
|
||||
endpoint: str
|
||||
keys: dict # {"p256dh": "...", "auth": "..."}
|
||||
|
||||
Reference in New Issue
Block a user