Рефакторинг структуры проекта: перемещение функций и логики работы с базой данных в новый файл helpers_bff.py. Обновлены импорты в fill_db.py и main.py для использования новых функций. Удалены устаревшие функции и классы из main.py, улучшена организация кода.

This commit is contained in:
Redsandyg 2025-06-08 21:23:26 +03:00
parent 155d1002fc
commit 6e804953c0
4 changed files with 267 additions and 249 deletions

View File

@ -1,11 +1,11 @@
import random import random
from uuid import uuid4 from uuid import uuid4
from sqlmodel import Session from sqlmodel import Session
from main import AUTH_DB_ENGINE, TgAgent, Ref, Sale, Account, Company, AgentTransaction, PartnerTransaction, CompanyBalance, AgentBalance from sql_models import TgAgent, Ref, Sale, Account, Company, AgentTransaction, PartnerTransaction, CompanyBalance, AgentBalance
from sqlalchemy import text from sqlalchemy import text
from datetime import datetime, timedelta from datetime import datetime, timedelta
from hashlib import sha256 from hashlib import sha256
from passlib.context import CryptContext from helpers_bff import AUTH_DB_ENGINE, get_password_hash
# Константа: список user_ids # Константа: список user_ids
@ -63,11 +63,6 @@ LOGINS = [
ALL_DESCRIPTIONS = DESCRIPTIONS ALL_DESCRIPTIONS = DESCRIPTIONS
# --- # ---
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def get_password_hash(password):
return pwd_context.hash(password)
def get_date_list(days=7): def get_date_list(days=7):
today = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0) today = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
return [today - timedelta(days=i) for i in range(days, -1, -1)] return [today - timedelta(days=i) for i in range(days, -1, -1)]

90
helpers_bff.py Normal file
View File

@ -0,0 +1,90 @@
from sqlmodel import Session, select, create_engine
from passlib.context import CryptContext
from typing import Optional
from datetime import datetime, timedelta
from bff_models import Token, TransactionStatus
from sql_models import Company, TgAgent, Account, AgentBalance, AgentTransaction, PartnerTransaction, Sale, Ref
from hashlib import sha256
import jwt
from jwt.exceptions import InvalidTokenError
from fastapi import HTTPException, status, Depends, Request
from fastapi.security import OAuth2PasswordBearer
# Конфигурация
AUTH_DATABASE_ADDRESS = "sqlite:///partner.db"
AUTH_DB_ENGINE = create_engine(AUTH_DATABASE_ADDRESS, echo=True)
SECRET_KEY = "supersecretkey"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def get_tg_agent_by_tg_id(db: Session, tg_id: int) -> Optional[TgAgent]:
statement = select(TgAgent).where(TgAgent.tg_id == tg_id)
return db.exec(statement).first()
def get_db():
with Session(AUTH_DB_ENGINE) as session:
yield session
def get_current_account(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
login: str = payload.get("sub")
if login is None:
raise credentials_exception
except InvalidTokenError:
raise credentials_exception
account = get_account_by_login(db, login)
if account is None:
raise credentials_exception
return account
async def get_current_tg_agent(request: Request, db: Session = Depends(get_db)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
auth_header = request.headers.get("Authorization")
if not auth_header or not auth_header.startswith("Bearer "):
raise credentials_exception
hash_value = auth_header.replace("Bearer ", "").strip()
tg_agent = db.exec(select(TgAgent).where(TgAgent.hash == hash_value)).first()
if tg_agent is None:
raise credentials_exception
return tg_agent
def authenticate_tg_agent(engine, tg_id: int):
with Session(engine) as db:
tg_agent = get_tg_agent_by_tg_id(db, tg_id)
if not tg_agent:
return None
return tg_agent
def create_access_token(data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
def get_account_by_login(db: Session, login: str) -> Optional[Account]:
statement = select(Account).where(Account.login == login)
return db.exec(statement).first()

324
main.py
View File

@ -1,204 +1,76 @@
import uuid import uuid
from fastapi import FastAPI, Depends, HTTPException, status, Query, Body, Request from fastapi import (
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm FastAPI,
from sqlmodel import SQLModel, Field, create_engine, Session, select Depends,
from passlib.context import CryptContext HTTPException,
status,
Query,
Body,
)
from fastapi.security import OAuth2PasswordRequestForm
from sqlmodel import SQLModel, Session, select
from typing import Optional, List, Dict from typing import Optional, List, Dict
from datetime import datetime, timedelta from datetime import timedelta, datetime
from bff_models import Token, AccountProfileUpdateRequest, AccountPasswordChangeRequest, AgentTransactionResponse, AutoApproveSettingsRequest, ApproveTransactionsRequest, TransactionStatus, RegisterResponse, DashboardCardsResponse, DashboardChartTotalResponse, DashboardChartAgentResponse, StatAgentsResponse, StatReferralsResponse, StatSalesResponse, BillingCardsResponse, BillingChartStatResponse, BillingChartPieResponse, AccountResponse, CompanyProfileResponse, AccountProfileResponse, AccountProfileUpdateResponse, AccountPasswordChangeResponse, AutoApproveSettingsGetResponse, AutoApproveSettingsUpdateResponse, ApproveTransactionsResult, TgAuthResponse, BillingPayoutsTransactionsResponse from bff_models import (
from tg_models import RefAddRequest, RefResponse, RegisterRequest, TokenRequest, RefAddResponse, RefStatResponse, StatResponse Token,
from uuid import uuid4 RegisterResponse,
from fastapi.responses import JSONResponse DashboardCardsResponse,
DashboardChartTotalResponse,
DashboardChartAgentResponse,
StatAgentsResponse,
StatReferralsResponse,
StatSalesResponse,
BillingCardsResponse,
BillingChartStatResponse,
BillingChartPieResponse,
AccountResponse,
AccountProfileResponse,
AccountProfileUpdateResponse,
AccountPasswordChangeResponse,
AutoApproveSettingsGetResponse,
AutoApproveSettingsUpdateResponse,
ApproveTransactionsResult,
TgAuthResponse,
BillingPayoutsTransactionsResponse,
AccountProfileUpdateRequest,
AccountPasswordChangeRequest,
AutoApproveSettingsRequest,
ApproveTransactionsRequest,
AgentTransactionResponse,
TransactionStatus
)
from tg_models import RefAddRequest, RefResponse, RegisterRequest, RefAddResponse, RefStatResponse, StatResponse
from sql_models import (
Company,
TgAgent,
Ref,
Sale,
AgentTransaction,
PartnerTransaction,
AgentBalance,
Account
)
from sqlalchemy import func from sqlalchemy import func
from hashlib import sha256 import hashlib
import jwt from helpers_bff import (
from jwt.exceptions import InvalidTokenError AUTH_DB_ENGINE,
from pydantic import BaseModel, EmailStr get_db,
from enum import Enum get_tg_agent_by_tg_id,
get_current_account,
# Конфигурация get_current_tg_agent,
AUTH_DATABASE_ADDRESS = "sqlite:///partner.db" create_access_token,
verify_password,
#SQLModel get_account_by_login,
class Company(SQLModel, table=True): ACCESS_TOKEN_EXPIRE_MINUTES,
id: Optional[int] = Field(default=None, primary_key=True) pwd_context,
name: str )
commission: float # процент комиссии
key: str = Field(index=True, unique=True)
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)
auto_approve_transactions: bool = Field(default=False) # Новое поле для автоподтверждения
class TgAgent(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
tg_id: int = Field(index=True, unique=True)
chat_id: Optional[int] = None
phone: Optional[str] = None
name: Optional[str] = None
login: Optional[str] = None
hash: Optional[str] = None
company_id: int = Field(foreign_key="company.id")
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)
class Ref(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
tg_agent_id: int = Field(foreign_key="tgagent.id")
ref: str
description: Optional[str] = None
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)
class Sale(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
cost: float
crediting: float # сколько начислено за продажу
ref: int = Field(foreign_key="ref.id")
sale_id: str
company_id: int = Field(foreign_key="company.id")
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)
class AgentTransaction(SQLModel, table=True):
__tablename__ = "agent_transactions" # Указываем имя таблицы явно
id: Optional[int] = Field(default=None, primary_key=True)
tg_agent_id: int = Field(foreign_key="tgagent.id") # ID агента, связь с TgAgent
amount: float # Используем float для DECIMAL(15,2)
status: str # 'waiting', 'process', 'done', 'reject', 'error'
transaction_group: uuid.UUID = Field(default_factory=uuid.uuid4) # UUID для группировки
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)
class PartnerTransaction(SQLModel, table=True):
__tablename__ = "partner_transactions" # Указываем имя таблицы явно
id: Optional[int] = Field(default=None, primary_key=True)
company_id: int = Field(foreign_key="company.id") # ID партнера, связь с Company
type: str # 'deposit', 'agent_payout', 'service_fee'
amount: float # Используем float для DECIMAL(15,2)
status: str # 'process', 'done', 'error'
transaction_group: uuid.UUID # UUID для группировки, может быть связан с agent_transactions
agent_transaction_id: Optional[int] = Field(default=None, foreign_key="agent_transactions.id") # Связь с агентской транзакцией
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)
class CompanyBalance(SQLModel, table=True):
__tablename__ = "company_balances" # Указываем имя таблицы явно
id: Optional[int] = Field(default=None, primary_key=True)
company_id: int = Field(foreign_key="company.id", unique=True) # ID компании, уникальный баланс на компанию
available_balance: float = Field(default=0.0) # Используем float для DECIMAL(15,2)
pending_balance: float = Field(default=0.0) # Используем float для DECIMAL(15,2)
updated_dttm: datetime = Field(default_factory=datetime.utcnow)
class AgentBalance(SQLModel, table=True):
__tablename__ = "agent_balances" # Указываем имя таблицы явно
id: Optional[int] = Field(default=None, primary_key=True)
tg_agent_id: int = Field(foreign_key="tgagent.id", unique=True) # ID агента, уникальный баланс на агента
available_balance: float = Field(default=0.0) # Используем float для DECIMAL(15,2)
frozen_balance: float = Field(default=0.0) # Используем float для DECIMAL(15,2)
updated_dttm: datetime = Field(default_factory=datetime.utcnow)
class Account(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
login: str = Field(index=True, unique=True)
password_hash: str
firstName: Optional[str] = None
surname: Optional[str] = None
phone: Optional[str] = None
email: Optional[str] = None
company_id: int = Field(foreign_key="company.id")
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)
class AccountProfileUpdateRequest(BaseModel):
firstName: str
surname: str
email: EmailStr
phone: str
class AccountPasswordChangeRequest(BaseModel):
currentPassword: str
newPassword: str
class TransactionStatus(str, Enum): # Определяем Enum для статусов
WAITING = 'waiting'
PROCESS = 'process'
DONE = 'done'
REJECT = 'reject'
ERROR = 'error'
NEW = 'new' # Новый статус
# Новая модель ответа для агентских транзакций с именем агента
class AgentTransactionResponse(BaseModel):
amount: float
status: TransactionStatus # Используем Enum
transaction_group: uuid.UUID
create_dttm: datetime
update_dttm: datetime
agent_name: Optional[str] = None # Поле для имени агента
# Создание движка базы данных # Создание движка базы данных
AUTH_DB_ENGINE = create_engine(AUTH_DATABASE_ADDRESS, echo=True)
SQLModel.metadata.create_all(AUTH_DB_ENGINE) SQLModel.metadata.create_all(AUTH_DB_ENGINE)
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
# FastAPI app # FastAPI app
app = FastAPI() app = FastAPI()
# CRUD
def get_tg_agent_by_tg_id(db: Session, tg_id: int) -> Optional[TgAgent]:
statement = select(TgAgent).where(TgAgent.tg_id == tg_id)
return db.exec(statement).first()
# Dependency
def get_db():
with Session(AUTH_DB_ENGINE) as session:
yield session
def get_current_account(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
login: str = payload.get("sub")
if login is None:
raise credentials_exception
except InvalidTokenError:
raise credentials_exception
account = get_account_by_login(db, login)
if account is None:
raise credentials_exception
return account
# Авторизация
async def get_current_tg_agent(request: Request, db: Session = Depends(get_db)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
auth_header = request.headers.get("Authorization")
if not auth_header or not auth_header.startswith("Bearer "):
raise credentials_exception
hash_value = auth_header.replace("Bearer ", "").strip()
tg_agent = db.exec(select(TgAgent).where(TgAgent.hash == hash_value)).first()
if tg_agent is None:
raise credentials_exception
return tg_agent
# Регистрация
@app.post("/register", tags=["partner-tg"], response_model=RegisterResponse) @app.post("/register", tags=["partner-tg"], response_model=RegisterResponse)
def register(req: RegisterRequest, db: Session = Depends(get_db)): def register(req: RegisterRequest, db: Session = Depends(get_db)):
@ -219,7 +91,7 @@ def register(req: RegisterRequest, db: Session = Depends(get_db)):
company = db.exec(select(Company).where(Company.key == company_key)).first() company = db.exec(select(Company).where(Company.key == company_key)).first()
if not company: if not company:
raise HTTPException(status_code=400, detail="Компания с таким ключом не найдена") raise HTTPException(status_code=400, detail="Компания с таким ключом не найдена")
hash_value = sha256(f"{tg_id}sold".encode()).hexdigest() hash_value = hashlib.sha256(f"{tg_id}sold".encode()).hexdigest()
new_tg_agent = TgAgent( new_tg_agent = TgAgent(
tg_id=tg_id, tg_id=tg_id,
chat_id=chat_id, chat_id=chat_id,
@ -234,30 +106,6 @@ def register(req: RegisterRequest, db: Session = Depends(get_db)):
db.refresh(new_tg_agent) db.refresh(new_tg_agent)
return {"msg": "TgAgent registered successfully"} return {"msg": "TgAgent registered successfully"}
def authenticate_tg_agent(engine, tg_id: int):
with Session(engine) as db:
tg_agent = get_tg_agent_by_tg_id(db, tg_id)
if not tg_agent:
return None
return tg_agent
# Авторизация
SECRET_KEY = "supersecretkey" # Лучше вынести в .env
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60
def create_access_token(data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
@app.post("/token", response_model=Token, tags=["bff", "token"]) @app.post("/token", response_model=Token, tags=["bff", "token"])
def login_account_for_access_token( def login_account_for_access_token(
@ -299,7 +147,7 @@ def add_ref(req: RefAddRequest, current_tg_agent: TgAgent = Depends(get_current_
""" """
new_ref = Ref( new_ref = Ref(
tg_agent_id=current_tg_agent.id, tg_agent_id=current_tg_agent.id,
ref=str(uuid4()), ref=str(uuid.uuid4()),
description=req.description description=req.description
) )
db.add(new_ref) db.add(new_ref)
@ -662,15 +510,6 @@ def tg_auth(hash: str = Body(..., embed=True), db: Session = Depends(get_db)):
return {"msg": "Auth success", "tg_id": tg_agent.tg_id} return {"msg": "Auth success", "tg_id": tg_agent.tg_id}
# --- Новый функционал для Account --- # --- Новый функционал для Account ---
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_account_by_login(db: Session, login: str) -> Optional[Account]:
statement = select(Account).where(Account.login == login)
return db.exec(statement).first()
@app.get("/account", tags=["bff", "account"], response_model=AccountResponse) @app.get("/account", tags=["bff", "account"], response_model=AccountResponse)
@ -754,9 +593,9 @@ def change_account_password(
@app.get("/account/agent-transaction", response_model=List[AgentTransactionResponse], tags=["bff", "account"]) @app.get("/account/agent-transaction", response_model=List[AgentTransactionResponse], tags=["bff", "account"])
def get_account_agent_transactions( def get_account_agent_transactions(
statuses: Optional[List[TransactionStatus]] = Query(None), # Изменено на List[TransactionStatus] statuses: Optional[List[TransactionStatus]] = Query(None),
date_start: str = Query(None), # Добавлен параметр date_start date_start: str = Query(None),
date_end: str = Query(None), # Добавлен параметр date_end date_end: str = Query(None),
current_account: Account = Depends(get_current_account), current_account: Account = Depends(get_current_account),
db: Session = Depends(get_db) db: Session = Depends(get_db)
): ):
@ -788,23 +627,26 @@ def get_account_agent_transactions(
# Формируем список ответов в формате AgentTransactionResponse # Формируем список ответов в формате AgentTransactionResponse
agent_transactions_response = [] agent_transactions_response = []
for agent_trans, agent in results: for agent_trans, agent in results:
try:
status_enum = TransactionStatus(agent_trans.status)
except ValueError:
# Если статус из БД не соответствует Enum, используем статус ERROR
status_enum = TransactionStatus.ERROR
agent_transactions_response.append( agent_transactions_response.append(
AgentTransactionResponse( AgentTransactionResponse(
amount=agent_trans.amount, amount=agent_trans.amount,
status=agent_trans.status, status=status_enum,
transaction_group=agent_trans.transaction_group, transaction_group=agent_trans.transaction_group,
create_dttm=agent_trans.create_dttm, create_dttm=agent_trans.create_dttm,
update_dttm=agent_trans.update_dttm, update_dttm=agent_trans.update_dttm,
agent_name=agent.name # Используем имя агента agent_name=agent.name
) )
) )
return agent_transactions_response return agent_transactions_response
# Модель запроса для POST /account/auto-approve
class AutoApproveSettingsRequest(BaseModel):
auto_approve: bool
apply_to_current: Optional[bool] = False
@app.get("/account/auto-approve", tags=["bff", "account"], response_model=AutoApproveSettingsGetResponse) @app.get("/account/auto-approve", tags=["bff", "account"], response_model=AutoApproveSettingsGetResponse)
def get_auto_approve_settings( def get_auto_approve_settings(
@ -854,8 +696,8 @@ def update_auto_approve_settings(
# Находим соответствующие партнерские транзакции и обновляем их статус # Находим соответствующие партнерские транзакции и обновляем их статус
partner_transactions_to_update = db.exec( partner_transactions_to_update = db.exec(
select(PartnerTransaction) select(PartnerTransaction)
.where(PartnerTransaction.agent_transaction_id == agent_trans.id) # Используем связь по ID .where(PartnerTransaction.agent_transaction_id == agent_trans.id)
.where(PartnerTransaction.status == TransactionStatus.PROCESS) # Предполагаем, что связанные партнерские транзакции в статусе PROCESS .where(PartnerTransaction.status == TransactionStatus.PROCESS)
).all() ).all()
for partner_trans in partner_transactions_to_update: for partner_trans in partner_transactions_to_update:
partner_trans.status = TransactionStatus.NEW partner_trans.status = TransactionStatus.NEW
@ -867,9 +709,7 @@ def update_auto_approve_settings(
return {"msg": "Настройка автоматического подтверждения обновлена", "auto_approve_transactions": company.auto_approve_transactions} return {"msg": "Настройка автоматического подтверждения обновлена", "auto_approve_transactions": company.auto_approve_transactions}
# Модель запроса для POST /account/approve-transactions
class ApproveTransactionsRequest(BaseModel):
transaction_ids: List[uuid.UUID]
@app.post("/account/approve-transactions", tags=["bff", "account"], response_model=ApproveTransactionsResult) @app.post("/account/approve-transactions", tags=["bff", "account"], response_model=ApproveTransactionsResult)
def approve_agent_transactions( def approve_agent_transactions(
@ -893,11 +733,11 @@ def approve_agent_transactions(
.join(TgAgent) .join(TgAgent)
.where(TgAgent.company_id == company_id) .where(TgAgent.company_id == company_id)
.where(AgentTransaction.transaction_group.in_(req.transaction_ids)) .where(AgentTransaction.transaction_group.in_(req.transaction_ids))
.where(AgentTransaction.status == TransactionStatus.WAITING) # Утверждаем только транзакции в статусе 'waiting' .where(AgentTransaction.status == TransactionStatus.WAITING)
).all() ).all()
for agent_trans in transactions_to_approve: for agent_trans in transactions_to_approve:
agent_trans.status = TransactionStatus.NEW # Переводим в статус 'new' agent_trans.status = TransactionStatus.NEW
agent_trans.update_dttm = datetime.utcnow() agent_trans.update_dttm = datetime.utcnow()
db.add(agent_trans) db.add(agent_trans)
approved_count += 1 approved_count += 1

93
sql_models.py Normal file
View File

@ -0,0 +1,93 @@
from typing import Optional
from datetime import datetime
import uuid
from sqlmodel import SQLModel, Field
class Company(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
commission: float # процент комиссии
key: str = Field(index=True, unique=True)
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)
auto_approve_transactions: bool = Field(default=False)
class TgAgent(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
tg_id: int = Field(index=True, unique=True)
chat_id: Optional[int] = None
phone: Optional[str] = None
name: Optional[str] = None
login: Optional[str] = None
hash: Optional[str] = None
company_id: int = Field(foreign_key="company.id")
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)
class Ref(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
tg_agent_id: int = Field(foreign_key="tgagent.id")
ref: str
description: Optional[str] = None
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)
class Sale(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
cost: float
crediting: float # сколько начислено за продажу
ref: int = Field(foreign_key="ref.id")
sale_id: str
company_id: int = Field(foreign_key="company.id")
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)
class AgentTransaction(SQLModel, table=True):
__tablename__ = "agent_transactions"
id: Optional[int] = Field(default=None, primary_key=True)
tg_agent_id: int = Field(foreign_key="tgagent.id")
amount: float
status: str
transaction_group: uuid.UUID = Field(default_factory=uuid.uuid4)
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)
class PartnerTransaction(SQLModel, table=True):
__tablename__ = "partner_transactions"
id: Optional[int] = Field(default=None, primary_key=True)
company_id: int = Field(foreign_key="company.id")
type: str
amount: float
status: str
transaction_group: uuid.UUID
agent_transaction_id: Optional[int] = Field(default=None, foreign_key="agent_transactions.id")
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)
class CompanyBalance(SQLModel, table=True):
__tablename__ = "company_balances"
id: Optional[int] = Field(default=None, primary_key=True)
company_id: int = Field(foreign_key="company.id", unique=True)
available_balance: float = Field(default=0.0)
pending_balance: float = Field(default=0.0)
updated_dttm: datetime = Field(default_factory=datetime.utcnow)
class AgentBalance(SQLModel, table=True):
__tablename__ = "agent_balances"
id: Optional[int] = Field(default=None, primary_key=True)
tg_agent_id: int = Field(foreign_key="tgagent.id", unique=True)
available_balance: float = Field(default=0.0)
frozen_balance: float = Field(default=0.0)
updated_dttm: datetime = Field(default_factory=datetime.utcnow)
class Account(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
login: str = Field(index=True, unique=True)
password_hash: str
firstName: Optional[str] = None
surname: Optional[str] = None
phone: Optional[str] = None
email: Optional[str] = None
company_id: int = Field(foreign_key="company.id")
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)