Обновление приложения

This commit is contained in:
2026-04-28 22:37:19 +03:00
parent 7d00cf062e
commit 52594b5dc6
99 changed files with 5736 additions and 1052 deletions

View File

@@ -0,0 +1,29 @@
from app.schemas.user import RoleOut, UserCreate, UserUpdate, UserOut, UserWithRole
from app.schemas.currency import CurrencyCreate, CurrencyUpdate, CurrencyOut, ExchangeRateCreate, ExchangeRateOut
from app.schemas.location import LocationCreate, LocationUpdate, LocationOut
from app.schemas.account import AccountTypeOut, AccountCreate, AccountUpdate, AccountOut
from app.schemas.category import CategoryCreate, CategoryUpdate, CategoryOut
from app.schemas.budget import BudgetCreate, BudgetUpdate, BudgetOut
from app.schemas.operation import (
OperationTypeOut,
OperationCreate, OperationUpdate, OperationOut,
OperationRelationCreate, OperationRelationOut,
TransferCreate,
)
from app.schemas.calendar import CalendarEventCreate, CalendarEventUpdate, CalendarEventOut
from app.schemas.auth import Token, TokenData, LoginRequest
__all__ = [
"RoleOut", "UserCreate", "UserUpdate", "UserOut", "UserWithRole",
"CurrencyCreate", "CurrencyUpdate", "CurrencyOut",
"ExchangeRateCreate", "ExchangeRateOut",
"LocationCreate", "LocationUpdate", "LocationOut",
"AccountTypeOut", "AccountCreate", "AccountUpdate", "AccountOut",
"CategoryCreate", "CategoryUpdate", "CategoryOut",
"BudgetCreate", "BudgetUpdate", "BudgetOut",
"OperationTypeOut",
"OperationCreate", "OperationUpdate", "OperationOut",
"OperationRelationCreate", "OperationRelationOut", "TransferCreate",
"CalendarEventCreate", "CalendarEventUpdate", "CalendarEventOut",
"Token", "TokenData", "LoginRequest",
]

View File

@@ -0,0 +1,53 @@
from datetime import datetime
from decimal import Decimal
from pydantic import BaseModel, Field
from app.schemas.currency import CurrencyOut
class AccountTypeOut(BaseModel):
id: int
name: str
icon: str | None = None
description: str | None = None
model_config = {"from_attributes": True}
class AccountCreate(BaseModel):
name: str = Field(..., max_length=200)
account_type_id: int
currency_id: int
initial_balance: Decimal = Decimal("0.00")
credit_limit: Decimal | None = None
description: str | None = None
icon: str | None = None
color: str | None = Field(None, pattern=r"^#[0-9A-Fa-f]{6}$")
class AccountUpdate(BaseModel):
name: str | None = Field(None, max_length=200)
is_active: bool | None = None
credit_limit: Decimal | None = None
description: str | None = None
icon: str | None = None
color: str | None = Field(None, pattern=r"^#[0-9A-Fa-f]{6}$")
class AccountOut(BaseModel):
id: int
user_id: int
name: str
account_type_id: int
account_type: AccountTypeOut
currency_id: int
currency: CurrencyOut
balance: Decimal
initial_balance: Decimal
is_active: bool
credit_limit: Decimal | None = None
description: str | None = None
icon: str | None = None
color: str | None = None
created_at: datetime
model_config = {"from_attributes": True}

View File

@@ -0,0 +1,17 @@
from pydantic import BaseModel
class LoginRequest(BaseModel):
username: str
password: str
class Token(BaseModel):
access_token: str
refresh_token: str
token_type: str = "bearer"
class TokenData(BaseModel):
user_id: int | None = None
role: str | None = None

View File

@@ -0,0 +1,43 @@
from datetime import datetime, date
from decimal import Decimal
from pydantic import BaseModel, Field
from app.schemas.category import CategoryOut
from app.schemas.currency import CurrencyOut
class BudgetCreate(BaseModel):
category_id: int
name: str = Field(..., max_length=200)
amount: Decimal = Field(..., gt=0)
currency_id: int
period_type: str = Field(..., pattern="^(monthly|weekly|yearly|custom)$")
start_date: date
end_date: date | None = None
is_active: bool = True
class BudgetUpdate(BaseModel):
name: str | None = None
amount: Decimal | None = Field(None, gt=0)
period_type: str | None = None
end_date: date | None = None
is_active: bool | None = None
class BudgetOut(BaseModel):
id: int
user_id: int
category_id: int
category: CategoryOut
name: str
amount: Decimal
currency_id: int
currency: CurrencyOut
period_type: str
start_date: date
end_date: date | None = None
is_active: bool
created_at: datetime
spent: Decimal | None = None # computed field
model_config = {"from_attributes": True}

View File

@@ -0,0 +1,55 @@
from datetime import datetime, date, time
from decimal import Decimal
from pydantic import BaseModel, Field
class CalendarEventCreate(BaseModel):
title: str = Field(..., max_length=200)
description: str | None = None
event_date: date
event_time: time | None = None
is_recurring: bool = False
recurrence_pattern: str | None = Field(None, pattern="^(daily|weekly|monthly|yearly)$")
recurrence_end_date: date | None = None
related_operation_id: int | None = None
category_id: int | None = None
amount: Decimal | None = None
currency_id: int | None = None
reminder_days: int | None = Field(None, ge=0)
is_completed: bool = False
class CalendarEventUpdate(BaseModel):
title: str | None = Field(None, max_length=200)
description: str | None = None
event_date: date | None = None
event_time: time | None = None
is_recurring: bool | None = None
recurrence_pattern: str | None = None
recurrence_end_date: date | None = None
related_operation_id: int | None = None
category_id: int | None = None
amount: Decimal | None = None
reminder_days: int | None = None
is_completed: bool | None = None
class CalendarEventOut(BaseModel):
id: int
user_id: int
title: str
description: str | None = None
event_date: date
event_time: time | None = None
is_recurring: bool
recurrence_pattern: str | None = None
recurrence_end_date: date | None = None
related_operation_id: int | None = None
category_id: int | None = None
amount: Decimal | None = None
currency_id: int | None = None
reminder_days: int | None = None
is_completed: bool
created_at: datetime
model_config = {"from_attributes": True}

View File

@@ -0,0 +1,30 @@
from datetime import datetime
from pydantic import BaseModel, Field
class CategoryCreate(BaseModel):
name: str = Field(..., max_length=200)
type: str = Field(..., pattern="^(income|expense|transfer)$")
parent_id: int | None = None
icon: str | None = None
color: str | None = Field(None, pattern=r"^#[0-9A-Fa-f]{6}$")
class CategoryUpdate(BaseModel):
name: str | None = Field(None, max_length=200)
parent_id: int | None = None
icon: str | None = None
color: str | None = Field(None, pattern=r"^#[0-9A-Fa-f]{6}$")
class CategoryOut(BaseModel):
id: int
user_id: int | None = None
name: str
type: str
parent_id: int | None = None
icon: str | None = None
color: str | None = None
created_at: datetime
model_config = {"from_attributes": True}

View File

@@ -0,0 +1,48 @@
from datetime import datetime, date
from decimal import Decimal
from pydantic import BaseModel, Field
class CurrencyCreate(BaseModel):
code: str = Field(..., min_length=3, max_length=3)
name: str = Field(..., max_length=100)
symbol: str | None = Field(None, max_length=10)
is_active: bool = True
class CurrencyUpdate(BaseModel):
name: str | None = Field(None, max_length=100)
symbol: str | None = Field(None, max_length=10)
is_active: bool | None = None
class CurrencyOut(BaseModel):
id: int
code: str
name: str
symbol: str | None = None
is_active: bool
model_config = {"from_attributes": True}
class ExchangeRateCreate(BaseModel):
currency_from_id: int
currency_to_id: int
rate: Decimal = Field(..., gt=0)
rate_date: date
source: str | None = None
class ExchangeRateOut(BaseModel):
id: int
currency_from_id: int
currency_to_id: int
rate: Decimal
rate_date: date
source: str | None = None
created_at: datetime
currency_from: CurrencyOut
currency_to: CurrencyOut
model_config = {"from_attributes": True}

View File

@@ -0,0 +1,41 @@
from datetime import datetime
from decimal import Decimal
from pydantic import BaseModel, Field
class LocationCreate(BaseModel):
name: str = Field(..., max_length=200)
type: str | None = Field(None, max_length=50)
address: str | None = None
city: str | None = Field(None, max_length=100)
country: str | None = Field(None, max_length=100)
latitude: Decimal | None = None
longitude: Decimal | None = None
parent_id: int | None = None
class LocationUpdate(BaseModel):
name: str | None = Field(None, max_length=200)
type: str | None = None
address: str | None = None
city: str | None = None
country: str | None = None
latitude: Decimal | None = None
longitude: Decimal | None = None
parent_id: int | None = None
class LocationOut(BaseModel):
id: int
user_id: int | None = None
name: str
type: str | None = None
address: str | None = None
city: str | None = None
country: str | None = None
latitude: Decimal | None = None
longitude: Decimal | None = None
parent_id: int | None = None
created_at: datetime
model_config = {"from_attributes": True}

View File

@@ -0,0 +1,89 @@
from datetime import datetime
from decimal import Decimal
from pydantic import BaseModel, Field
from app.schemas.currency import CurrencyOut
from app.schemas.category import CategoryOut
from app.schemas.location import LocationOut
class OperationTypeOut(BaseModel):
id: int
name: str
description: str | None = None
model_config = {"from_attributes": True}
class OperationCreate(BaseModel):
account_id: int
category_id: int | None = None
operation_type_id: int
amount: Decimal = Field(..., gt=0)
currency_id: int
amount_converted: Decimal | None = None
exchange_rate: Decimal | None = None
date: datetime
description: str | None = None
location_id: int | None = None
is_planned: bool = False
class OperationUpdate(BaseModel):
category_id: int | None = None
amount: Decimal | None = Field(None, gt=0)
date: datetime | None = None
description: str | None = None
location_id: int | None = None
is_planned: bool | None = None
class OperationOut(BaseModel):
id: int
user_id: int
account_id: int
category_id: int | None = None
category: CategoryOut | None = None
operation_type_id: int
operation_type: OperationTypeOut
amount: Decimal
currency_id: int
currency: CurrencyOut
amount_converted: Decimal | None = None
exchange_rate: Decimal | None = None
date: datetime
description: str | None = None
location_id: int | None = None
location: LocationOut | None = None
is_planned: bool
created_at: datetime
model_config = {"from_attributes": True}
class OperationRelationCreate(BaseModel):
operation_from_id: int
operation_to_id: int
relation_type: str = Field(..., pattern="^(transfer|split|correction)$")
class OperationRelationOut(BaseModel):
id: int
operation_from_id: int
operation_to_id: int
relation_type: str
created_at: datetime
model_config = {"from_attributes": True}
class TransferCreate(BaseModel):
"""Convenience schema: creates two linked operations in one request."""
from_account_id: int
to_account_id: int
amount: Decimal = Field(..., gt=0)
currency_id: int
amount_converted: Decimal | None = None
exchange_rate: Decimal | None = None
date: datetime
description: str | None = None
location_id: int | None = None

View File

@@ -0,0 +1,39 @@
from datetime import datetime
from pydantic import BaseModel, EmailStr, Field
class RoleOut(BaseModel):
id: int
name: str
description: str | None = None
model_config = {"from_attributes": True}
class UserCreate(BaseModel):
username: str = Field(..., min_length=3, max_length=100)
email: EmailStr
password: str = Field(..., min_length=8)
role_id: int = 3 # default: user
class UserUpdate(BaseModel):
username: str | None = Field(None, min_length=3, max_length=100)
email: EmailStr | None = None
is_active: bool | None = None
role_id: int | None = None
class UserOut(BaseModel):
id: int
username: str
email: str
role_id: int
is_active: bool
created_at: datetime
model_config = {"from_attributes": True}
class UserWithRole(UserOut):
role: RoleOut