Sprint 3 completo — 7 etapas: - Etapa 1-2: biblioteca de procesos con agrupación por cliente (Dexie v4, LibraryPage, library-store, groupId/tags en Process) - Etapa 3: modal de configuración global por proceso (horizonte, frecuencia, inversión, moneda) con validación y persistencia - Etapa 4: panel de recursos rediseñado con soporte de unidades y minutos de utilización (utilizationMinutes + units) - Etapa 5: recursos en escenario automatizado — motor de simulación, UI de edición en ActivityPanel, comparación AS-IS/TO-BE en reporte - Etapa 6: PDF recursos automatizados, automatedAssignedResources requerido, 561 tests pasando - Etapa 7: identidad de marca — AppHeader gradiente naranja/magenta con logo InQuality en 5 páginas, favicon con logo real, logo/marca navegan a home Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
271 lines
11 KiB
Markdown
271 lines
11 KiB
Markdown
# 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)
|