- Tabla crm.cases (expediente) con folio EXP2026-08-001 (next_folio entidad EXP,
sin dirección). Nace al crear la Oportunidad y se hereda vía case_id a
solicitud → cotización → operación → factura. advance_stage solo avanza.
- case_id (FK a crm.cases) en crm.opportunities/service_requests/quotes,
ops.shipments y fin.invoices; propagación en sus create_*. Migración
d4e5f6a7b8c9 reversible.
- Endpoints GET /v1/crm/cases, /cases/{id}, /cases/by-ref/{ref} con timeline
(historia completa para UI y otros sistemas).
- Frontend: casesAPI, ruta /dashboard/crm/expedientes (lista + timeline vertical),
chip "📁 Expediente" en solicitud/cotización, "Expedientes" en el sidebar.
- Consecutivo de folios sin tope (soporta >10,000,000/mes).
- 4 pruebas de expediente (minteo, propagación, timeline, no-retroceso). Suite en verde (113).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
52 lines
1.7 KiB
Python
52 lines
1.7 KiB
Python
from fastapi import APIRouter, Depends, Query
|
|
from sqlalchemy.orm import Session
|
|
|
|
from core.database import get_core_db
|
|
from core.security import get_current_user
|
|
|
|
from . import service
|
|
from .dto import CaseResponse, CaseWithTimeline
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
def _with_timeline(db, case) -> CaseWithTimeline:
|
|
data = CaseWithTimeline.model_validate(case)
|
|
data.timeline = service.build_timeline(db, case) # type: ignore[assignment]
|
|
return data
|
|
|
|
|
|
@router.get("/cases", response_model=list[CaseResponse])
|
|
def list_cases(
|
|
company_id: int = Query(..., description="Company ID"),
|
|
search: str | None = Query(None),
|
|
account_id: int | None = Query(None),
|
|
stage: str | None = Query(None),
|
|
current_user: dict = Depends(get_current_user),
|
|
db: Session = Depends(get_core_db),
|
|
):
|
|
return service.get_cases(db, current_user["tenant_id"], company_id, search, account_id, stage)
|
|
|
|
|
|
@router.get("/cases/by-ref/{reference}", response_model=CaseWithTimeline)
|
|
def get_case_by_ref(
|
|
reference: str,
|
|
company_id: int = Query(..., description="Company ID"),
|
|
current_user: dict = Depends(get_current_user),
|
|
db: Session = Depends(get_core_db),
|
|
):
|
|
"""Expediente + historia completa por su referencia (para UI y otros sistemas)."""
|
|
case = service.get_case_by_reference(db, reference, current_user["tenant_id"], company_id)
|
|
return _with_timeline(db, case)
|
|
|
|
|
|
@router.get("/cases/{case_id}", response_model=CaseWithTimeline)
|
|
def get_case(
|
|
case_id: int,
|
|
company_id: int = Query(..., description="Company ID"),
|
|
current_user: dict = Depends(get_current_user),
|
|
db: Session = Depends(get_core_db),
|
|
):
|
|
case = service.get_case(db, case_id, current_user["tenant_id"], company_id)
|
|
return _with_timeline(db, case)
|