from __future__ import annotations from datetime import date from pydantic import BaseModel, Field class BudgetOut(BaseModel): id: int user_id: int name: str total_amount: float start_date: date end_date: date currency: str is_active: bool class BudgetStatusOut(BaseModel): budget: BudgetOut today: date days_left: int total_spent: float remaining: float daily_limit: float spent_today: float remaining_today: float is_over_daily: bool is_over_budget: bool is_expired: bool selected: bool = False class ExpenseOut(BaseModel): id: int budget_id: int amount: float note: str | None spent_at: date class ExpensesPageOut(BaseModel): page: int total_pages: int total_count: int total_sum: float page_size: int budget_id: int items: list[ExpenseOut] class BudgetsListOut(BaseModel): items: list[BudgetStatusOut] = Field(default_factory=list) class TelegramLoginIn(BaseModel): id: int first_name: str last_name: str | None = None username: str | None = None photo_url: str | None = None auth_date: int hash: str class InternalAuthIn(BaseModel): """Service auth for trusted clients (Telegram bot) acting as a user.""" user_id: int = Field(gt=0) first_name: str | None = None last_name: str | None = None username: str | None = None class AuthUserOut(BaseModel): user_id: int first_name: str | None = None last_name: str | None = None username: str | None = None photo_url: str | None = None class AuthSessionOut(BaseModel): access_token: str token_type: str = "bearer" user: AuthUserOut class ExpenseCreateIn(BaseModel): amount: float = Field(gt=0) note: str | None = None spent_at: date | None = None budget_id: int | None = None class BudgetCreateIn(BaseModel): total_amount: float = Field(gt=0) end_date: date name: str = "Бюджет" is_active: bool = True select: bool = True class BudgetUpdateIn(BaseModel): total_amount: float | None = Field(default=None, gt=0) end_date: date | None = None name: str | None = None reset_expenses: bool = False class BudgetActiveIn(BaseModel): is_active: bool class BudgetUpsertIn(BaseModel): """Legacy upsert against selected budget (or create if none).""" total_amount: float = Field(gt=0) end_date: date reset_expenses: bool = True name: str | None = None budget_id: int | None = None class UndoExpenseOut(BaseModel): deleted_amount: float status: BudgetStatusOut