""" API Routes for Leads Management. """ from datetime import datetime from typing import Optional, List from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from pydantic import BaseModel from app.core.database import get_db from app.models.lead import Lead from app.models.interaction import Interaction from app.services.odoo_service import odoo_service router = APIRouter() class LeadCreate(BaseModel): """Schema for creating a lead.""" name: Optional[str] = None email: Optional[str] = None phone: Optional[str] = None company: Optional[str] = None platform: str username: Optional[str] = None profile_url: Optional[str] = None interest: Optional[str] = None notes: Optional[str] = None priority: str = "medium" products_interested: Optional[List[int]] = None services_interested: Optional[List[int]] = None tags: Optional[List[str]] = None class LeadUpdate(BaseModel): """Schema for updating a lead.""" name: Optional[str] = None email: Optional[str] = None phone: Optional[str] = None company: Optional[str] = None interest: Optional[str] = None notes: Optional[str] = None status: Optional[str] = None priority: Optional[str] = None assigned_to: Optional[str] = None products_interested: Optional[List[int]] = None services_interested: Optional[List[int]] = None tags: Optional[List[str]] = None @router.get("/") async def list_leads( status: Optional[str] = None, priority: Optional[str] = None, platform: Optional[str] = None, synced: Optional[bool] = None, limit: int = Query(50, ge=1, le=200), offset: int = Query(0, ge=0), db: Session = Depends(get_db) ): """ List all leads with optional filters. - **status**: Filter by status (new, contacted, qualified, proposal, won, lost) - **priority**: Filter by priority (low, medium, high, urgent) - **platform**: Filter by source platform - **synced**: Filter by Odoo sync status """ query = db.query(Lead) if status: query = query.filter(Lead.status == status) if priority: query = query.filter(Lead.priority == priority) if platform: query = query.filter(Lead.platform == platform) if synced is not None: query = query.filter(Lead.synced_to_odoo == synced) total = query.count() leads = query.order_by(Lead.created_at.desc()).offset(offset).limit(limit).all() return { "leads": [lead.to_dict() for lead in leads], "total": total, "limit": limit, "offset": offset } @router.get("/{lead_id}") async def get_lead( lead_id: int, db: Session = Depends(get_db) ): """ Get a specific lead by ID. """ lead = db.query(Lead).filter(Lead.id == lead_id).first() if not lead: raise HTTPException(status_code=404, detail="Lead not found") return lead.to_dict() @router.post("/") async def create_lead( lead_data: LeadCreate, db: Session = Depends(get_db) ): """ Create a new lead manually. """ lead = Lead( name=lead_data.name, email=lead_data.email, phone=lead_data.phone, company=lead_data.company, platform=lead_data.platform, username=lead_data.username, profile_url=lead_data.profile_url, interest=lead_data.interest, notes=lead_data.notes, priority=lead_data.priority, products_interested=lead_data.products_interested, services_interested=lead_data.services_interested, tags=lead_data.tags, status="new" ) db.add(lead) db.commit() db.refresh(lead) return { "message": "Lead created successfully", "lead": lead.to_dict() } @router.post("/from-interaction/{interaction_id}") async def create_lead_from_interaction( interaction_id: int, interest: Optional[str] = None, priority: str = "medium", notes: Optional[str] = None, db: Session = Depends(get_db) ): """ Create a lead from an existing interaction. - **interaction_id**: ID of the interaction to convert - **interest**: What the lead is interested in - **priority**: Lead priority (low, medium, high, urgent) - **notes**: Additional notes """ # Get interaction interaction = db.query(Interaction).filter(Interaction.id == interaction_id).first() if not interaction: raise HTTPException(status_code=404, detail="Interaction not found") # Check if lead already exists for this interaction existing = db.query(Lead).filter(Lead.interaction_id == interaction_id).first() if existing: raise HTTPException( status_code=400, detail="Lead already exists for this interaction" ) # Create lead lead = Lead( interaction_id=interaction_id, platform=interaction.platform, username=interaction.author_username, profile_url=interaction.author_profile_url, source_content=interaction.content, interest=interest or f"Interest shown in post {interaction.post_id}", notes=notes, priority=priority, status="new" ) # Mark interaction as potential lead interaction.is_potential_lead = True db.add(lead) db.commit() db.refresh(lead) return { "message": "Lead created from interaction", "lead": lead.to_dict() } @router.put("/{lead_id}") async def update_lead( lead_id: int, lead_data: LeadUpdate, db: Session = Depends(get_db) ): """ Update an existing lead. """ lead = db.query(Lead).filter(Lead.id == lead_id).first() if not lead: raise HTTPException(status_code=404, detail="Lead not found") # Update only provided fields update_data = lead_data.dict(exclude_unset=True) for field, value in update_data.items(): if value is not None: setattr(lead, field, value) lead.updated_at = datetime.utcnow() # If status changed to contacted, update last_contacted_at if lead_data.status == "contacted": lead.last_contacted_at = datetime.utcnow() db.commit() db.refresh(lead) return { "message": "Lead updated successfully", "lead": lead.to_dict() } @router.delete("/{lead_id}") async def delete_lead( lead_id: int, db: Session = Depends(get_db) ): """ Delete a lead. """ lead = db.query(Lead).filter(Lead.id == lead_id).first() if not lead: raise HTTPException(status_code=404, detail="Lead not found") db.delete(lead) db.commit() return {"message": "Lead deleted successfully"} @router.post("/{lead_id}/sync-odoo") async def sync_lead_to_odoo( lead_id: int, db: Session = Depends(get_db) ): """ Sync a specific lead to Odoo CRM. """ lead = db.query(Lead).filter(Lead.id == lead_id).first() if not lead: raise HTTPException(status_code=404, detail="Lead not found") if lead.synced_to_odoo: return { "message": "Lead already synced to Odoo", "odoo_lead_id": lead.odoo_lead_id } result = await odoo_service.create_lead(lead) if not result.get("success"): raise HTTPException( status_code=500, detail=result.get("error", "Sync failed") ) lead.odoo_lead_id = result["odoo_lead_id"] lead.synced_to_odoo = True lead.odoo_synced_at = datetime.utcnow() db.commit() return { "message": "Lead synced to Odoo successfully", "odoo_lead_id": result["odoo_lead_id"] } @router.get("/stats/summary") async def get_leads_summary( db: Session = Depends(get_db) ): """ Get leads summary statistics. """ from sqlalchemy import func total = db.query(Lead).count() by_status = db.query( Lead.status, func.count(Lead.id) ).group_by(Lead.status).all() by_platform = db.query( Lead.platform, func.count(Lead.id) ).group_by(Lead.platform).all() by_priority = db.query( Lead.priority, func.count(Lead.id) ).group_by(Lead.priority).all() unsynced = db.query(Lead).filter(Lead.synced_to_odoo == False).count() return { "total": total, "by_status": {status: count for status, count in by_status}, "by_platform": {platform: count for platform, count in by_platform}, "by_priority": {priority: count for priority, count in by_priority}, "unsynced_to_odoo": unsynced }