Добавлены новые модели SaleCategoryRequest и SaleCategoryResponse в bff_models.py для работы с категориями продаж. Обновлены функции в fill_db.py для заполнения базы данных категориями продаж. Изменены эндпоинты в main.py для создания и получения категорий продаж. Обновлены модели и SQL-скрипты для поддержки новых полей и связей. Улучшена логика обработки продаж с учетом категорий.
This commit is contained in:
parent
4f366680bf
commit
bf6a6a8987
@ -189,3 +189,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)
|
||||||
|
|||||||
@ -12,7 +12,8 @@ REF = "9bd1a6bd-98e1-48f4-a120-3b3d016011c0"
|
|||||||
sale_data = {
|
sale_data = {
|
||||||
"cost": 100.50, # Стоимость продажи
|
"cost": 100.50, # Стоимость продажи
|
||||||
"ref": REF, # Ваш реферальный код
|
"ref": REF, # Ваш реферальный код
|
||||||
"sale_id": str(uuid.uuid4()) # Уникальный идентификатор продажи для вашей компании
|
"sale_id": str(uuid.uuid4()), # Уникальный идентификатор продажи для вашей компании
|
||||||
|
"category": 1 # id категории (например, 1 - basic)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Эндпоинты
|
# Эндпоинты
|
||||||
|
|||||||
22
fill_db.py
22
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,6 +81,7 @@ 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
|
||||||
@ -110,6 +111,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 +195,26 @@ 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):
|
for _ 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)
|
||||||
|
|
||||||
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()),
|
||||||
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,7 +7,7 @@ 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
|
||||||
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
|
||||||
@ -201,6 +201,8 @@ async def create_sale(
|
|||||||
# Проверка входных данных
|
# Проверка входных данных
|
||||||
if not req.ref and not req.promocode:
|
if not req.ref and not req.promocode:
|
||||||
raise HTTPException(status_code=400, detail="Необходимо передать либо ref, либо 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
|
# 1. Найти Ref по ref и/или promocode
|
||||||
referral = None
|
referral = None
|
||||||
@ -235,8 +237,11 @@ async def create_sale(
|
|||||||
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
|
||||||
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()
|
||||||
@ -253,6 +258,7 @@ async def create_sale(
|
|||||||
ref=referral.id,
|
ref=referral.id,
|
||||||
sale_id=req.sale_id,
|
sale_id=req.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)
|
||||||
|
|||||||
@ -20,6 +20,7 @@ class SaleCreateRequest(BaseModel):
|
|||||||
promocode: Optional[str] = None
|
promocode: Optional[str] = None
|
||||||
sale_id: str
|
sale_id: str
|
||||||
cost: float
|
cost: float
|
||||||
|
category: int # id категории продажи
|
||||||
|
|
||||||
class SaleCreateResponse(BaseModel):
|
class SaleCreateResponse(BaseModel):
|
||||||
msg: str
|
msg: str
|
||||||
|
|||||||
65
main.py
65
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
|
||||||
|
|
||||||
|
|
||||||
# Создание движка базы данных
|
# Создание движка базы данных
|
||||||
@ -726,3 +730,60 @@ def delete_integration_token(
|
|||||||
db.delete(token)
|
db.delete(token)
|
||||||
db.commit()
|
db.commit()
|
||||||
return {"msg": "Токен удален успешно"}
|
return {"msg": "Токен удален успешно"}
|
||||||
|
|
||||||
|
# --- Категории продаж ---
|
||||||
|
|
||||||
|
@app.get("/account/category", tags=["bff", "account"], 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("/account/category", tags=["bff", "account"], 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,
|
||||||
@ -150,12 +165,14 @@ CREATE TABLE sale (
|
|||||||
ref INTEGER NOT NULL,
|
ref INTEGER NOT NULL,
|
||||||
sale_id VARCHAR NOT NULL,
|
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)
|
||||||
)
|
)
|
||||||
|
|
||||||
;
|
;
|
||||||
@ -20,6 +20,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,6 +51,18 @@ 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
|
||||||
@ -57,12 +70,14 @@ class Sale(SQLModel, table=True):
|
|||||||
ref: int = Field(foreign_key="ref.id")
|
ref: int = Field(foreign_key="ref.id")
|
||||||
sale_id: str
|
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"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user