6.2 KiB
Aprobación de campañas por rol Manager
Fecha: 2026-07-02
Contexto
Hoy red_lead/blue_lead crean, editan, añaden/quitan tests y activan campañas directamente, incluida la fecha de inicio (start_date). No hay ningún control de aprobación intermedio, ni un registro dedicado de quién modifica qué en una campaña (solo el AuditLog genérico, sin endpoint de lectura para campañas).
Roles actuales: admin, red_lead, blue_lead, red_tech, blue_tech, viewer.
Estados actuales de campaña: draft → active → completed → archived.
Objetivo
Los leads solo pueden "montar" campañas (crear, nombrar, añadir/quitar tests) mientras están en borrador. Un nuevo rol manager aprueba la campaña y es quien fija las fechas. Una vez activa, cualquier cambio de tests requiere justificación y pasa de nuevo por aprobación del manager antes de aplicarse. Todo queda registrado en un timeline consultable.
Roles
- Nuevo rol
manager, transversal (no separado por equipo red/blue). adminconserva capacidad de aprobar/rechazar, como respaldo (patrón ya usado en el resto de la app).red_lead/blue_leadpierden la capacidad de activar campañas directamente y de fijarstart_date.
Máquina de estados de campaña
draft ──submit(lead/admin)──> pending_approval ──approve(manager/admin, fija start_date)──> active
│
└──reject(manager/admin, motivo obligatorio)──> draft
active ──complete (sin cambios)──> completed ──archive (sin cambios)──> archived
Reglas:
submitrequiere que la campaña tenga ≥1 test (misma regla que hoy exigeactivate).start_dateno es editable por el lead (se oculta/bloquea en creación y edición). Solo el manager la fija al aprobar; es obligatoria para poder aprobar.- Edición de nombre/descripción/tests solo permitida en
draft. Enpending_approvalla campaña queda congelada. - Rechazo devuelve la campaña a
draft, guardando el motivo; el lead puede corregir y reenviar.
Modificación de campaña activa (añadir/quitar test)
Ninguna modificación de tests se aplica directamente sobre una campaña active. Se crea una entidad CampaignModificationRequest:
- Campos:
campaign_id,requested_by,action(add_test|remove_test),test_id,justification(obligatoria),status(pending/approved/rejected),reviewed_by,reviewed_at,review_notes,created_at. - Mientras la solicitud está
pending, la campaña sigueactivesin ningún cambio real enCampaignTest. - Manager aprueba → se aplica el cambio real (añade/quita la fila de
CampaignTest) y la solicitud pasa aapproved. - Manager rechaza → no se aplica ningún cambio; la solicitud pasa a
rejectedconreview_notes.
Timeline / auditoría
Se reutiliza el AuditLog genérico existente (entity_type="campaign"), sin tabla nueva. Nuevas acciones registradas:
submit_campaign_for_approval, approve_campaign, reject_campaign, request_campaign_modification, approve_campaign_modification, reject_campaign_modification
Nuevo endpoint de lectura GET /campaigns/{id}/timeline que devuelve las entradas de AuditLog para esa campaña ordenadas cronológicamente (mismo patrón que el timeline ya existente para tests).
Endpoints backend
| Endpoint | Rol | Efecto |
|---|---|---|
POST /campaigns/{id}/submit |
lead/admin | draft → pending_approval |
GET /campaigns/pending-approval |
manager/admin | cola de campañas pendientes |
POST /campaigns/{id}/approve |
manager/admin | fija fechas, pending_approval → active |
POST /campaigns/{id}/reject |
manager/admin | motivo obligatorio, pending_approval → draft |
POST /campaigns/{id}/modification-requests |
lead/admin | crea solicitud (solo si active) |
GET /campaigns/modification-requests/pending |
manager/admin | cola de solicitudes pendientes |
POST /campaigns/modification-requests/{id}/approve |
manager/admin | aplica el cambio de test |
POST /campaigns/modification-requests/{id}/reject |
manager/admin | nota obligatoria, no aplica cambio |
GET /campaigns/{id}/timeline |
cualquiera autenticado | historial de la campaña |
Los endpoints existentes PATCH /campaigns/{id}, POST/DELETE .../tests pasan a devolver 409 fuera de draft.
Modelo de datos
backend/app/models/campaign.py:
- Ampliar enum de
statusconpending_approval. - Añadir
approved_by(FK users, nullable),approved_at(datetime, nullable),rejection_reason(text, nullable). start_datepasa a nullable en creación (solo se rellena enapprove).
Nuevo modelo CampaignModificationRequest (tabla nueva, migración Alembic):
id,campaign_id(FK),requested_by(FK users),action(enum),test_id(FK tests),justification(text, not null),status(enum, defaultpending),reviewed_by(FK users, nullable),reviewed_at(datetime, nullable),review_notes(text, nullable),created_at.
Frontend
- Rol
managerañadido al selector de roles en gestión de usuarios. - Botón "Enviar a aprobación" reemplaza "Activar" para el lead en campañas
draft. - Página/cola de aprobación para manager: lista de campañas
pending_approvalcon modal de aprobar (pidestart_dateobligatoria) y modal de rechazar (pide motivo obligatorio). - En campaña
active: botón "Solicitar modificación" (lead) con formulario de justificación obligatoria; sección de solicitudes pendientes con aprobar/rechazar (manager, rechazo con nota obligatoria). - Pestaña "Timeline" en el detalle de campaña, listando las entradas de auditoría en orden cronológico (mismo patrón visual que el timeline ya existente en tests).
Testing
Tests pytest cubriendo:
- Permisos por rol en cada endpoint nuevo (lead no puede aprobar, manager no puede montar/editar tests directamente, tech no puede ni montar ni aprobar).
approvefalla sinstart_date.rejectfalla sin motivo.submitfalla si la campaña no tiene tests.- Campaña
activeno cambia su lista de tests hasta que se aprueba laCampaignModificationRequest. - Rechazo de campaña vuelve a
draftconservando el motivo. GET /campaigns/{id}/timelinedevuelve las entradas en orden cronológico correcto.