# 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<Activity, 'automatable' | 'automatedCostFixed' | 'automatedTimeMinutes'>`. 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)
