mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-18 07:19:35 +00:00
162 lines
5.9 KiB
Python
162 lines
5.9 KiB
Python
"""Sales OS: commission ledger (شفافية عمولة)، مهام، حصص، تأهيل مندوب."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Dict, Optional
|
|
|
|
from fastapi import APIRouter, Depends
|
|
from pydantic import BaseModel, Field
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.database import get_db
|
|
from app.api.deps import get_current_user, get_optional_user, require_role, get_current_tenant
|
|
from app.models.user import User
|
|
from app.models.tenant import Tenant
|
|
from app.services.sales_os_service import (
|
|
build_commission_ledger,
|
|
build_daily_digest,
|
|
build_deal_health,
|
|
build_manager_team_summary,
|
|
demo_commission_ledger,
|
|
merge_quota_view,
|
|
pipeline_value_open_deals,
|
|
pipeline_value_open_deals_scoped,
|
|
rep_onboarding_playbook,
|
|
tasks_inbox_today,
|
|
)
|
|
|
|
router = APIRouter(prefix="/sales-os", tags=["Sales OS"])
|
|
|
|
|
|
class QuotaUpdate(BaseModel):
|
|
default_monthly_quota_sar: Optional[float] = None
|
|
rep_quotas: Optional[Dict[str, float]] = Field(default=None, description="user_id -> monthly SAR target")
|
|
|
|
|
|
@router.get("/commission-ledger")
|
|
async def commission_ledger(
|
|
db: AsyncSession = Depends(get_db),
|
|
user: Optional[User] = Depends(get_optional_user),
|
|
):
|
|
"""
|
|
صفقة → عمولة → دفعة. بدون توكن: بيانات توضيحية. مع توكن: بيانات المستأجر (أو توضيحي إن فارغ).
|
|
"""
|
|
if user:
|
|
data = await build_commission_ledger(db, user.tenant_id)
|
|
if not data["items"]:
|
|
return demo_commission_ledger()
|
|
return data
|
|
return demo_commission_ledger()
|
|
|
|
|
|
@router.get("/tasks-inbox")
|
|
async def tasks_inbox(
|
|
db: AsyncSession = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
):
|
|
"""أنشطة مرتبطة بالمستخدم — بداية صندوق مهام."""
|
|
items = await tasks_inbox_today(db, user.tenant_id, user.id)
|
|
return {"items": items, "count": len(items)}
|
|
|
|
|
|
@router.get("/quota")
|
|
async def quota_overview(
|
|
db: AsyncSession = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
tenant: Tenant = Depends(get_current_tenant),
|
|
):
|
|
pipeline = await pipeline_value_open_deals_scoped(
|
|
db, user.tenant_id, user_id=user.id, role=user.role or "agent"
|
|
)
|
|
settings = tenant.settings if isinstance(tenant.settings, dict) else {}
|
|
return merge_quota_view(settings, user.id, pipeline)
|
|
|
|
|
|
@router.put("/quota")
|
|
async def quota_update(
|
|
body: QuotaUpdate,
|
|
db: AsyncSession = Depends(get_db),
|
|
user: User = Depends(require_role("owner", "admin", "manager")),
|
|
tenant: Tenant = Depends(get_current_tenant),
|
|
):
|
|
base = tenant.settings if isinstance(tenant.settings, dict) else {}
|
|
sales_os = dict(base.get("sales_os") or {})
|
|
if body.default_monthly_quota_sar is not None:
|
|
sales_os["default_monthly_quota_sar"] = body.default_monthly_quota_sar
|
|
if body.rep_quotas is not None:
|
|
sales_os["rep_quotas"] = {str(k): float(v) for k, v in body.rep_quotas.items()}
|
|
tenant.settings = {**base, "sales_os": sales_os}
|
|
db.add(tenant)
|
|
await db.flush()
|
|
pipeline = await pipeline_value_open_deals_scoped(
|
|
db, user.tenant_id, user_id=user.id, role=user.role or "agent"
|
|
)
|
|
return merge_quota_view(tenant.settings or {}, user.id, pipeline)
|
|
|
|
|
|
@router.get("/rep-onboarding")
|
|
async def rep_onboarding_playbook_endpoint():
|
|
"""مسار 7/14/30 يوم — محتوى ثابت يُربط لاحقاً بتتبع DB."""
|
|
return rep_onboarding_playbook()
|
|
|
|
|
|
@router.get("/overview")
|
|
async def sales_os_overview(
|
|
db: AsyncSession = Depends(get_db),
|
|
user: Optional[User] = Depends(get_optional_user),
|
|
):
|
|
"""لقطة واحدة للواجهة: عمولة + حصة + مهام + تأهيل."""
|
|
out: Dict[str, Any] = {"rep_onboarding": rep_onboarding_playbook()}
|
|
if user:
|
|
t_result = await db.execute(select(Tenant).where(Tenant.id == user.tenant_id))
|
|
tenant = t_result.scalar_one_or_none()
|
|
settings = tenant.settings if tenant and isinstance(tenant.settings, dict) else {}
|
|
pipeline = await pipeline_value_open_deals_scoped(
|
|
db, user.tenant_id, user_id=user.id, role=user.role or "agent"
|
|
)
|
|
ledger = await build_commission_ledger(db, user.tenant_id)
|
|
if not ledger["items"]:
|
|
ledger = demo_commission_ledger()
|
|
out["commission_ledger"] = ledger
|
|
out["quota"] = merge_quota_view(settings, user.id, pipeline)
|
|
out["tasks"] = await tasks_inbox_today(db, user.tenant_id, user.id)
|
|
out["daily_digest"] = await build_daily_digest(
|
|
db, user.tenant_id, user.id, user.role or "agent", settings
|
|
)
|
|
else:
|
|
out["commission_ledger"] = demo_commission_ledger()
|
|
out["quota"] = None
|
|
out["tasks"] = []
|
|
out["daily_digest"] = None
|
|
return out
|
|
|
|
|
|
@router.get("/daily-digest")
|
|
async def daily_digest(
|
|
db: AsyncSession = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
tenant: Tenant = Depends(get_current_tenant),
|
|
):
|
|
"""ملخّص يومي: مهام، حصة، إغلاقات قريبة، اقتراحات."""
|
|
settings = tenant.settings if isinstance(tenant.settings, dict) else {}
|
|
return await build_daily_digest(db, user.tenant_id, user.id, user.role or "agent", settings)
|
|
|
|
|
|
@router.get("/manager-summary")
|
|
async def manager_summary(
|
|
db: AsyncSession = Depends(get_db),
|
|
user: User = Depends(require_role("owner", "admin", "manager")),
|
|
):
|
|
"""أنبوب الفريق حسب المندوب — للمدير."""
|
|
return await build_manager_team_summary(db, user.tenant_id)
|
|
|
|
|
|
@router.get("/deal-health")
|
|
async def deal_health(
|
|
db: AsyncSession = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
):
|
|
"""صحة الصفقات المفتوحة — إشارات أولية."""
|
|
return await build_deal_health(db, user.tenant_id, user.id, user.role or "agent")
|