# Sprint 3 — Etapa 6: PDF recursos automatizados + tests del sprint ## Contexto Sos Claude Code trabajando en **InQ ROI**. Las reglas de implementación están en `simulador-web/CLAUDE.md`. El brief del sprint está en `sprints/sprint-3/BRIEF.md`. **Estado al iniciar esta etapa:** - Etapas 1–5 completadas. Motor de simulación usa `automatedAssignedResources` correctamente. ActivityPanel tiene sección "Recursos TO-BE". Reporte web muestra comparación AS-IS/TO-BE en filas expandibles. - `Activity.automatedAssignedResources` sigue siendo `?` (opcional) en `types.ts` — pendiente de esta etapa. - `drawResourcesSection` en `pdf-sections.ts` lee `activityRow?.assignedResources` para los campos `utilizationMinutes` y `units`, incluso cuando se exporta el tab "automatizado". Esto es un bug: para el escenario automatizado debería leer `automatedAssignedResources`. - Los factories de Activity en tests (`makeAutoActivity`, `makeActivity`) no incluyen `automatedAssignedResources`. Cuando hagamos el campo requerido, TypeScript los marcará con error. **Esta etapa cierra los tres pendientes del sprint: PDF correcto, campo promovido a requerido, tests del sprint.** --- ## Objetivo **F.1 — PDF recursos automatizados** Corregir `drawResourcesSection` para que cuando se exporta el escenario automatizado lea `automatedAssignedResources` (no `assignedResources`) al mostrar `utilizationMinutes` y `units` por fila. **F.2 — Promover campo a requerido** Cambiar `automatedAssignedResources?: ActivityResourceAssignment[]` → `automatedAssignedResources: ActivityResourceAssignment[]` en `types.ts`. Corregir todos los factories en tests que crean `Activity` sin ese campo. **F.3 — Tests nuevos del sprint** Tests unitarios del motor de simulación cubriendo los tres casos con `automatedAssignedResources`: solo fijo, solo recursos, mixto. Tests de `drawResourcesSection` para escenario automatizado. --- ## Cambios autorizados ### `src/domain/types.ts` Remover el `?` del campo: ```typescript // ANTES: automatedAssignedResources?: ActivityResourceAssignment[] // 🆕 Sprint 3 // DESPUÉS: automatedAssignedResources: ActivityResourceAssignment[] // requerido desde Etapa 6 ``` Después de este cambio, TypeScript reportará errores en los factories de tests. Corregir todos los que aparezcan (ver sección de tests). ### `src/lib/export/pdf-sections.ts` **Agregar parámetro `scenario` a `drawResourcesSection`:** ```typescript export function drawResourcesSection( doc: any, autoTable: Function, result: { perActivity: ActivitySimResult[] }, resources: Resource[], activities: Activity[], currency: string, startY: number, scenario: 'actual' | 'automatizado' = 'actual' // 🆕 default 'actual' = retrocompatible ): void ``` **Corregir el lookup de asignación** (línea ~547 del archivo actual): ```typescript // ANTES (siempre usa AS-IS): const assignment = activityRow?.assignedResources.find((a) => a.resourceId === br.resourceId) // DESPUÉS (usa el array del escenario correcto): const assignment = scenario === 'automatizado' ? activityRow?.automatedAssignedResources?.find((a) => a.resourceId === br.resourceId) : activityRow?.assignedResources.find((a) => a.resourceId === br.resourceId) ``` **Nota:** `automatedAssignedResources` puede ser `undefined` en actividades antiguas migradas sin pasar por ImportPage (aunque la migración Dexie v4 debería poblarlas con `[]`). El optional chaining `?.find` ya cubre ese caso. El resto del cuerpo de `drawResourcesSection` no cambia. ### `src/lib/export/pdf-export.ts` En `exportScenarioPdf`, actualizar la llamada a `drawResourcesSection` para pasar el `tab`: ```typescript // Antes: drawResourcesSection(docAny, autoTable, result, resources, activities, currency, y) // Después: drawResourcesSection(docAny, autoTable, result, resources, activities, currency, y, tab) ``` Esta es la única línea que cambia en este archivo. --- ## Tests — correcciones de factories Cuando se promueva `automatedAssignedResources` a requerido, TypeScript reportará error en los siguientes archivos. Agregar `automatedAssignedResources: []` a cada factory que construye un `Activity`: ### `tests/domain/simulation.test.ts` **Factory `makeAutoActivity`** (~línea 218): agregar campo: ```typescript automatedCostFixed: opts.automatedCost ?? 0, automatedTimeMinutes: opts.automatedTime ?? 0, automatedAssignedResources: opts.automatedAssignedResources ?? [], // 🆕 ``` Si la interfaz de `opts` no incluye el campo, agregar: ```typescript automatedAssignedResources?: ActivityResourceAssignment[] ``` **Factory genérico** (~línea 77): agregar `automatedAssignedResources: []`. **Factory `makeActivity` para recursos** (~línea 410): agregar `automatedAssignedResources: []`. ### `tests/integration/indexeddb-persistence.test.ts` **`makeActivity`** (~línea 40): agregar `automatedAssignedResources: []` al objeto retornado. ### `tests/integration/sprint1-report-roi.test.ts` **Factories inline** (~líneas 51, 311): agregar `automatedAssignedResources: []` a cada construcción de Activity. ### `tests/lib/export/pdf-resources-section.test.ts` **`mockActivity`** (~línea 34): agregar `automatedAssignedResources: []`. ### `tests/integration/dexie-migration.test.ts` **`makeLegacyActivity`** (~línea 19): usa `Omit`. Verificar si el tipo resultante cubre `automatedAssignedResources`. Si TypeScript reporta error, agregar `'automatedAssignedResources'` al Omit O cambiar la función para retornar directamente el campo con `[]`. --- ## Tests — nuevos ### `tests/domain/simulation-sprint3.test.ts` (NUEVO) Cubrir los tres casos de recursos en escenario automatizado. Usar el mismo BPMN lineal simple de un nodo que ya existe en otros tests del dominio. #### Caso 1: solo costo fijo, sin recursos TO-BE ``` Actividad automatable, automatedCostFixed=100, automatedTimeMinutes=60, automatedAssignedResources=[] → scenario=automated: costo esperado = 100 * execProb (sin costo de recursos) → resourceCostBreakdown vacío ``` #### Caso 2: solo recursos TO-BE, sin costo fijo ``` Actividad automatable, automatedCostFixed=0, automatedTimeMinutes=30 automatedAssignedResources=[{ resourceId: 'r1', utilizationMinutes: 60, units: 2 }] Resource r1: costPerHour=30 → scenario=automated: resourceCost = 30 * (60/60) * 2 * execProb = 60 * execProb → resourceCostBreakdown: [{ resourceId: 'r1', resourceName: 'Bot', cost: 60 * execProb }] ``` #### Caso 3: mixto — costo fijo + recurso TO-BE ``` Actividad automatable, automatedCostFixed=50, automatedTimeMinutes=30 automatedAssignedResources=[{ resourceId: 'r1', utilizationMinutes: 30, units: 1 }] Resource r1: costPerHour=60 → scenario=automated: totalCost = (50 + 60*(30/60)*1) * execProb = (50+30) * execProb = 80 * execProb ``` #### Caso 4: actividad NO automatable → usa assignedResources (regresión) ``` Actividad automatable=false, con assignedResources=[r1] y automatedAssignedResources=[r2] → scenario=automated: usa r1 (assignedResources), NO r2 ``` **Nota de arquitectura:** para los tests de dominio puro, usar un grafo BPMN de un solo nodo secuencial (sin gateways), igual que en el `describe('scenario=automated')` ya existente en `simulation.test.ts`. Los inputs del motor (`activities`, `resources`, `gateways`, `graph`) se construyen inline. ### `tests/lib/export/pdf-resources-section.test.ts` — tests nuevos (agregar al archivo existente) Agregar un `describe` nuevo: `drawResourcesSection — escenario automatizado`: #### Test 1: escenario automatizado usa automatedAssignedResources para min/unidades ``` mockActivity con assignedResources=[{ utilMin: 30, units: 1 }] y automatedAssignedResources=[{ utilMin: 5, units: 3 }] Llamar drawResourcesSection(..., scenario='automatizado') → autoTable debe recibir min='5' y units='3' (no '30' y '1') ``` #### Test 2: escenario actual usa assignedResources (regresión) ``` Mismo mockActivity Llamar drawResourcesSection(..., scenario='actual') [o sin el param] → autoTable recibe min='30' y units='1' ``` --- ## ❌ Lo que NO entra en esta etapa - Tests E2E de la biblioteca (Playwright) — diferido a Etapa 7 o post-sprint si hay tiempo - PDF de ROI — no modificar `pdf-sections-roi.ts` - Cambios en `simulation.ts`, `ActivityPanel.tsx`, `ActivitiesTable.tsx` — ya cerrados en Etapa 5 - Cambios en `WorkspacePage.tsx`, `BpmnCanvas.tsx`, stores — ya cerrados en Etapas anteriores - Nuevas features de UI --- ## Verificación antes de entregar ```bash # 1. Build limpio (verifica que el campo requerido no deja ningún any suelto) npm run build # 2. Tests — el count debe subir respecto a 555 npm run test # 3. Control de scope git diff --name-only HEAD ``` El diff debe mostrar únicamente: - `src/domain/types.ts` - `src/lib/export/pdf-sections.ts` - `src/lib/export/pdf-export.ts` - `tests/domain/simulation.test.ts` - `tests/domain/simulation-sprint3.test.ts` (nuevo) - `tests/lib/export/pdf-resources-section.test.ts` - `tests/integration/indexeddb-persistence.test.ts` - `tests/integration/sprint1-report-roi.test.ts` - `tests/integration/dexie-migration.test.ts` Si aparece cualquier archivo de `src/features/`, `src/store/`, o `src/persistence/`: **revertir y reportar**. ### Verificación funcional mandatoria Antes de reportar, verificar manualmente: 1. **PDF automatizado muestra min/unidades correctas:** asignar un recurso TO-BE con `utilizationMinutes=5, units=3` → exportar PDF desde el tab Automatizado → verificar que la tabla de recursos muestra `5` y `3` (no los valores del AS-IS). 2. **PDF actual no regresiona:** exportar PDF desde tab Actual → tabla de recursos muestra los valores de `assignedResources` sin cambio. 3. **TypeScript limpio:** `npm run build` sin errores — el campo promovido a requerido no deja `any` ni errores de tipo. --- ## Criterios de aceptación - [ ] `automatedAssignedResources` requerido en `types.ts` sin errores TypeScript en build. - [ ] PDF tab "Automatizado": tabla de recursos muestra `utilizationMinutes` y `units` de `automatedAssignedResources`. - [ ] PDF tab "Actual": sin regresión, tabla sin cambio. - [ ] Todos los tests previos siguen verdes (555+). - [ ] 4 nuevos tests en `simulation-sprint3.test.ts` (casos 1–4) verdes. - [ ] 2 nuevos tests en `pdf-resources-section.test.ts` verdes. - [ ] Validación visual del director aprobada (PDF tab Automatizado con datos correctos). --- ## Reporte esperado al terminar 1. Resultado de `git diff --name-only HEAD`. 2. Resultado de `npm run build` y `npm run test` (incluir count final de tests). 3. Confirmación de los 3 casos de verificación funcional. 4. Lista de factories de tests corregidos (archivo + línea aproximada). 5. Cualquier decisión de iniciativa (Nivel 1/2/3). 6. Si algún archivo de test tuvo más cambios de los esperados: declararlo. --- ## Referencias - `sprints/sprint-3/BRIEF.md` — sección 4, Feature F - `src/lib/export/pdf-sections.ts` — `drawResourcesSection` (~línea 479), lookup de `assignedResources` (~línea 547) - `src/lib/export/pdf-export.ts` — `exportScenarioPdf` (~línea 54), llamada a `drawResourcesSection` (~línea 147) - `tests/domain/simulation.test.ts` — factories `makeAutoActivity` (~línea 218) y genérico (~línea 77) - `tests/lib/export/pdf-resources-section.test.ts` — `mockActivity` fixture (~línea 34) - `methodology/ANTI_PATTERNS.md` — AP.8 (adelanto silencioso), AP.9 (restricciones contradictorias) - `methodology/PATTERNS.md` — C.7 (git diff como control preventivo)