Compare commits
5 Commits
d113ae4adb
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c8cbe753e | ||
|
|
a6308582cb | ||
|
|
9157570e58 | ||
|
|
bf6a6a8987 | ||
|
|
4f366680bf |
@@ -145,7 +145,6 @@ class CompanyProfileResponse(BaseModel):
|
|||||||
name: str
|
name: str
|
||||||
key: str
|
key: str
|
||||||
commission: float
|
commission: float
|
||||||
agent_commission: float
|
|
||||||
|
|
||||||
class AccountProfileResponse(BaseModel):
|
class AccountProfileResponse(BaseModel):
|
||||||
firstName: Optional[str] = None
|
firstName: Optional[str] = None
|
||||||
@@ -189,3 +188,20 @@ class IntegrationTokenCreateRequest(BaseModel):
|
|||||||
class IntegrationTokenUpdateRequest(BaseModel):
|
class IntegrationTokenUpdateRequest(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
description: str
|
description: str
|
||||||
|
|
||||||
|
# New models for sale categories
|
||||||
|
class SaleCategoryRequest(BaseModel):
|
||||||
|
id: int | None = None
|
||||||
|
category: str
|
||||||
|
description: str | None = None
|
||||||
|
perc: float
|
||||||
|
|
||||||
|
class SaleCategoryResponse(BaseModel):
|
||||||
|
id: int
|
||||||
|
category: str
|
||||||
|
description: str | None = None
|
||||||
|
perc: float
|
||||||
|
create_dttm: datetime
|
||||||
|
update_dttm: datetime
|
||||||
|
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|||||||
@@ -4,15 +4,17 @@ import json
|
|||||||
|
|
||||||
# Конфигурация API
|
# Конфигурация API
|
||||||
BASE_URL = "http://127.0.0.1:8001"
|
BASE_URL = "http://127.0.0.1:8001"
|
||||||
API_KEY = "672a1437-70e8-461f-9bff-20f5ce4a023d"
|
API_KEY = "de058226-37d3-4d0e-a483-3c2a7fac3573"
|
||||||
REF = "9bd1a6bd-98e1-48f4-a120-3b3d016011c0"
|
REF = "0d9aaa96-80e6-424c-84c9-ff70a6eb915e"
|
||||||
|
|
||||||
# Данные для запроса на создание продажи
|
# Данные для запроса на создание продажи
|
||||||
# Замените эти значения на актуальные для вашей продажи
|
# Замените эти значения на актуальные для вашей продажи
|
||||||
sale_data = {
|
sale_data = {
|
||||||
"cost": 100.50, # Стоимость продажи
|
"cost": 100, # Стоимость продажи
|
||||||
"ref": REF, # Ваш реферальный код
|
"ref": REF, # Ваш реферальный код
|
||||||
"sale_id": str(uuid.uuid4()) # Уникальный идентификатор продажи для вашей компании
|
"sale_id": str(uuid.uuid4()), # Уникальный идентификатор продажи для вашей компании
|
||||||
|
"category": "vip", # название категории (например, 'basic', 'premium', 'vip')
|
||||||
|
"group_sale_id": str(uuid.uuid4()) # уникальный идентификатор группы продаж
|
||||||
}
|
}
|
||||||
|
|
||||||
# Эндпоинты
|
# Эндпоинты
|
||||||
|
|||||||
63
call_validation_promo_api.py
Normal file
63
call_validation_promo_api.py
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
# Конфигурация API
|
||||||
|
BASE_URL = "http://127.0.0.1:8001"
|
||||||
|
API_KEY = "9efb2df0-03d7-4212-93db-0ae6418667e1" # API-ключ
|
||||||
|
PROMOCODE = "brw0OMOz"
|
||||||
|
|
||||||
|
# Данные для запроса на валидацию промокода
|
||||||
|
validation_data = {
|
||||||
|
"promocode": PROMOCODE,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Эндпоинты
|
||||||
|
token_endpoint = f"{BASE_URL}/token"
|
||||||
|
validation_endpoint = f"{BASE_URL}/validationPromo"
|
||||||
|
|
||||||
|
# Шаг 1: Получение JWT токена
|
||||||
|
print(f"Отправка запроса на получение токена на {token_endpoint}")
|
||||||
|
token_headers = {
|
||||||
|
"X-API-Key": API_KEY,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
token_response = requests.post(token_endpoint, headers=token_headers)
|
||||||
|
token_response.raise_for_status()
|
||||||
|
|
||||||
|
token_data = token_response.json()
|
||||||
|
jwt_token = token_data["access_token"]
|
||||||
|
print("JWT токен успешно получен.")
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"Произошла ошибка при получении токена: {e}")
|
||||||
|
if hasattr(e, 'response') and e.response is not None:
|
||||||
|
try:
|
||||||
|
print("Тело ответа с ошибкой:", e.response.json())
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print("Тело ответа с ошибкой (не JSON):", e.response.text)
|
||||||
|
exit() # Прерываем выполнение, если не удалось получить токен
|
||||||
|
|
||||||
|
# Шаг 2: Вызов эндпоинта /validationPromo с использованием полученного JWT токена
|
||||||
|
headers_with_jwt = {
|
||||||
|
"Authorization": f"Bearer {jwt_token}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
print(f"Отправка запроса на {validation_endpoint} с данными: {validation_data}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
validation_response = requests.post(validation_endpoint, headers=headers_with_jwt, data=json.dumps(validation_data))
|
||||||
|
validation_response.raise_for_status() # Вызовет исключение для ошибок HTTP (4xx или 5xx)
|
||||||
|
|
||||||
|
print("Статус ответа:", validation_response.status_code)
|
||||||
|
print("Тело ответа:", validation_response.json())
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"Произошла ошибка при вызове API validationPromo: {e}")
|
||||||
|
if hasattr(e, 'response') and e.response is not None:
|
||||||
|
try:
|
||||||
|
print("Тело ответа с ошибкой:", e.response.json())
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print("Тело ответа с ошибкой (не JSON):", e.response.text)
|
||||||
31
fill_db.py
31
fill_db.py
@@ -1,7 +1,7 @@
|
|||||||
import random
|
import random
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from sqlmodel import Session
|
from sqlmodel import Session
|
||||||
from sql_models import TgAgent, Ref, Sale, Account, Company, AgentTransaction, PartnerTransaction, CompanyBalance, AgentBalance, IntegrationToken
|
from sql_models import TgAgent, Ref, Sale, Account, Company, AgentTransaction, PartnerTransaction, CompanyBalance, AgentBalance, IntegrationToken, SaleCategory
|
||||||
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
|
||||||
@@ -81,13 +81,13 @@ def fill_db():
|
|||||||
session.execute(text('DELETE FROM "partner_transactions"'))
|
session.execute(text('DELETE FROM "partner_transactions"'))
|
||||||
session.execute(text('DELETE FROM "company_balances"'))
|
session.execute(text('DELETE FROM "company_balances"'))
|
||||||
session.execute(text('DELETE FROM "agent_balances"'))
|
session.execute(text('DELETE FROM "agent_balances"'))
|
||||||
|
session.execute(text("DELETE FROM salecategory"))
|
||||||
session.execute(text("DELETE FROM company"))
|
session.execute(text("DELETE FROM company"))
|
||||||
session.commit()
|
session.commit()
|
||||||
# 0. Company
|
# 0. Company
|
||||||
company = Company(
|
company = Company(
|
||||||
name="RE: Premium",
|
name="RE: Premium",
|
||||||
commission=10.0,
|
commission=0.0,
|
||||||
agent_commission=15.0,
|
|
||||||
key="re-premium-key",
|
key="re-premium-key",
|
||||||
)
|
)
|
||||||
session.add(company)
|
session.add(company)
|
||||||
@@ -110,6 +110,18 @@ def fill_db():
|
|||||||
session.add(integration_token)
|
session.add(integration_token)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
# 0.2 SaleCategory
|
||||||
|
sale_categories = [
|
||||||
|
SaleCategory(category="basic", description="Базовая продажа", perc=10.0, company_id=company.id),
|
||||||
|
SaleCategory(category="premium", description="Премиум продажа", perc=20.0, company_id=company.id),
|
||||||
|
SaleCategory(category="vip", description="VIP продажа", perc=30.0, company_id=company.id),
|
||||||
|
]
|
||||||
|
for cat in sale_categories:
|
||||||
|
session.add(cat)
|
||||||
|
session.commit()
|
||||||
|
for cat in sale_categories:
|
||||||
|
session.refresh(cat)
|
||||||
|
|
||||||
# 1. Accounts
|
# 1. Accounts
|
||||||
accounts = []
|
accounts = []
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
@@ -182,25 +194,30 @@ def fill_db():
|
|||||||
for ref in refs:
|
for ref in refs:
|
||||||
session.refresh(ref)
|
session.refresh(ref)
|
||||||
# 4. Sales (минимум 20 на каждый ref)
|
# 4. Sales (минимум 20 на каждый ref)
|
||||||
|
all_categories = session.query(SaleCategory).filter_by(company_id=company.id).all()
|
||||||
for ref in refs:
|
for ref in refs:
|
||||||
sale_count = random.randint(20, int(20 * 1.25)) # от 20 до 25
|
sale_count = random.randint(20, int(20 * 1.25)) # от 20 до 25
|
||||||
for _ in range(sale_count):
|
group_size = 5
|
||||||
|
group_sale_ids = [str(uuid4()) for _ in range((sale_count // group_size) + 1)]
|
||||||
|
for idx in range(sale_count):
|
||||||
cost = round(random.uniform(100, 1000), 2)
|
cost = round(random.uniform(100, 1000), 2)
|
||||||
crediting = round(cost * (company.agent_commission / 100.0), 2)
|
sale_category = random.choice(all_categories)
|
||||||
|
crediting = round(cost * (sale_category.perc / 100.0), 2)
|
||||||
# Генерируем случайную дату и время в пределах последних 7 дней
|
# Генерируем случайную дату и время в пределах последних 7 дней
|
||||||
end_dttm = datetime.utcnow()
|
end_dttm = datetime.utcnow()
|
||||||
start_dttm = end_dttm - timedelta(days=7)
|
start_dttm = end_dttm - timedelta(days=7)
|
||||||
time_diff = end_dttm - start_dttm
|
time_diff = end_dttm - start_dttm
|
||||||
random_seconds = random.uniform(0, time_diff.total_seconds())
|
random_seconds = random.uniform(0, time_diff.total_seconds())
|
||||||
sale_dttm = start_dttm + timedelta(seconds=random_seconds)
|
sale_dttm = start_dttm + timedelta(seconds=random_seconds)
|
||||||
|
group_sale_id = group_sale_ids[idx // group_size]
|
||||||
sale = Sale(
|
sale = Sale(
|
||||||
cost=cost,
|
cost=cost,
|
||||||
crediting=crediting,
|
crediting=crediting,
|
||||||
ref=ref.id,
|
ref=ref.id,
|
||||||
sale_id=str(uuid4()),
|
sale_id=str(uuid4()),
|
||||||
|
group_sale_id=group_sale_id,
|
||||||
company_id=company.id,
|
company_id=company.id,
|
||||||
|
category=sale_category.id,
|
||||||
sale_dttm=sale_dttm,
|
sale_dttm=sale_dttm,
|
||||||
create_dttm=sale_dttm, # create_dttm также будет случайным в этом диапазоне
|
create_dttm=sale_dttm, # create_dttm также будет случайным в этом диапазоне
|
||||||
update_dttm=sale_dttm # update_dttm также будет случайным в этом диапазоне
|
update_dttm=sale_dttm # update_dttm также будет случайным в этом диапазоне
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import uuid
|
|||||||
from random import choices
|
from random import choices
|
||||||
import string
|
import string
|
||||||
|
|
||||||
from sql_models import Company, IntegrationToken, Ref, Sale, AgentTransaction, PartnerTransaction, AgentBalance, TgAgent, CompanyBalance
|
from sql_models import Company, IntegrationToken, Ref, Sale, AgentTransaction, PartnerTransaction, AgentBalance, TgAgent, CompanyBalance, SaleCategory
|
||||||
from integration_models import Token, SaleCreateRequest, SaleCreateResponse, TransactionStatus, WithdrawRequest, WithdrawResponse
|
from integration_models import Token, SaleCreateRequest, SaleCreateResponse, TransactionStatus, WithdrawRequest, WithdrawResponse, PromoValidationRequest, PromoValidationResponse
|
||||||
from bff_models import RegisterResponse, TgAuthResponse
|
from bff_models import RegisterResponse, TgAuthResponse
|
||||||
from tg_models import RefAddRequest, RefResponse, RefAddResponse, RefStatResponse, RegisterRequest, StatResponse
|
from tg_models import RefAddRequest, RefResponse, RefAddResponse, RefStatResponse, RegisterRequest, StatResponse
|
||||||
from helpers_bff import AUTH_DB_ENGINE, get_integration_db, create_integration_jwt_token, get_current_company_from_jwt, get_tg_agent_by_tg_id, get_current_tg_agent
|
from helpers_bff import AUTH_DB_ENGINE, get_integration_db, create_integration_jwt_token, get_current_company_from_jwt, get_tg_agent_by_tg_id, get_current_tg_agent
|
||||||
@@ -189,7 +189,7 @@ async def withdraw_funds(
|
|||||||
@app.post("/sale", tags=["integration"], response_model=SaleCreateResponse)
|
@app.post("/sale", tags=["integration"], response_model=SaleCreateResponse)
|
||||||
async def create_sale(
|
async def create_sale(
|
||||||
req: SaleCreateRequest,
|
req: SaleCreateRequest,
|
||||||
company: Company = Depends(get_current_company_from_jwt), # Используем новую зависимость
|
company: Company = Depends(get_current_company_from_jwt),
|
||||||
db: Session = Depends(get_integration_db)
|
db: Session = Depends(get_integration_db)
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@@ -198,24 +198,35 @@ async def create_sale(
|
|||||||
# Устанавливаем уровень изоляции для текущей транзакции
|
# Устанавливаем уровень изоляции для текущей транзакции
|
||||||
db.connection(execution_options={'isolation_level': 'SERIALIZABLE'})
|
db.connection(execution_options={'isolation_level': 'SERIALIZABLE'})
|
||||||
|
|
||||||
# 1. Найти Ref по `ref` и `company.id`
|
# Проверка входных данных
|
||||||
# Сначала находим TgAgent, связанный с компанией, затем Ref
|
if not req.ref and not req.promocode:
|
||||||
|
raise HTTPException(status_code=400, detail="Необходимо передать либо ref, либо promocode")
|
||||||
|
if not req.category:
|
||||||
|
raise HTTPException(status_code=400, detail="Необходимо передать category (id категории)")
|
||||||
|
|
||||||
|
# 1. Найти Ref по ref и/или promocode
|
||||||
|
referral = None
|
||||||
tg_agent = db.exec(
|
if req.ref and req.promocode:
|
||||||
select(TgAgent)
|
referral_by_ref = db.exec(select(Ref).where(Ref.ref == req.ref)).first()
|
||||||
.join(Ref)
|
referral_by_code = db.exec(select(Ref).where(Ref.promocode == req.promocode)).first()
|
||||||
.where(TgAgent.company_id == company.id)
|
if not referral_by_ref or not referral_by_code:
|
||||||
.where(Ref.ref == req.ref)
|
raise HTTPException(status_code=404, detail="Реферальная ссылка или промокод не найдены")
|
||||||
).first()
|
if referral_by_ref.id != referral_by_code.id:
|
||||||
|
raise HTTPException(status_code=400, detail="ref и promocode не соответствуют одной ссылке")
|
||||||
if not tg_agent:
|
referral = referral_by_ref
|
||||||
raise HTTPException(status_code=404, detail="Реферальная ссылка не найдена или не принадлежит данной компании")
|
elif req.ref:
|
||||||
|
referral = db.exec(select(Ref).where(Ref.ref == req.ref)).first()
|
||||||
referral = db.exec(select(Ref).where(Ref.ref == req.ref).where(Ref.tg_agent_id == tg_agent.id)).first()
|
|
||||||
if not referral:
|
if not referral:
|
||||||
raise HTTPException(status_code=404, detail="Реферальная ссылка не найдена")
|
raise HTTPException(status_code=404, detail="Реферальная ссылка не найдена")
|
||||||
|
elif req.promocode:
|
||||||
|
referral = db.exec(select(Ref).where(Ref.promocode == req.promocode)).first()
|
||||||
|
if not referral:
|
||||||
|
raise HTTPException(status_code=404, detail="Промокод не найден")
|
||||||
|
|
||||||
|
# Проверяем, что реф действительно принадлежит компании
|
||||||
|
tg_agent = db.exec(select(TgAgent).where(TgAgent.id == referral.tg_agent_id, TgAgent.company_id == company.id)).first()
|
||||||
|
if not tg_agent:
|
||||||
|
raise HTTPException(status_code=404, detail="Реферальная ссылка не принадлежит данной компании")
|
||||||
|
|
||||||
# 2. Проверить, что sale_id уникален для данной компании
|
# 2. Проверить, что sale_id уникален для данной компании
|
||||||
existing_sale = db.exec(
|
existing_sale = db.exec(
|
||||||
@@ -223,20 +234,19 @@ async def create_sale(
|
|||||||
.where(Sale.company_id == company.id)
|
.where(Sale.company_id == company.id)
|
||||||
.where(Sale.sale_id == req.sale_id)
|
.where(Sale.sale_id == req.sale_id)
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
if existing_sale:
|
if existing_sale:
|
||||||
raise HTTPException(status_code=400, detail="Продажа с таким sale_id уже существует для данной компании")
|
raise HTTPException(status_code=400, detail="Продажа с таким sale_id уже существует для данной компании")
|
||||||
|
|
||||||
# 3. Рассчитать crediting
|
# 3. Найти категорию и рассчитать crediting
|
||||||
crediting_amount = req.cost * (company.agent_commission / 100.0)
|
sale_category = db.exec(select(SaleCategory).where(SaleCategory.category == req.category, SaleCategory.company_id == company.id)).first()
|
||||||
|
if not sale_category:
|
||||||
|
raise HTTPException(status_code=404, detail="Категория продажи не найдена")
|
||||||
|
crediting_amount = req.cost * (sale_category.perc / 100.0)
|
||||||
|
|
||||||
# 4. Проверить и обновить AgentBalance и CompanyBalance
|
# 4. Проверить и обновить AgentBalance и CompanyBalance
|
||||||
# AgentBalance
|
|
||||||
agent_balance = db.exec(select(AgentBalance).where(AgentBalance.tg_agent_id == tg_agent.id)).first()
|
agent_balance = db.exec(select(AgentBalance).where(AgentBalance.tg_agent_id == tg_agent.id)).first()
|
||||||
if not agent_balance:
|
if not agent_balance:
|
||||||
raise HTTPException(status_code=404, detail="Баланс агента не найден")
|
raise HTTPException(status_code=404, detail="Баланс агента не найден")
|
||||||
|
|
||||||
# CompanyBalance
|
|
||||||
company_balance = db.exec(select(CompanyBalance).where(CompanyBalance.company_id == company.id)).first()
|
company_balance = db.exec(select(CompanyBalance).where(CompanyBalance.company_id == company.id)).first()
|
||||||
if not company_balance:
|
if not company_balance:
|
||||||
raise HTTPException(status_code=404, detail="Баланс компании не найден")
|
raise HTTPException(status_code=404, detail="Баланс компании не найден")
|
||||||
@@ -247,13 +257,14 @@ async def create_sale(
|
|||||||
crediting=crediting_amount,
|
crediting=crediting_amount,
|
||||||
ref=referral.id,
|
ref=referral.id,
|
||||||
sale_id=req.sale_id,
|
sale_id=req.sale_id,
|
||||||
|
group_sale_id=req.group_sale_id,
|
||||||
company_id=company.id,
|
company_id=company.id,
|
||||||
|
category=sale_category.id,
|
||||||
sale_dttm=datetime.utcnow()
|
sale_dttm=datetime.utcnow()
|
||||||
)
|
)
|
||||||
db.add(new_sale)
|
db.add(new_sale)
|
||||||
|
|
||||||
# Создать AgentTransaction
|
# Создать AgentTransaction
|
||||||
agent_transaction_status = TransactionStatus.DONE # auto_approve_transactions отвечает только за апрув агентских транзакций на вывод
|
agent_transaction_status = TransactionStatus.DONE
|
||||||
agent_transaction = AgentTransaction(
|
agent_transaction = AgentTransaction(
|
||||||
tg_agent_id=tg_agent.id,
|
tg_agent_id=tg_agent.id,
|
||||||
amount=crediting_amount,
|
amount=crediting_amount,
|
||||||
@@ -261,7 +272,6 @@ async def create_sale(
|
|||||||
transaction_group=uuid.uuid4()
|
transaction_group=uuid.uuid4()
|
||||||
)
|
)
|
||||||
db.add(agent_transaction)
|
db.add(agent_transaction)
|
||||||
|
|
||||||
# Обновление балансов для продаж - всегда в замороженный/ожидающий баланс
|
# Обновление балансов для продаж - всегда в замороженный/ожидающий баланс
|
||||||
agent_balance.frozen_balance += crediting_amount
|
agent_balance.frozen_balance += crediting_amount
|
||||||
company_balance.pending_balance -= crediting_amount
|
company_balance.pending_balance -= crediting_amount
|
||||||
@@ -279,6 +289,25 @@ async def create_sale(
|
|||||||
"crediting": new_sale.crediting
|
"crediting": new_sale.crediting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@app.post("/validationPromo", tags=["integration"], response_model=PromoValidationResponse)
|
||||||
|
async def validate_promocode(
|
||||||
|
req: PromoValidationRequest,
|
||||||
|
company: Company = Depends(get_current_company_from_jwt),
|
||||||
|
db: Session = Depends(get_integration_db)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Проверяет валидность промокода для текущей компании.
|
||||||
|
"""
|
||||||
|
referral = db.exec(select(Ref).where(Ref.promocode == req.promocode)).first()
|
||||||
|
if not referral:
|
||||||
|
return {"validation": False}
|
||||||
|
|
||||||
|
tg_agent = db.exec(select(TgAgent).where(TgAgent.id == referral.tg_agent_id, TgAgent.company_id == company.id)).first()
|
||||||
|
if not tg_agent:
|
||||||
|
return {"validation": False}
|
||||||
|
|
||||||
|
return {"validation": True}
|
||||||
|
|
||||||
@app.post("/register", tags=["agent-tg"], response_model=RegisterResponse)
|
@app.post("/register", tags=["agent-tg"], response_model=RegisterResponse)
|
||||||
def register(req: RegisterRequest, db: Session = Depends(get_integration_db)):
|
def register(req: RegisterRequest, db: Session = Depends(get_integration_db)):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -16,9 +16,12 @@ class IntegrationTokenResponse(BaseModel):
|
|||||||
|
|
||||||
# Models for /sale endpoint
|
# Models for /sale endpoint
|
||||||
class SaleCreateRequest(BaseModel):
|
class SaleCreateRequest(BaseModel):
|
||||||
ref: str
|
ref: Optional[str] = None
|
||||||
|
promocode: Optional[str] = None
|
||||||
sale_id: str
|
sale_id: str
|
||||||
cost: float
|
cost: float
|
||||||
|
category: str # название категории продажи
|
||||||
|
group_sale_id: str # новое поле для группировки продаж
|
||||||
|
|
||||||
class SaleCreateResponse(BaseModel):
|
class SaleCreateResponse(BaseModel):
|
||||||
msg: str
|
msg: str
|
||||||
@@ -40,3 +43,9 @@ class TransactionStatus(str, Enum):
|
|||||||
DONE = "done"
|
DONE = "done"
|
||||||
CANCELED = "canceled"
|
CANCELED = "canceled"
|
||||||
ERROR = "error"
|
ERROR = "error"
|
||||||
|
|
||||||
|
class PromoValidationRequest(BaseModel):
|
||||||
|
promocode: str
|
||||||
|
|
||||||
|
class PromoValidationResponse(BaseModel):
|
||||||
|
validation: bool
|
||||||
112
main.py
112
main.py
@@ -38,7 +38,9 @@ from bff_models import (
|
|||||||
TransactionStatus,
|
TransactionStatus,
|
||||||
IntegrationTokenResponse,
|
IntegrationTokenResponse,
|
||||||
IntegrationTokenCreateRequest,
|
IntegrationTokenCreateRequest,
|
||||||
IntegrationTokenUpdateRequest
|
IntegrationTokenUpdateRequest,
|
||||||
|
SaleCategoryRequest,
|
||||||
|
SaleCategoryResponse
|
||||||
)
|
)
|
||||||
from sql_models import (
|
from sql_models import (
|
||||||
Company,
|
Company,
|
||||||
@@ -49,7 +51,8 @@ from sql_models import (
|
|||||||
PartnerTransaction,
|
PartnerTransaction,
|
||||||
AgentBalance,
|
AgentBalance,
|
||||||
Account,
|
Account,
|
||||||
IntegrationToken
|
IntegrationToken,
|
||||||
|
SaleCategory
|
||||||
)
|
)
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
import hashlib
|
import hashlib
|
||||||
@@ -64,6 +67,7 @@ from helpers_bff import (
|
|||||||
pwd_context,
|
pwd_context,
|
||||||
)
|
)
|
||||||
import os
|
import os
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
# Создание движка базы данных
|
# Создание движка базы данных
|
||||||
@@ -75,7 +79,7 @@ app = FastAPI()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/token", response_model=Token, tags=["bff", "token"])
|
@app.post("/token", response_model=Token, tags=[ "token"])
|
||||||
def login_account_for_access_token(
|
def login_account_for_access_token(
|
||||||
form_data: OAuth2PasswordRequestForm = Depends(),
|
form_data: OAuth2PasswordRequestForm = Depends(),
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
@@ -99,7 +103,7 @@ def login_account_for_access_token(
|
|||||||
return Token(access_token=access_token, token_type="bearer")
|
return Token(access_token=access_token, token_type="bearer")
|
||||||
|
|
||||||
|
|
||||||
@app.get("/dashboard/cards", tags=["bff", "dashboard"], response_model=DashboardCardsResponse)
|
@app.get("/dashboard/cards", tags=[ "dashboard"], response_model=DashboardCardsResponse)
|
||||||
def get_dashboard_cards(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
def get_dashboard_cards(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
||||||
"""
|
"""
|
||||||
Возвращает данные для карточек на главной панели (dashboard) пользователя, включая общий доход, выплаты, активных рефералов, ожидающие выплаты и общее количество продаж.
|
Возвращает данные для карточек на главной панели (dashboard) пользователя, включая общий доход, выплаты, активных рефералов, ожидающие выплаты и общее количество продаж.
|
||||||
@@ -130,7 +134,7 @@ def get_dashboard_cards(current_account: Account = Depends(get_current_account),
|
|||||||
"totalSales": totalSales
|
"totalSales": totalSales
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/dashboard/chart/total", tags=["bff", "dashboard"], response_model=DashboardChartTotalResponse)
|
@app.get("/dashboard/chart/total", tags=[ "dashboard"], response_model=DashboardChartTotalResponse)
|
||||||
def get_dashboard_chart_total(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
def get_dashboard_chart_total(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
||||||
"""
|
"""
|
||||||
Возвращает данные для графика "Общий доход и продажи по датам" на главной панели (dashboard).
|
Возвращает данные для графика "Общий доход и продажи по датам" на главной панели (dashboard).
|
||||||
@@ -151,7 +155,7 @@ def get_dashboard_chart_total(current_account: Account = Depends(get_current_acc
|
|||||||
]
|
]
|
||||||
return DashboardChartTotalResponse(items=data)
|
return DashboardChartTotalResponse(items=data)
|
||||||
|
|
||||||
@app.get("/dashboard/chart/agent", tags=["bff", "dashboard"], response_model=DashboardChartAgentResponse)
|
@app.get("/dashboard/chart/agent", tags=[ "dashboard"], response_model=DashboardChartAgentResponse)
|
||||||
def get_dashboard_chart_agent(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
def get_dashboard_chart_agent(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
||||||
"""
|
"""
|
||||||
Возвращает данные для графика "Продажи по агентам" на главной панели (dashboard).
|
Возвращает данные для графика "Продажи по агентам" на главной панели (dashboard).
|
||||||
@@ -181,7 +185,7 @@ def get_dashboard_chart_agent(current_account: Account = Depends(get_current_acc
|
|||||||
})
|
})
|
||||||
return DashboardChartAgentResponse(items=result)
|
return DashboardChartAgentResponse(items=result)
|
||||||
|
|
||||||
@app.get("/stat/agents", tags=["bff", "stat"], response_model=StatAgentsResponse)
|
@app.get("/stat/agents", tags=[ "stat"], response_model=StatAgentsResponse)
|
||||||
def get_agents_stat(
|
def get_agents_stat(
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
date_start: str = Query(None),
|
date_start: str = Query(None),
|
||||||
@@ -225,7 +229,7 @@ def get_agents_stat(
|
|||||||
})
|
})
|
||||||
return StatAgentsResponse(items=result)
|
return StatAgentsResponse(items=result)
|
||||||
|
|
||||||
@app.get("/stat/referrals", tags=["bff", "stat"], response_model=StatReferralsResponse)
|
@app.get("/stat/referrals", tags=[ "stat"], response_model=StatReferralsResponse)
|
||||||
def get_referrals_stat(
|
def get_referrals_stat(
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
date_start: str = Query(None),
|
date_start: str = Query(None),
|
||||||
@@ -258,7 +262,7 @@ def get_referrals_stat(
|
|||||||
})
|
})
|
||||||
return StatReferralsResponse(items=result)
|
return StatReferralsResponse(items=result)
|
||||||
|
|
||||||
@app.get("/stat/sales", tags=["bff", "stat"], response_model=StatSalesResponse)
|
@app.get("/stat/sales", tags=[ "stat"], response_model=StatSalesResponse)
|
||||||
def get_sales_stat(
|
def get_sales_stat(
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
date_start: str = Query(None),
|
date_start: str = Query(None),
|
||||||
@@ -294,7 +298,7 @@ def get_sales_stat(
|
|||||||
})
|
})
|
||||||
return StatSalesResponse(items=result)
|
return StatSalesResponse(items=result)
|
||||||
|
|
||||||
@app.get("/billing/cards", tags=["bff", "billing"], response_model=BillingCardsResponse)
|
@app.get("/billing/cards", tags=[ "billing"], response_model=BillingCardsResponse)
|
||||||
def get_billing_cards(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
def get_billing_cards(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
||||||
"""
|
"""
|
||||||
Возвращает ключевые показатели биллинга для компании, включая общий заработок, общие выплаты и доступные к выводу средства.
|
Возвращает ключевые показатели биллинга для компании, включая общий заработок, общие выплаты и доступные к выводу средства.
|
||||||
@@ -317,7 +321,7 @@ def get_billing_cards(current_account: Account = Depends(get_current_account), d
|
|||||||
"pendingPayouts": pendingPayouts
|
"pendingPayouts": pendingPayouts
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/billing/payouts/transactions", tags=["bff", "billing"], response_model=BillingPayoutsTransactionsResponse)
|
@app.get("/billing/payouts/transactions", tags=[ "billing"], response_model=BillingPayoutsTransactionsResponse)
|
||||||
def get_billing_payouts_transactions(
|
def get_billing_payouts_transactions(
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
date_start: str = Query(None),
|
date_start: str = Query(None),
|
||||||
@@ -357,7 +361,7 @@ def get_billing_payouts_transactions(
|
|||||||
})
|
})
|
||||||
return BillingPayoutsTransactionsResponse(items=result)
|
return BillingPayoutsTransactionsResponse(items=result)
|
||||||
|
|
||||||
@app.get("/billing/chart/stat", tags=["bff", "billing"], response_model=BillingChartStatResponse)
|
@app.get("/billing/chart/stat", tags=[ "billing"], response_model=BillingChartStatResponse)
|
||||||
def get_billing_chart_stat(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
def get_billing_chart_stat(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
||||||
"""
|
"""
|
||||||
Возвращает статистику выплат по датам и статусам для компании текущего пользователя.
|
Возвращает статистику выплат по датам и статусам для компании текущего пользователя.
|
||||||
@@ -382,7 +386,7 @@ def get_billing_chart_stat(current_account: Account = Depends(get_current_accoun
|
|||||||
]
|
]
|
||||||
return BillingChartStatResponse(items=data)
|
return BillingChartStatResponse(items=data)
|
||||||
|
|
||||||
@app.get("/billing/chart/pie", tags=["bff", "billing"], response_model=BillingChartPieResponse)
|
@app.get("/billing/chart/pie", tags=[ "billing"], response_model=BillingChartPieResponse)
|
||||||
def get_billing_chart_pie(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
def get_billing_chart_pie(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
||||||
"""
|
"""
|
||||||
Возвращает круговую диаграмму статистики выплат по статусам для компании текущего пользователя.
|
Возвращает круговую диаграмму статистики выплат по статусам для компании текущего пользователя.
|
||||||
@@ -402,7 +406,7 @@ def get_billing_chart_pie(current_account: Account = Depends(get_current_account
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/account", tags=["bff", "account"], response_model=AccountResponse)
|
@app.get("/account", tags=[ "account"], response_model=AccountResponse)
|
||||||
def get_account(current_account: Account = Depends(get_current_account)):
|
def get_account(current_account: Account = Depends(get_current_account)):
|
||||||
"""
|
"""
|
||||||
Возвращает базовую информацию об аккаунте текущего пользователя (имя и фамилию).
|
Возвращает базовую информацию об аккаунте текущего пользователя (имя и фамилию).
|
||||||
@@ -412,7 +416,7 @@ def get_account(current_account: Account = Depends(get_current_account)):
|
|||||||
"surname": current_account.surname
|
"surname": current_account.surname
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/account/profile", tags=["bff", "account"], response_model=AccountProfileResponse)
|
@app.get("/account/profile", tags=[ "account"], response_model=AccountProfileResponse)
|
||||||
def get_account_profile(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
def get_account_profile(current_account: Account = Depends(get_current_account), db: Session = Depends(get_db)):
|
||||||
"""
|
"""
|
||||||
Возвращает полную информацию о профиле аккаунта текущего пользователя, включая данные компании.
|
Возвращает полную информацию о профиле аккаунта текущего пользователя, включая данные компании.
|
||||||
@@ -430,11 +434,10 @@ def get_account_profile(current_account: Account = Depends(get_current_account),
|
|||||||
"name": company.name,
|
"name": company.name,
|
||||||
"key": company.key,
|
"key": company.key,
|
||||||
"commission": company.commission,
|
"commission": company.commission,
|
||||||
"agent_commission": company.agent_commission
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.post("/account/profile", tags=["bff", "account"], response_model=AccountProfileUpdateResponse)
|
@app.post("/account/profile", tags=[ "account"], response_model=AccountProfileUpdateResponse)
|
||||||
def update_account_profile(
|
def update_account_profile(
|
||||||
req: AccountProfileUpdateRequest,
|
req: AccountProfileUpdateRequest,
|
||||||
current_account: Account = Depends(get_current_account),
|
current_account: Account = Depends(get_current_account),
|
||||||
@@ -456,7 +459,7 @@ def update_account_profile(
|
|||||||
db.refresh(current_account)
|
db.refresh(current_account)
|
||||||
return {"msg": "Профиль обновлён успешно"}
|
return {"msg": "Профиль обновлён успешно"}
|
||||||
|
|
||||||
@app.post("/account/password", tags=["bff", "account"], response_model=AccountPasswordChangeResponse)
|
@app.post("/account/password", tags=[ "account"], response_model=AccountPasswordChangeResponse)
|
||||||
def change_account_password(
|
def change_account_password(
|
||||||
req: AccountPasswordChangeRequest,
|
req: AccountPasswordChangeRequest,
|
||||||
current_account: Account = Depends(get_current_account),
|
current_account: Account = Depends(get_current_account),
|
||||||
@@ -482,7 +485,7 @@ 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=[ "account"])
|
||||||
def get_account_agent_transactions(
|
def get_account_agent_transactions(
|
||||||
statuses: Optional[List[TransactionStatus]] = Query(None),
|
statuses: Optional[List[TransactionStatus]] = Query(None),
|
||||||
date_start: str = Query(None),
|
date_start: str = Query(None),
|
||||||
@@ -539,7 +542,7 @@ def get_account_agent_transactions(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/account/auto-approve", tags=["bff", "account"], response_model=AutoApproveSettingsGetResponse)
|
@app.get("/account/auto-approve", tags=[ "account"], response_model=AutoApproveSettingsGetResponse)
|
||||||
def get_auto_approve_settings(
|
def get_auto_approve_settings(
|
||||||
current_account: Account = Depends(get_current_account),
|
current_account: Account = Depends(get_current_account),
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
@@ -552,7 +555,7 @@ def get_auto_approve_settings(
|
|||||||
raise HTTPException(status_code=404, detail="Компания не найдена")
|
raise HTTPException(status_code=404, detail="Компания не найдена")
|
||||||
return {"auto_approve_transactions": company.auto_approve_transactions}
|
return {"auto_approve_transactions": company.auto_approve_transactions}
|
||||||
|
|
||||||
@app.post("/account/auto-approve", tags=["bff", "account"], response_model=AutoApproveSettingsUpdateResponse)
|
@app.post("/account/auto-approve", tags=[ "account"], response_model=AutoApproveSettingsUpdateResponse)
|
||||||
def update_auto_approve_settings(
|
def update_auto_approve_settings(
|
||||||
req: AutoApproveSettingsRequest,
|
req: AutoApproveSettingsRequest,
|
||||||
current_account: Account = Depends(get_current_account),
|
current_account: Account = Depends(get_current_account),
|
||||||
@@ -602,7 +605,7 @@ def update_auto_approve_settings(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/account/approve-transactions", tags=["bff", "account"], response_model=ApproveTransactionsResult)
|
@app.post("/account/approve-transactions", tags=[ "account"], response_model=ApproveTransactionsResult)
|
||||||
def approve_agent_transactions(
|
def approve_agent_transactions(
|
||||||
req: ApproveTransactionsRequest,
|
req: ApproveTransactionsRequest,
|
||||||
current_account: Account = Depends(get_current_account),
|
current_account: Account = Depends(get_current_account),
|
||||||
@@ -639,7 +642,7 @@ def approve_agent_transactions(
|
|||||||
|
|
||||||
# --- Новый функционал для интеграционных токенов ---
|
# --- Новый функционал для интеграционных токенов ---
|
||||||
|
|
||||||
@app.get("/account/integration-tokens", tags=["bff", "account"], response_model=List[IntegrationTokenResponse])
|
@app.get("/account/integration-tokens", tags=[ "account"], response_model=List[IntegrationTokenResponse])
|
||||||
def get_integration_tokens(
|
def get_integration_tokens(
|
||||||
current_account: Account = Depends(get_current_account),
|
current_account: Account = Depends(get_current_account),
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
@@ -651,7 +654,7 @@ def get_integration_tokens(
|
|||||||
return tokens # Позволяем FastAPI самостоятельно сериализовать объекты IntegrationToken в IntegrationTokenResponse
|
return tokens # Позволяем FastAPI самостоятельно сериализовать объекты IntegrationToken в IntegrationTokenResponse
|
||||||
|
|
||||||
|
|
||||||
@app.post("/account/integration-tokens", tags=["bff", "account"], response_model=IntegrationTokenResponse)
|
@app.post("/account/integration-tokens", tags=[ "account"], response_model=IntegrationTokenResponse)
|
||||||
def create_integration_token(
|
def create_integration_token(
|
||||||
req: IntegrationTokenCreateRequest,
|
req: IntegrationTokenCreateRequest,
|
||||||
current_account: Account = Depends(get_current_account),
|
current_account: Account = Depends(get_current_account),
|
||||||
@@ -684,7 +687,7 @@ def create_integration_token(
|
|||||||
|
|
||||||
return response_token
|
return response_token
|
||||||
|
|
||||||
@app.post("/account/integration-tokens/update-description", tags=["bff", "account"], response_model=Dict[str, str])
|
@app.post("/account/integration-tokens/update-description", tags=[ "account"], response_model=Dict[str, str])
|
||||||
def update_integration_token_description(
|
def update_integration_token_description(
|
||||||
req: IntegrationTokenUpdateRequest,
|
req: IntegrationTokenUpdateRequest,
|
||||||
current_account: Account = Depends(get_current_account),
|
current_account: Account = Depends(get_current_account),
|
||||||
@@ -707,7 +710,7 @@ def update_integration_token_description(
|
|||||||
db.refresh(token)
|
db.refresh(token)
|
||||||
return {"msg": "Описание токена обновлено успешно"}
|
return {"msg": "Описание токена обновлено успешно"}
|
||||||
|
|
||||||
@app.delete("/account/integration-tokens/{token_id}", tags=["bff", "account"], response_model=Dict[str, str])
|
@app.delete("/account/integration-tokens/{token_id}", tags=[ "account"], response_model=Dict[str, str])
|
||||||
def delete_integration_token(
|
def delete_integration_token(
|
||||||
token_id: int,
|
token_id: int,
|
||||||
current_account: Account = Depends(get_current_account),
|
current_account: Account = Depends(get_current_account),
|
||||||
@@ -726,3 +729,60 @@ def delete_integration_token(
|
|||||||
db.delete(token)
|
db.delete(token)
|
||||||
db.commit()
|
db.commit()
|
||||||
return {"msg": "Токен удален успешно"}
|
return {"msg": "Токен удален успешно"}
|
||||||
|
|
||||||
|
# --- Категории продаж ---
|
||||||
|
|
||||||
|
@app.get("/category", tags=["category"], response_model=List[SaleCategoryResponse])
|
||||||
|
def get_sale_categories(
|
||||||
|
current_account: Account = Depends(get_current_account),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Возвращает список всех категорий продаж компании пользователя.
|
||||||
|
"""
|
||||||
|
categories = db.exec(select(SaleCategory).where(SaleCategory.company_id == current_account.company_id)).all()
|
||||||
|
return categories
|
||||||
|
|
||||||
|
@app.post("/category", tags=["category"], response_model=SaleCategoryResponse)
|
||||||
|
def create_or_update_sale_category(
|
||||||
|
req: SaleCategoryRequest,
|
||||||
|
current_account: Account = Depends(get_current_account),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Создает новую или обновляет существующую категорию продаж для компании пользователя.
|
||||||
|
"""
|
||||||
|
# Проверка уникальности category для компании
|
||||||
|
existing = db.exec(
|
||||||
|
select(SaleCategory)
|
||||||
|
.where(SaleCategory.category == req.category)
|
||||||
|
.where(SaleCategory.company_id == current_account.company_id)
|
||||||
|
).first()
|
||||||
|
if req.id is not None:
|
||||||
|
category = db.exec(select(SaleCategory).where(SaleCategory.id == req.id, SaleCategory.company_id == current_account.company_id)).first()
|
||||||
|
if not category:
|
||||||
|
raise HTTPException(status_code=404, detail="Категория не найдена")
|
||||||
|
# Если меняем имя, оно не должно совпадать с другой категорией
|
||||||
|
if existing and existing.id != req.id:
|
||||||
|
raise HTTPException(status_code=400, detail="Категория с таким именем уже существует")
|
||||||
|
category.category = req.category
|
||||||
|
category.description = req.description
|
||||||
|
category.perc = req.perc
|
||||||
|
category.update_dttm = datetime.utcnow()
|
||||||
|
db.add(category)
|
||||||
|
else:
|
||||||
|
if existing:
|
||||||
|
raise HTTPException(status_code=400, detail="Категория с таким именем уже существует")
|
||||||
|
now = datetime.utcnow()
|
||||||
|
category = SaleCategory(
|
||||||
|
category=req.category,
|
||||||
|
description=req.description,
|
||||||
|
perc=req.perc,
|
||||||
|
company_id=current_account.company_id,
|
||||||
|
create_dttm=now,
|
||||||
|
update_dttm=now
|
||||||
|
)
|
||||||
|
db.add(category)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(category)
|
||||||
|
return SaleCategoryResponse.model_validate(category)
|
||||||
|
|||||||
@@ -62,6 +62,21 @@ CREATE TABLE integrationtoken (
|
|||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE salecategory (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
category VARCHAR NOT NULL,
|
||||||
|
description VARCHAR,
|
||||||
|
perc FLOAT NOT NULL,
|
||||||
|
company_id INTEGER NOT NULL,
|
||||||
|
create_dttm DATETIME NOT NULL,
|
||||||
|
update_dttm DATETIME NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
FOREIGN KEY(company_id) REFERENCES company (id)
|
||||||
|
)
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE tgagent (
|
CREATE TABLE tgagent (
|
||||||
id INTEGER NOT NULL,
|
id INTEGER NOT NULL,
|
||||||
tg_id INTEGER NOT NULL,
|
tg_id INTEGER NOT NULL,
|
||||||
@@ -149,13 +164,16 @@ CREATE TABLE sale (
|
|||||||
crediting FLOAT NOT NULL,
|
crediting FLOAT NOT NULL,
|
||||||
ref INTEGER NOT NULL,
|
ref INTEGER NOT NULL,
|
||||||
sale_id VARCHAR NOT NULL,
|
sale_id VARCHAR NOT NULL,
|
||||||
|
group_sale_id VARCHAR NOT NULL,
|
||||||
company_id INTEGER NOT NULL,
|
company_id INTEGER NOT NULL,
|
||||||
|
category INTEGER NOT NULL,
|
||||||
sale_dttm DATETIME NOT NULL,
|
sale_dttm DATETIME NOT NULL,
|
||||||
create_dttm DATETIME NOT NULL,
|
create_dttm DATETIME NOT NULL,
|
||||||
update_dttm DATETIME NOT NULL,
|
update_dttm DATETIME NOT NULL,
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
FOREIGN KEY(ref) REFERENCES ref (id),
|
FOREIGN KEY(ref) REFERENCES ref (id),
|
||||||
FOREIGN KEY(company_id) REFERENCES company (id)
|
FOREIGN KEY(company_id) REFERENCES company (id),
|
||||||
|
FOREIGN KEY(category) REFERENCES salecategory (id)
|
||||||
)
|
)
|
||||||
|
|
||||||
;
|
;
|
||||||
@@ -8,7 +8,6 @@ class Company(SQLModel, table=True):
|
|||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
name: str
|
name: str
|
||||||
commission: float # процент комиссии, который взымается за пользование сервисом
|
commission: float # процент комиссии, который взымается за пользование сервисом
|
||||||
agent_commission: float = Field(default=0.0) # процент от продаж, который начисляется агенту
|
|
||||||
key: str = Field(index=True, unique=True)
|
key: str = Field(index=True, unique=True)
|
||||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||||
@@ -20,6 +19,7 @@ class Company(SQLModel, table=True):
|
|||||||
partner_transactions: List["PartnerTransaction"] = Relationship(back_populates="company")
|
partner_transactions: List["PartnerTransaction"] = Relationship(back_populates="company")
|
||||||
company_balance: Optional["CompanyBalance"] = Relationship(back_populates="company")
|
company_balance: Optional["CompanyBalance"] = Relationship(back_populates="company")
|
||||||
accounts: List["Account"] = Relationship(back_populates="company")
|
accounts: List["Account"] = Relationship(back_populates="company")
|
||||||
|
sale_categories: List["SaleCategory"] = Relationship(back_populates="company")
|
||||||
|
|
||||||
class TgAgent(SQLModel, table=True):
|
class TgAgent(SQLModel, table=True):
|
||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
@@ -50,19 +50,34 @@ class Ref(SQLModel, table=True):
|
|||||||
tg_agent: "TgAgent" = Relationship(back_populates="refs")
|
tg_agent: "TgAgent" = Relationship(back_populates="refs")
|
||||||
sales: List["Sale"] = Relationship(back_populates="ref_obj")
|
sales: List["Sale"] = Relationship(back_populates="ref_obj")
|
||||||
|
|
||||||
|
class SaleCategory(SQLModel, table=True):
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
category: str
|
||||||
|
description: Optional[str] = None
|
||||||
|
perc: float # процент начисления партнеру
|
||||||
|
company_id: int = Field(foreign_key="company.id")
|
||||||
|
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||||
|
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||||
|
|
||||||
|
company: "Company" = Relationship(back_populates="sale_categories")
|
||||||
|
sales: List["Sale"] = Relationship(back_populates="sale_category")
|
||||||
|
|
||||||
class Sale(SQLModel, table=True):
|
class Sale(SQLModel, table=True):
|
||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
cost: float
|
cost: float
|
||||||
crediting: float # сколько начислено за продажу
|
crediting: float # сколько начислено за продажу
|
||||||
ref: int = Field(foreign_key="ref.id")
|
ref: int = Field(foreign_key="ref.id")
|
||||||
sale_id: str
|
sale_id: str
|
||||||
|
group_sale_id: str # новое поле для группировки продаж
|
||||||
company_id: int = Field(foreign_key="company.id")
|
company_id: int = Field(foreign_key="company.id")
|
||||||
|
category: int = Field(foreign_key="salecategory.id") # новая ссылка на категорию
|
||||||
sale_dttm: datetime = Field(default_factory=datetime.utcnow)
|
sale_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||||
|
|
||||||
ref_obj: "Ref" = Relationship(back_populates="sales")
|
ref_obj: "Ref" = Relationship(back_populates="sales")
|
||||||
company: "Company" = Relationship(back_populates="sales")
|
company: "Company" = Relationship(back_populates="sales")
|
||||||
|
sale_category: "SaleCategory" = Relationship(back_populates="sales")
|
||||||
|
|
||||||
class AgentTransaction(SQLModel, table=True):
|
class AgentTransaction(SQLModel, table=True):
|
||||||
__tablename__ = "agent_transactions"
|
__tablename__ = "agent_transactions"
|
||||||
|
|||||||
Reference in New Issue
Block a user