init
This commit is contained in:
@@ -9,6 +9,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.database import get_db
|
||||
from app.dependencies import get_current_admin
|
||||
from app.models.admin_user import AdminUser
|
||||
from app.models.client import Client
|
||||
from app.models.material import Material
|
||||
from app.models.calculation import Calculation
|
||||
from app.models.order import Order
|
||||
@@ -230,6 +231,7 @@ class DashboardStats(BaseModel):
|
||||
total_revenue: float
|
||||
total_calculations: int
|
||||
materials_count: int
|
||||
clients_count: int
|
||||
orders_today: int
|
||||
|
||||
|
||||
@@ -244,6 +246,7 @@ async def dashboard(admin: AdminUser = Depends(get_current_admin), db: AsyncSess
|
||||
total_revenue = (await db.execute(select(func.sum(Order.total_rub)))).scalar() or 0.0
|
||||
total_calcs = (await db.execute(select(func.count(Calculation.id)))).scalar() or 0
|
||||
materials_count = (await db.execute(select(func.count(Material.id)))).scalar() or 0
|
||||
clients_count = (await db.execute(select(func.count(Client.id)))).scalar() or 0
|
||||
|
||||
today_start = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
orders_today = (await db.execute(
|
||||
@@ -256,6 +259,7 @@ async def dashboard(admin: AdminUser = Depends(get_current_admin), db: AsyncSess
|
||||
total_revenue=round(total_revenue, 2),
|
||||
total_calculations=total_calcs,
|
||||
materials_count=materials_count,
|
||||
clients_count=clients_count,
|
||||
orders_today=orders_today,
|
||||
)
|
||||
|
||||
@@ -276,7 +280,7 @@ class MaterialCreate(BaseModel):
|
||||
uv_resistance: str | None = None
|
||||
food_safe: bool = False
|
||||
description: str | None = None
|
||||
color_options: list[str] = []
|
||||
color_options: list[dict] = []
|
||||
is_active: bool = True
|
||||
|
||||
|
||||
@@ -575,3 +579,96 @@ async def update_setting(
|
||||
await db.commit()
|
||||
logger.info("Setting updated: %s = %s", key, data.value)
|
||||
return {"key": key, "value": data.value}
|
||||
|
||||
|
||||
# ─── Clients CRM ────────────────────────────────────────
|
||||
|
||||
class ClientOut(BaseModel):
|
||||
id: int
|
||||
email: str
|
||||
name: str
|
||||
phone: str | None
|
||||
company: str | None
|
||||
is_active: bool
|
||||
created_at: str
|
||||
orders_count: int = 0
|
||||
total_spent: float = 0
|
||||
|
||||
|
||||
@router.get("/clients", response_model=list[ClientOut])
|
||||
async def list_clients(
|
||||
admin: AdminUser = Depends(get_current_admin),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
logger.info("Admin clients list requested")
|
||||
result = await db.execute(select(Client).order_by(desc(Client.created_at)))
|
||||
clients = result.scalars().all()
|
||||
|
||||
out = []
|
||||
for c in clients:
|
||||
orders_result = await db.execute(
|
||||
select(func.count(Order.id), func.coalesce(func.sum(Order.total_rub), 0))
|
||||
.where(Order.client_id == c.id)
|
||||
)
|
||||
row = orders_result.one()
|
||||
out.append(ClientOut(
|
||||
id=c.id, email=c.email, name=c.name, phone=c.phone,
|
||||
company=c.company, is_active=c.is_active,
|
||||
created_at=c.created_at.isoformat() if c.created_at else "",
|
||||
orders_count=row[0], total_spent=round(row[1], 2),
|
||||
))
|
||||
return out
|
||||
|
||||
|
||||
@router.get("/clients/{client_id}")
|
||||
async def get_client_detail(
|
||||
client_id: int,
|
||||
admin: AdminUser = Depends(get_current_admin),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
result = await db.execute(select(Client).where(Client.id == client_id))
|
||||
client = result.scalar_one_or_none()
|
||||
if not client:
|
||||
raise HTTPException(404, "Клиент не найден")
|
||||
|
||||
orders_result = await db.execute(
|
||||
select(Order).where(Order.client_id == client_id).order_by(desc(Order.created_at))
|
||||
)
|
||||
orders = orders_result.scalars().all()
|
||||
|
||||
return {
|
||||
"client": {
|
||||
"id": client.id, "email": client.email, "name": client.name,
|
||||
"phone": client.phone, "company": client.company,
|
||||
"is_active": client.is_active,
|
||||
"created_at": client.created_at.isoformat() if client.created_at else "",
|
||||
},
|
||||
"orders": [
|
||||
{
|
||||
"order_id": o.order_id, "status": o.status, "total_rub": o.total_rub,
|
||||
"created_at": o.created_at.isoformat() if o.created_at else "",
|
||||
}
|
||||
for o in orders
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
class ClientToggleActive(BaseModel):
|
||||
is_active: bool
|
||||
|
||||
|
||||
@router.patch("/clients/{client_id}/active")
|
||||
async def toggle_client_active(
|
||||
client_id: int,
|
||||
data: ClientToggleActive,
|
||||
admin: AdminUser = Depends(get_current_admin),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
result = await db.execute(select(Client).where(Client.id == client_id))
|
||||
client = result.scalar_one_or_none()
|
||||
if not client:
|
||||
raise HTTPException(404, "Клиент не найден")
|
||||
client.is_active = data.is_active
|
||||
await db.commit()
|
||||
logger.info("Client id=%d is_active=%s (by %s)", client_id, data.is_active, admin.email)
|
||||
return {"status": "ok"}
|
||||
|
||||
Reference in New Issue
Block a user