Untitled

 avatar
unknown
plain_text
9 months ago
7.8 kB
13
Indexable
# app/routers/leads.py
from fastapi import APIRouter, Depends, HTTPException, Query
from app.schemas.lead import LeadCreate, LeadUpdate, LeadOut
from app.core.deps import get_current_user, require_role
from app.db.mongodb import get_db
from datetime import datetime, timezone
from bson import ObjectId

router = APIRouter(prefix="/leads", tags=["Leads"])


# ---- HELPERS ----
def normalize_note(note: dict) -> dict:
    return {
        "id": str(note.get("_id")),
        "author_id": str(note.get("author_id")),
        "author_name": note.get("author_name", ""),
        "content": note.get("content", ""),
        "created_at": note.get("created_at"),
    }


def normalize_lead(lead: dict) -> dict:
    return {
        "id": str(lead["_id"]),
        "full_name": lead.get("full_name"),
        "phone_e164": lead.get("phone_e164"),
        "email": lead.get("email"),
        "source": lead.get("source"),
        "status": lead.get("status"),
        "custom_status": lead.get("custom_status"),
        "pool": lead.get("pool"),
        "assigned_to": lead.get("assigned_to"),  # e-posta veya None
        "last_contact_at": lead.get("last_contact_at"),
        "last_activity_at": lead.get("last_activity_at"),
        "updated_at": lead.get("updated_at"),
        "notes": [normalize_note(n) for n in lead.get("notes", [])],
    }


async def resolve_assigned_email(lead: dict, db) -> dict:
    """assigned_to alanını id yerine email'e çevirir"""
    if lead.get("assigned_to"):
        try:
            user = await db["users"].find_one({"_id": ObjectId(lead["assigned_to"])})
            if user:
                lead["assigned_to"] = user.get("email")
            else:
                lead["assigned_to"] = None
        except Exception:
            lead["assigned_to"] = None
    else:
        lead["assigned_to"] = None
    return lead


# ---- ROUTES ----

# CREATE
@router.post("/", response_model=LeadOut)
async def create_lead(data: LeadCreate, user=Depends(require_role("admin"))):
    db = get_db()
    now = datetime.now(timezone.utc)
    doc = {
        "full_name": data.full_name,
        "phone_e164": data.phone_e164,
        "raw_phone": data.raw_phone,
        "email": data.email,
        "source": data.source,
        "status": "Yeni",
        "custom_status": None,
        "pool": "Taze",
        "assigned_to": None,
        "last_contact_at": None,
        "last_activity_at": None,
        "last_update_at": now,
        "notes": [],
        "manager_notes": [],
        "consultations": [],
        "created_at": now,
        "updated_at": now,
    }
    result = await db["leads"].insert_one(doc)
    doc["_id"] = result.inserted_id
    return normalize_lead(doc)


# LIST (dinamik filtreleme + pagination)
@router.get("/")
async def list_leads(
    status: str | None = Query(None, description="Tekli ya da çoklu statü. Örn: cevapsız,meşgul,ulaşılamadı"),
    pool: str | None = Query(None),
    source: str | None = Query(None),
    assigned_to: str | None = Query(None),
    noteSearch: str | None = Query(None),
    phone: str | None = Query(None),
    page: int = Query(1, ge=1),
    page_size: int = Query(500, ge=20, le=500),
    user=Depends(get_current_user),
):
    db = get_db()
    query = {}

    # 🔹 Sales sadece kendi leadlerini görsün
    if not phone:
        if user["role"] == "sales":
            query["assigned_to"] = str(user["_id"])

    # 🔹 Admin email filtresi
    if assigned_to and user["role"] == "admin":
        target_user = await db["users"].find_one({"email": assigned_to})
        if target_user:
            query["assigned_to"] = str(target_user["_id"])
        else:
            query["assigned_to"] = "__not_found__"

    # 🔹 Status filtresi
    if status:
        statuses = [s.strip() for s in status.split(",") if s.strip()]
        if len(statuses) > 1:
            query["status"] = {"$in": statuses}
        else:
            query["status"] = {"$regex": f"^{statuses[0]}$", "$options": "i"}

    # 🔹 Pool filtresi
    if pool:
        query["pool"] = {"$regex": f"^{pool}$", "$options": "i"}

    if source:
        query["source"] = {"$regex": source, "$options": "i"}
    if phone:
        query["phone_e164"] = phone
    if noteSearch:
        query["notes.content"] = {"$regex": noteSearch, "$options": "i"}

    total = await db["leads"].count_documents(query)
    skip = (page - 1) * page_size
    leads = (
        await db["leads"]
        .find(query)
        .skip(skip)
        .limit(page_size)
        .to_list(page_size)
    )

    # 🔹 Sales farklı lead ararsa dummy dön
    if phone and user["role"] == "sales" and not leads:
        lead = await db["leads"].find_one({"phone_e164": phone})
        if lead and str(lead.get("assigned_to")) != str(user["_id"]):
            lead = await resolve_assigned_email(lead, db)
            return {
                "total": 1,
                "page": 1,
                "page_size": 1,
                "items": [{
                    "id": str(lead["_id"]),
                    "full_name": "Başka kullanıcıda",
                    "phone_e164": phone,
                    "email": None,
                    "source": None,
                    "status": None,
                    "custom_status": None,
                    "pool": None,
                    "assigned_to": lead.get("assigned_to"),
                    "last_contact_at": None,
                    "last_activity_at": None,
                    "updated_at": None,
                    "notes": [],
                }]
            }

    result = []
    for l in leads:
        l = await resolve_assigned_email(l, db)
        result.append(normalize_lead(l))

    return {"total": total, "page": page, "page_size": page_size, "items": result}


# GET SINGLE LEAD
@router.get("/{lead_id}", response_model=LeadOut)
async def get_lead(lead_id: str, user=Depends(get_current_user)):
    db = get_db()
    lead = await db["leads"].find_one({"_id": ObjectId(lead_id)})
    if not lead:
        raise HTTPException(404, "Lead not found")

    # 🔒 Sales sadece kendi lead'ini görebilsin
    if user["role"] == "sales" and str(lead.get("assigned_to")) != str(user["_id"]):
        raise HTTPException(403, "Permission denied")

    # 🔹 assigned_to e-mail'e çevrilirken id kaybolmasın
    assigned_to_id = lead.get("assigned_to")
    lead = await resolve_assigned_email(lead, db)
    if assigned_to_id:
        lead["assigned_to_id"] = str(assigned_to_id)

    # 🔹 notları normalize ederek dönelim
    lead["notes"] = [normalize_note(n) for n in lead.get("notes", [])]

    return normalize_lead(lead)


# UPDATE
@router.patch("/{lead_id}", response_model=LeadOut)
async def update_lead(lead_id: str, data: LeadUpdate, user=Depends(get_current_user)):
    db = get_db()
    lead = await db["leads"].find_one({"_id": ObjectId(lead_id)})
    if not lead:
        raise HTTPException(404, "Lead not found")

    if user["role"] == "sales" and str(lead.get("assigned_to")) != str(user["_id"]):
        raise HTTPException(403, "Permission denied")

    update_data = {k: v for k, v in data.dict().items() if v is not None}
    now = datetime.now(timezone.utc)
    update_data["last_update_at"] = now

    if "status" in update_data:
        update_data["last_activity_at"] = now

    await db["leads"].update_one({"_id": ObjectId(lead_id)}, {"$set": update_data})
    new_lead = await db["leads"].find_one({"_id": ObjectId(lead_id)})

    new_lead = await resolve_assigned_email(new_lead, db)
    return normalize_lead(new_lead)
Editor is loading...
Leave a Comment