from datetime import datetime, timezone from fastapi import HTTPException, status from sqlalchemy.orm import Session from ..accounts.models import Account from ..contacts.models import Contact from ..leads.models import Lead from ..opportunities.models import Opportunity from .dto import ActivityCreate, ActivityUpdate from .models import Activity _ALLOWED_TYPES = {"call", "meeting", "task", "email", "note"} def _validate_refs(db: Session, data: dict, tenant_id: int, company_id: int) -> None: """Valida las entidades relacionadas opcionales dentro del tenant/company.""" def scope(model, _id): return ( db.query(model.id) .filter( model.id == _id, model.tenant_id == tenant_id, model.company_id == company_id, model.deleted_at.is_(None), ) .first() ) checks = [ ("account_id", Account, "La cuenta asociada no existe"), ("contact_id", Contact, "El contacto asociado no existe"), ("lead_id", Lead, "El prospecto asociado no existe"), ("opportunity_id", Opportunity, "La oportunidad asociada no existe"), ] for field, model, message in checks: value = data.get(field) if value is not None and not scope(model, value): raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=message) def get_activities( db: Session, tenant_id: int, company_id: int, activity_type: str | None = None, activity_status: str | None = None, account_id: int | None = None, contact_id: int | None = None, lead_id: int | None = None, opportunity_id: int | None = None, ) -> list[Activity]: query = db.query(Activity).filter( Activity.tenant_id == tenant_id, Activity.company_id == company_id, Activity.deleted_at.is_(None), ) if activity_type: query = query.filter(Activity.activity_type == activity_type) if activity_status: query = query.filter(Activity.status == activity_status) if account_id is not None: query = query.filter(Activity.account_id == account_id) if contact_id is not None: query = query.filter(Activity.contact_id == contact_id) if lead_id is not None: query = query.filter(Activity.lead_id == lead_id) if opportunity_id is not None: query = query.filter(Activity.opportunity_id == opportunity_id) return query.order_by(Activity.due_date.asc().nullslast(), Activity.created_at.desc()).all() def get_activity(db: Session, activity_id: int, tenant_id: int, company_id: int) -> Activity: activity = ( db.query(Activity) .filter( Activity.id == activity_id, Activity.tenant_id == tenant_id, Activity.company_id == company_id, Activity.deleted_at.is_(None), ) .first() ) if not activity: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Actividad no encontrada") return activity def create_activity(db: Session, payload: ActivityCreate, tenant_id: int, company_id: int) -> Activity: if payload.activity_type not in _ALLOWED_TYPES: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Tipo de actividad inválido", ) data = payload.model_dump() _validate_refs(db, data, tenant_id, company_id) activity = Activity(**data, tenant_id=tenant_id, company_id=company_id) db.add(activity) db.commit() db.refresh(activity) return activity def update_activity( db: Session, activity_id: int, payload: ActivityUpdate, tenant_id: int, company_id: int ) -> Activity: activity = get_activity(db, activity_id, tenant_id, company_id) data = payload.model_dump(exclude_unset=True) if data.get("activity_type") and data["activity_type"] not in _ALLOWED_TYPES: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Tipo de actividad inválido", ) _validate_refs(db, data, tenant_id, company_id) for field, value in data.items(): setattr(activity, field, value) db.commit() db.refresh(activity) return activity def complete_activity(db: Session, activity_id: int, tenant_id: int, company_id: int) -> Activity: activity = get_activity(db, activity_id, tenant_id, company_id) activity.status = "completed" activity.completed_at = datetime.now(timezone.utc) db.commit() db.refresh(activity) return activity def delete_activity(db: Session, activity_id: int, tenant_id: int, company_id: int) -> None: activity = get_activity(db, activity_id, tenant_id, company_id) activity.deleted_at = datetime.now(timezone.utc) db.commit()