# *Dynamic Report Builder — Backend por fases*

## *Visión*

*Backend para que cada* `maincompany` *personalice **documentos imprimibles** (warehouse, paquete, guía; facturas y otros tipos en release futuro) sin depender de un template Jasper por cliente. Reutiliza* `PdfGeneratorService`*,* `ExcelService` *y el patrón* `Labelformat` *+* `Element`*.*

***Roadmap activo:** refinar los **tres documentos** Fase 2 (*`WHREC_DOCUMENT`*,* `PACKAGE_DOCUMENT`*,* `GUIA_DOCUMENT`*). **No** nuevos templates documento hasta validarlos en producción. Reportes **lista** tabulares: MVP Fase 1; expansión deferida.*

***Fuera de scope backend:** UI, Jasper nuevo, SQL libre para clientes, BI, scheduling, email, IA generadora.*

---

## *Arquitectura objetivo*

```
ReportTemplate (catálogo global)
    └── ReportConfiguration (por maincompany, JSON)
            └── ReportDataProvider (por tipo)
                    └── DynamicReportRenderService
                            ├── Preview JSON
                            ├── PDF (PdfGeneratorService)
                            └── Excel (ExcelService)
```

### *Referencias en el codebase actual*


| *Patrón existente*         | *Ubicación*                                                              | *Uso para Report Builder*             |
| -------------------------- | ------------------------------------------------------------------------ | ------------------------------------- |
| *Jasper externo*           | `src/Service/ReportService.php`                                          | *No extender; convivir en Fase 4*     |
| *Export tabular PDF/Excel* | `src/Service/BaseService.php`*,* `PdfGeneratorService`*,* `ExcelService` | *Motor principal Fase 1*              |
| *Builder dinámico*         | `src/Entity/Labelformat.php`*,* `Element`                                | *Modelo de configuración por cliente* |


---

## *Estrategia de implementación (IA y PRs)*

***No implementar una fase completa en un solo prompt.** Segmentar en bloques pequeños: cada segmento = **1 PR revisable** o **1 sesión de IA**.*


| *Regla*       | *Detalle*                                                                 |
| ------------- | ------------------------------------------------------------------------- |
| *Un segmento* | *Una responsabilidad clara, compila y es probabile*                       |
| *Orden*       | *Respetar dependencias entre segmentos*                                   |
| *Validar*     | *Probar cada segmento antes del siguiente*                                |
| *Prompt IA*   | *Referenciar este doc + número de segmento (ej.* `Fase 1 — Segmento 3`*)* |
| *Tamaño PR*   | *Ideal: 5–15 archivos por segmento*                                       |


### *Mapa de segmentos por fase*


| *Fase*   | *Segmentos* | *PRs estimados* |
| -------- | ----------- | --------------- |
| *Fase 1* | *7*         | *7*             |
| *Fase 2* | *6*         | *6*             |
| *Fase 3* | *5*         | *5*             |
| *Fase 4* | *4*         | *4*             |


---

## *Fase 1 — Fundación + MVP (4–6 semanas)*

### *Objetivo*

*Catálogo de reportes, CRUD de configuraciones, preview JSON y export PDF/Excel para reportes piloto (*`PACKAGES`*,* `WAREHOUSE_RECEIPT`*,* `GUIA`*).*

### *Estado de implementación (backend)*


| *Segmento*                        | *Estado*  | *Notas*                                                  |
| --------------------------------- | --------- | -------------------------------------------------------- |
| *1.1 Fundación de datos*          | *✅ Hecho* | *Enum, entidades, repos, migración + seed (3 templates)* |
| *1.2 Catálogo templates*          | *✅ Hecho* | *Listado paginado + detalle por* `code`                  |
| *1.3 CRUD configurations*         | *✅ Hecho* | *CRUD + filtros + paginación por cursor*                 |
| *1.4 Motor merge/render*          | *✅ Hecho* | `DynamicReportRenderService`*, registry, DTOs render*    |
| *1.5 Provider PACKAGES + preview* | *✅ Hecho* | `PackagesReportDataProvider`*,* `POST .../preview`       |
| *1.6 Export PDF/Excel*            | *✅ Hecho* | `ReportExportService::export()`*,* `POST .../export`     |
| *1.7 Provider WHREC + cierre*     | *✅ Hecho* | `WarehouseReceiptReportDataProvider`*; GUIA sigue mock*  |


***Fase 1 backend:** ✅ Completa (2026-06).*

***Archivos principales (Fase 1 completa):***


| *Área*            | *Rutas*                                                                                                                                               |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| *Enum*            | `src/Enums/ReportTemplateCodeEnum.php`                                                                                                                |
| *Entidades*       | `src/Entity/ReportTemplate.php`*,* `ReportConfiguration.php`                                                                                          |
| *Repositories*    | `src/Repository/ReportTemplateRepository.php`*,* `ReportConfigurationRepository.php`                                                                  |
| *Motor*           | `src/Service/ReportBuilder/DynamicReportRenderService.php`*,* `ReportProviderRegistry.php`*,* `ReportExportService.php`                               |
| *Providers*       | `Provider/PackagesReportDataProvider.php`*,* `Provider/WarehouseReceiptReportDataProvider.php`*,* `Provider/MockReportDataProvider.php` *(solo GUIA)* |
| *Services CRUD*   | `src/Service/ReportTemplateService.php`*,* `ReportConfigurationService.php`*,* `ReportBuilder/ReportConfigurationConfigValidator.php`                 |
| *Controllers*     | `src/Controller/ReportTemplateController.php`*,* `ReportConfigurationController.php`                                                                  |
| *DTOs respuesta*  | `src/DTO/ReportTemplateListItemDTO.php`*,* `ReportTemplateDetailDTO.php`*,* `ReportConfigurationDTO.php`*,* `src/DTO/ReportBuilder/`*                 |
| *DTOs validación* | `src/ValidationDTO/ValidationReportBuilder/`*                                                                                                         |
| *Tests*           | `tests/Unit/Service/ReportBuilder/`*                                                                                                                  |
| *Migración / SQL* | `migrations/Version20260611120000.php`*,* `scripts/report_builder_tables_Version20260611120000.sql`                                                   |


### *Entregables globales*


| *Área*        | *Entregable*                                                                                         |
| ------------- | ---------------------------------------------------------------------------------------------------- |
| *Modelo*      | `ReportTemplate`*,* `ReportConfiguration`                                                            |
| *Enum*        | `ReportTemplateCodeEnum`*:* `PACKAGES`*,* `WAREHOUSE_RECEIPT`*,* `GUIA`                              |
| *Migraciones* | `migrations/Version20260611120000.php` *+* `scripts/report_builder_tables_Version20260611120000.sql` |
| *API*         | *Catálogo ✅, CRUD config ✅, preview/export ✅*                                                        |
| *Motor*       | `ReportDataProviderInterface`*, render service, export service*                                      |
| *Seguridad*   | *Scope por* `maincompany`*, roles admin*                                                             |
| *Docs*        | *OpenAPI en* `/api/doc`                                                                              |


### *Modelo de datos*

`**report_template`****

- `id`*,* `code` *(unique),* `name`*,* `description`*,* `category`
- `default_config` *(JSON): secciones y columnas base*
- `active`*,* `created_at`*,* `updated_at`

`**report_configuration*`*

- `id`*,* `maincompany_id`*,* `report_template_id`
- `name`*,* `is_default`*,* `active`
- `config` *(JSON): overrides del cliente*
- `created_by`*,* `created_at`*,* `updated_at`

***Estructura JSON* `config` *(ejemplo)***

```json
{
  "branding": {
    "showLogo": true,
    "showCompanyName": true,
    "headerTitle": "Reporte de Paquetes"
  },
  "sections": [
    {
      "key": "summary",
      "visible": true,
      "order": 1,
      "title": "Resumen"
    },
    {
      "key": "detail_table",
      "visible": true,
      "order": 2,
      "title": "Detalle",
      "columns": [
        { "key": "number", "visible": true, "order": 1, "label": "Número" },
        { "key": "tracking", "visible": true, "order": 2, "label": "Tracking" }
      ]
    }
  ]
}
```

### *API Fase 1*

***Autenticación:** JWT. **Permiso:*** `ROLE_ADMIN` *en todos los endpoints implementados.*

#### *Endpoints implementados (1.2 + 1.3)*


| *Método* | *Ruta*                            | *Descripción*                                       | *Estado* |
| -------- | --------------------------------- | --------------------------------------------------- | -------- |
| *GET*    | `/api/report-templates`           | *Catálogo paginado*                                 | *✅*      |
| *GET*    | `/api/report-templates/{code}`    | *Template +* `defaultConfig` *+* `availableColumns` | *✅*      |
| *GET*    | `/api/report-configurations`      | *Configs del cliente (paginado + filtros)*          | *✅*      |
| *GET*    | `/api/report-configurations/{id}` | *Detalle (scope* `maincompany`*)*                   | *✅*      |
| *POST*   | `/api/report-configurations`      | *Crear desde* `templateCode`                        | *✅*      |
| *PUT*    | `/api/report-configurations/{id}` | *Actualizar* `name`*,* `config`*,* `isDefault`      | *✅*      |
| *DELETE* | `/api/report-configurations/{id}` | *Desactivar (*`active = false`*)*                   | *✅*      |


#### *Endpoints preview y export (1.5–1.6)*


| *Método* | *Ruta*                                    | *Descripción*                                                      | *Estado* |
| -------- | ----------------------------------------- | ------------------------------------------------------------------ | -------- |
| *POST*   | `/api/report-configurations/{id}/preview` | *Preview JSON (*`fromDate`*,* `toDate`*,* `agencyId` *opcionales)* | *✅*      |
| *POST*   | `/api/report-configurations/{id}/export`  | *PDF o Excel (*`format` *+* `filters` *anidados)*                  | *✅*      |


***Preview body (opcional):*** `{ "fromDate": "2026-01-01", "toDate": "2026-06-30", "agencyId": 32 }`

***Export body:*** `{ "format": "pdf"|"excel", "filters": { ... } }` *— reutiliza los mismos filtros que preview.*

***Providers Fase 1:*** `PACKAGES` *y* `WAREHOUSE_RECEIPT` *con datos reales;* `GUIA` *usa* `MockReportDataProvider` *(preview/export con fixtures hasta Fase 2+).*

---

#### *Paginación por cursor (listas)*

*Patrón alineado a* `AirportController` */* `CursorService`*. Respuesta:*

```json
{
  "data": [ ... ],
  "pagination": {
    "cursor": "eyJmaXJzdCI6...",
    "hasNext": true,
    "hasPrevious": false,
    "itemsPerPage": 20
  }
}
```


| *Query param* | *Default* | *Aplica a*                              |
| ------------- | --------- | --------------------------------------- |
| `limit`       | `20`      | *Ambos listados*                        |
| `cursor`      | *—*       | *Token devuelto en* `pagination.cursor` |
| `direction`   | `next`    | `next` *|* `prev`                       |


***Orden:*** `createdAt DESC, id DESC` *(más recientes primero).*

***Ejemplos:***

```
GET /api/report-templates?limit=10
GET /api/report-templates?limit=10&cursor=<cursor>&direction=next
GET /api/report-configurations?limit=10&templateCode=PACKAGES&pattern=paquetes
```

---

#### *Filtros de listado*

`**GET /api/report-templates*`*


| *Query param* | *Tipo*   | *Descripción*                           |
| ------------- | -------- | --------------------------------------- |
| `category`    | *string* | *Filtra por* `report_template.category` |


`**GET /api/report-configurations*`*


| *Query param*     | *Tipo*   | *Default* | *Descripción*                                       |
| ----------------- | -------- | --------- | --------------------------------------------------- |
| `pattern`         | *string* | *—*       | *Búsqueda parcial en* `name` *(*`LIKE %pattern%`*)* |
| `templateCode`    | *string* | *—*       | `PACKAGES`*,* `WAREHOUSE_RECEIPT`*,* `GUIA`         |
| `isDefault`       | *bool*   | *—*       | *Solo configs default o no default*                 |
| `includeInactive` | *bool*   | `false`   | *Incluir configs desactivadas (DELETE lógico)*      |


*Scope implícito: solo configs de la* `maincompany` *del usuario JWT.*

---

#### *Cuerpos de request (CRUD config)*

***POST* `/api/report-configurations*`* *— crea config copiando* `default_config` *del template:*

```json
{
  "templateCode": "PACKAGES",
  "name": "Mi reporte de paquetes",
  "isDefault": false
}
```

***PUT* `/api/report-configurations/{id}`** *— al menos un campo requerido;* `config` *reemplaza el JSON completo:*

```json
{
  "name": "Reporte personalizado",
  "isDefault": true,
  "config": {
    "branding": {
      "showLogo": true,
      "showCompanyName": true,
      "headerTitle": "Reporte de Paquetes"
    },
    "sections": [
      {
        "key": "detail_table",
        "visible": true,
        "order": 1,
        "title": "Detalle",
        "columns": [
          { "key": "number", "visible": true, "order": 1, "label": "Número" },
          { "key": "tracking", "visible": true, "order": 2, "label": "Tracking" }
        ]
      }
    ]
  }
}
```

*Validación: keys de* `sections` *y* `columns` *deben existir en el* `default_config` *del template asociado.*

---

#### *Respuestas de referencia*

***GET* `/api/report-templates/{code}*`* *— incluye schema de columnas:*

```json
{
  "data": {
    "id": 1,
    "code": "PACKAGES",
    "name": "Packages Report",
    "defaultConfig": { "branding": { ... }, "sections": [ ... ] },
    "availableColumns": [
      {
        "sectionKey": "detail_table",
        "sectionTitle": "Package Detail",
        "key": "number",
        "label": "Number",
        "order": 1,
        "visible": true
      }
    ]
  }
}
```

***POST* `/api/report-configurations`** *— respuesta:*

```json
{
  "data": {
    "id": 1,
    "name": "Mi reporte de paquetes",
    "templateCode": "PACKAGES",
    "templateName": "Packages Report",
    "isDefault": false,
    "active": true,
    "config": { ... },
    "createdById": 42,
    "createdAt": "2026-06-11 14:30:00",
    "updatedAt": "2026-06-11 14:30:00"
  }
}
```

---

#### *Preview y export (pendiente)*

```json
{
  "format": "pdf",
  "filters": {
    "fromDate": "2026-01-01",
    "toDate": "2026-06-30",
    "agencyId": 32
  }
}
```

***Respuesta preview (ejemplo)***

```json
{
  "metadata": {
    "templateCode": "PACKAGES",
    "configurationName": "Mi reporte de paquetes",
    "generatedAt": "2026-06-11 10:00:00",
    "totalRows": 150
  },
  "sections": [
    {
      "key": "detail_table",
      "title": "Detalle",
      "headers": ["Número", "Tracking", "Peso"],
      "rows": [["REC-001", "1Z999...", "1.25"]]
    }
  ]
}
```

---

### *Fase 1 — Segmentación (7 segmentos)*

#### *Segmento 1.1 — Fundación de datos*

***Objetivo:** Modelo persistente y seed inicial. Sin API aún.*


| *Entregable*                    | *Archivos*                                                         |
| ------------------------------- | ------------------------------------------------------------------ |
| *Enum* `ReportTemplateCodeEnum` | `src/Enums/ReportTemplateCodeEnum.php`                             |
| *Entidades*                     | `ReportTemplate`*,* `ReportConfiguration`                          |
| *Repositories*                  | `ReportTemplateRepository`*,* `ReportConfigurationRepository`      |
| *Migración + seed*              | `PACKAGES`*,* `WAREHOUSE_RECEIPT`*,* `GUIA` *con* `default_config` |
| *SQL manual*                    | `scripts/report_builder_tables_Version20260611120000.sql`          |


***Estado:** ✅ Implementado.*

***Prompt IA sugerido:***

> *Implementa solo Fase 1 Segmento 1.1 según* `docs/report-builder/BACKEND_PHASES.md`*: entidades, enum, repos, migración y seed. Sin controllers ni services.*

***Criterios de aceptación:***

- [x] *Migración corre sin error*
- [x] *Seed inserta 3 templates con JSON válido*
- [x] *FK a* `maincompany` *correcta*

***Depende de:** —*  
***Bloquea:** 1.2, 1.3*

---

#### *Segmento 1.2 — Catálogo (solo lectura)*

***Objetivo:** Endpoints para listar y ver templates base.*


| *Entregable*               | *Detalle*                                                         |
| -------------------------- | ----------------------------------------------------------------- |
| `ReportTemplateRepository` | `listActive()` *paginado,* `findByCode()`                         |
| `ReportTemplateService`    | `listActive()`*,* `getByCode()`                                   |
| `ReportTemplateController` | `GET /api/report-templates`*,* `GET /api/report-templates/{code}` |
| *DTOs + OpenAPI*           | *List item, detail con* `defaultConfig` *y* `availableColumns`    |


***Estado:** ✅ Implementado (incluye filtros* `category` *y paginación por cursor).*

***Prompt IA sugerido:***

> *Implementa Fase 1 Segmento 1.2: ReportTemplateService + ReportTemplateController. Seguir patrón LabelFormatController. JWT + scope lectura.*

***Criterios de aceptación:***

- [x] `GET /api/report-templates` *devuelve templates del seed (paginado)*
- [x] `GET /api/report-templates/PACKAGES` *incluye schema de columnas*
- [x] `GET /api/report-templates/GUIA` *funciona*

***Depende de:** 1.1*  
***Bloquea:** 1.3*

---

#### *Segmento 1.3 — CRUD configuraciones*

***Objetivo:** Crear, editar, listar y desactivar configs por* `maincompany`*.*


| *Entregable*                         | *Detalle*                                      |
| ------------------------------------ | ---------------------------------------------- |
| `ReportConfigurationRepository`      | `searchByMaincompany()` *con filtros y cursor* |
| `ReportConfigurationService`         | *list, get, create, update, deactivate*        |
| `ReportConfigurationController`      | *GET, POST, PUT, DELETE*                       |
| `ReportConfigurationConfigValidator` | *Keys de sección/columna vs template*          |
| *DTOs*                               | *Create, Update, Search, Response*             |


***Estado:** ✅ Implementado (incluye filtros de listado y paginación por cursor).*

***Prompt IA sugerido:***

> *Implementa Fase 1 Segmento 1.3: CRUD report-configurations. Crear desde templateCode copia default_config. Aislar por maincompany del usuario JWT.*

***Criterios de aceptación:***

- [x] *POST crea config con defaults del template*
- [x] *PUT guarda overrides de columnas/secciones*
- [x] *Usuario solo ve configs de su maincompany*
- [x] *DELETE desactiva sin hard delete*
- [x] *Listado con filtros (*`templateCode`*,* `pattern`*,* `isDefault`*,* `includeInactive`*)*
- [x] *Listado paginado por cursor (*`limit`*,* `cursor`*,* `direction`*)*

***Depende de:** 1.1, 1.2*  
***Bloquea:** 1.4, 1.5*

---

#### *Segmento 1.4 — Motor de merge y contrato de render*

***Objetivo:** Unificar template + config cliente en estructura renderizable.*


| *Entregable*                  | *Detalle*                                                     |
| ----------------------------- | ------------------------------------------------------------- |
| `ReportDataProviderInterface` | `supports(code)`*,* `fetchData(filters, company)`             |
| `DynamicReportRenderService`  | *Merge config, ordenar secciones/columnas, aplicar labels*    |
| `ReportProviderRegistry`      | *Resolver provider por* `templateCode`                        |
| *Value objects / arrays*      | *Estructura:* `headers`*,* `rows`*,* `sections`*,* `metadata` |


***Prompt IA sugerido:***

> *Implementa Fase 1 Segmento 1.4: DynamicReportRenderService + interface provider + registry. Sin queries aún; usar datos mock para probar merge.*

***Criterios de aceptación:***

- [ ] *Merge respeta* `visible`*,* `order`*,* `label` *del config*
- [ ] *Secciones ocultas no aparecen en output*
- [ ] *Unit test del merge con JSON fixture*

***Depende de:** 1.3*  
***Bloquea:** 1.5, 1.6, 1.7*

---

#### *Segmento 1.5 — Provider PACKAGES + preview JSON*

***Objetivo:** Primer reporte real con endpoint preview.*


| *Entregable*                     | *Detalle*                                                    |
| -------------------------------- | ------------------------------------------------------------ |
| `PackagesReportDataProvider`     | *Reutilizar queries de* `ReceiptService` */ export packages* |
| `ReportExportService::preview()` | *Orquesta provider + render*                                 |
| *Endpoint*                       | `POST /api/report-configurations/{id}/preview`               |
| *Filtros básicos*                | `fromDate`*,* `toDate`*,* `agencyId`                         |


***Prompt IA sugerido:***

> *Implementa Fase 1 Segmento 1.5: PackagesReportDataProvider + preview endpoint. Reutilizar lógica de listado/export de packages existente.*

***Criterios de aceptación:***

- [ ] *Preview PACKAGES devuelve headers/rows según config*
- [ ] *Filtros por fecha funcionan*
- [ ] *Respuesta incluye metadata (totalRows, templateCode)*

***Depende de:** 1.4*  
***Bloquea:** 1.6*

---

#### *Segmento 1.6 — Export PDF y Excel*

***Objetivo:** Generar archivos desde la misma config que preview.*


| *Entregable*                    | *Detalle*                                                   |
| ------------------------------- | ----------------------------------------------------------- |
| `ReportExportService::export()` | *PDF vía* `PdfGeneratorService`*, Excel vía* `ExcelService` |
| *Endpoint*                      | `POST /api/report-configurations/{id}/export`               |
| *Headers HTTP*                  | `Content-Type`*,* `Content-Disposition` *correctos*         |
| *Límite filas*                  | *Configurable (ej. 10 000) para evitar timeout*             |


***Prompt IA sugerido:***

> *Implementa Fase 1 Segmento 1.6: export PDF/Excel reutilizando generateTablePdf y ExcelService. Misma estructura que preview.*

***Criterios de aceptación:***

- [ ] `format: pdf` *descarga PDF válido*
- [ ] `format: excel` *descarga XLSX válido*
- [ ] *Columnas visibles en config = columnas en export*

***Depende de:** 1.5*  
***Bloquea:** 1.7*

---

#### *Segmento 1.7 — Provider WAREHOUSE_RECEIPT + cierre Fase 1*

***Objetivo:** Segundo reporte piloto y documentación final.*


| *Entregable*                         | *Detalle*                                                   |
| ------------------------------------ | ----------------------------------------------------------- |
| `WarehouseReceiptReportDataProvider` | *Reutilizar* `WarehouseReceiptService` */ export warehouse* |
| *Seed update*                        | `default_config` *WHREC con columnas reales*                |
| *OpenAPI completo*                   | *Todos los endpoints documentados*                          |
| *Pruebas manuales*                   | *Checklist Fase 1*                                          |


***Prompt IA sugerido:***

> *Implementa Fase 1 Segmento 1.7: WarehouseReceiptReportDataProvider. Completar OpenAPI. Actualizar seed WHREC si hace falta.*

***Criterios de aceptación Fase 1 (global):***

- [x] *Cliente crea config desde template base (*`PACKAGES`*,* `WAREHOUSE_RECEIPT`*)*
- [x] *Puede ocultar/reordenar columnas vía JSON (merge en* `DynamicReportRenderService`*)*
- [x] *Preview devuelve headers + rows + metadata*
- [x] *Export PDF y Excel con misma config y filtros*
- [x] *Config aislada por* `maincompany`
- [x] `PACKAGES` *+* `WAREHOUSE_RECEIPT` *sin Jasper*
- [x] `GUIA` *permanece mock (documentado)*

***Checklist manual (QA):***

- [ ] *POST crear config* `WAREHOUSE_RECEIPT`
- [ ] *PUT ocultar columna* `status` *→ preview sin esa columna*
- [ ] *POST preview WHREC con* `fromDate`*/*`toDate`
- [ ] *POST export* `pdf` *y* `excel` *WHREC*
- [ ] *Verificar scope: otra* `maincompany` *no ve la config*
- [ ] *PACKAGES sigue funcionando tras agregar WHREC provider*
- [ ] *GUIA sigue con mock (preview mock OK)*

***Depende de:** 1.6*  
***Bloquea:** Fase 2*

---

## *Fase 2 — Documentos imprimibles (3–4 semanas)*

### *Objetivo*

***Templates de documento** (*`layout: document`*) para imprimir warehouse, paquete y guía completos — reemplazo progresivo de PDFs legacy (Jasper / impresión fija).*

*Validación robusta del JSON de configuración, duplicate/default, y motor de render documento (Twig).*

### *Alcance Fase 2 (enfoque actual)*


| *Incluido*                                                | *Fuera de alcance (release futuro)*                                                                      |
| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `WHREC_DOCUMENT`*,* `PACKAGE_DOCUMENT`*,* `GUIA_DOCUMENT` | *Nuevos templates **lista** (*`CONSOLIDATED`*,* `SHIPMENT`*,* `LOAD_UNIT`*,* `AWB_HAWB`*,* `MANIFEST`*)* |
| *Validación schema (2.1), duplicate/default (2.2)*        | *Filtros avanzados por template lista (2.5 deferido)*                                                    |
| *Preview + export PDF por documento*                      | *Expansión del catálogo de reportes tabulares*                                                           |


***Fase 1** entregó el motor base y tres templates **lista** (*`PACKAGES`*,* `WAREHOUSE_RECEIPT`*,* `GUIA`*) como MVP — se mantienen sin regresión, pero **no se amplían** en esta fase.*

### *Familias de template*


| `layout`   | *Uso*                          | *Estado en roadmap*                             |
| ---------- | ------------------------------ | ----------------------------------------------- |
| `list`     | *Muchos registros en tabla*    | *Fase 1 ✅ — sin expansión hasta release futuro* |
| `document` | *Un solo documento imprimible* | ***Fase 2** — foco actual*                      |


### *Documentos imprimibles — tres variantes (referencia PDF)*

*Comparten base (empresa, party, tablas). Cada tipo añade secciones propias:*


| *Template*         | *Sección distintiva*                                                                                      |
| ------------------ | --------------------------------------------------------------------------------------------------------- |
| `PACKAGE_DOCUMENT` | `items_table` *(líneas del paquete)*                                                                      |
| `WHREC_DOCUMENT`   | `packages_table` *(paquetes del WH + totales) + términos/firmas*                                          |
| `GUIA_DOCUMENT`    | `packages_table` *(Package(s)) +* `**rate_details*`* *(tarifa) +* `**charges_summary*`* *(totales envío)* |


`**PACKAGE_DOCUMENT*`* *— un paquete/receipt (ej.* `WR3010XP1`*):*

```
┌─────────────────────────────────────────────────────────────┐
│ HEADER (2 cols): Empresa │ Nº paquete, fecha, recibido por │
├─────────────────────────────────────────────────────────────┤
│ PARTY: SHIPPER │ CONSIGNEE                                 │
├─────────────────────────────────────────────────────────────┤
│ NOTE (texto libre)                                            │
├─────────────────────────────────────────────────────────────┤
│ ITEMS (tabla): líneas del paquete (descripción, peso, dims) │
├─────────────────────────────────────────────────────────────┤
│ FOOTER: firma / página                                        │
└─────────────────────────────────────────────────────────────┘
```

*Filtro preview/export:* `**receiptId*`* *(required).*

`**WHREC_DOCUMENT*`* *— warehouse receipt completo (ej.* `Warehouse WR3010`*):*

```
┌─────────────────────────────────────────────────────────────┐
│ HEADER: Empresa + datos WH (fecha, processed by, type,     │
│         agent, instructions)                                 │
├─────────────────────────────────────────────────────────────┤
│ PARTY: SHIPPER │ CONSIGNEE (+ email)                        │
├─────────────────────────────────────────────────────────────┤
│ NOTE (ej. "Created from OCR label scan…")                    │
├─────────────────────────────────────────────────────────────┤
│ PACKAGES (tabla multi-fila):                                 │
│   Nº | Descripción | Dims | Tracking | Consol | Value |     │
│   Weight | Volume | WVol | WMax | Units                     │
│   WR3010XP1, WR3010XP2, … + fila TOTALES                     │
├─────────────────────────────────────────────────────────────┤
│ TERMS (términos y condiciones)                               │
├─────────────────────────────────────────────────────────────┤
│ SIGNATURES: Firma 1 │ Firma 2                                │
└─────────────────────────────────────────────────────────────┘
```

*Filtro preview/export:* `**whrecId*`* *(required).*

`**GUIA_DOCUMENT*`* *— guía/shipment completa (ej.* `SHIPMENT GUI1320132124`*):*

```
┌─────────────────────────────────────────────────────────────┐
│ HEADER: Empresa + datos guía (fecha, shipping type,        │
│         processed by) + título SHIPMENT + número guía        │
├─────────────────────────────────────────────────────────────┤
│ PARTY: SHIPPER │ CONSIGNEE                                 │
├─────────────────────────────────────────────────────────────┤
│ PACKAGE(S) (tabla): Unit | Id | Value | L×W×H | Desc |     │
│   Lb/Kg | Volume | PVol + fila Total                         │
├─────────────────────────────────────────────────────────────┤
│ RATE DETAILS — Shipment Rate Details {número guía}:        │
│   Tarifa asociada: Name | Unit | Qty | Price ($) | Total   │
│   + Total services | Total Shipment $                        │
├─────────────────────────────────────────────────────────────┤
│ CHARGES SUMMARY — Shipment's Basic Details:                │
│   Insured Value, Declared Value, COD, Insurance paid,        │
│   Other fees, Commissions, Tax paid, Total shipment,       │
│   Discount %                                                 │
└─────────────────────────────────────────────────────────────┘
```

*Filtro preview/export:* `**guideId*`* *(required).*


| *Aspecto*          | `PACKAGE_DOCUMENT`                  | `WHREC_DOCUMENT`            | `GUIA_DOCUMENT`                              |
| ------------------ | ----------------------------------- | --------------------------- | -------------------------------------------- |
| *Entidad*          | `Receipt`                           | `WHrec`                     | `Guide` *(guía/shipment)*                    |
| *Filtro*           | `receiptId`                         | `whrecId`                   | `guideId`                                    |
| *Header doc*       | *Nº paquete, barcode, recibido por* | *processed by, type, agent* | *shipping type, processed by*                |
| *Tabla paquetes*   | *—*                                 | `packages_table` *(WH)*     | `packages_table` *(Package(s))*              |
| *Tarifa / totales* | *—*                                 | *—*                         | `**rate_details*`* *+* `**charges_summary*`* |
| *Footer*           | *Firma simple*                      | *Términos + 2 firmas*       | *— (PDF ref. sin firmas)*                    |
| *Referencia PDF*   | `WR3010XP1`                         | `Warehouse WR3010`          | `SHIPMENT GUI1320132124`                     |
| *Fuente datos*     | `ReceiptService`                    | `WarehouseReceiptService`   | `GuideService` *+* `TariffService`           |


### *Templates Fase 2 (documentos)*

- `WHREC_DOCUMENT` *— warehouse + **PACKAGES** + totales (prioridad negocio)*
- `PACKAGE_DOCUMENT` *— paquete individual (reutiliza motor de 2.7)*
- `GUIA_DOCUMENT` *— guía/shipment + Package(s) + **tarifa** + **totales** (reutiliza motor de 2.7)*

### *Estado de implementación (backend Fase 2)*


| *Segmento*                             | *Estado*  | *Notas*                                                                  |
| -------------------------------------- | --------- | ------------------------------------------------------------------------ |
| *2.1 Validación schema JSON*           | *✅ Hecho* | *List + document (sections, columns, fields, parties, blocks)*           |
| *2.2 Duplicate y default*              | *✅ Hecho* | `POST .../duplicate`*,* `PATCH .../set-default`                          |
| *2.7 Motor documento + WHREC_DOCUMENT* | *✅ Hecho* | `DocumentReportRenderService`*,* `WhrecDocumentDataProvider`*, Twig PDF* |
| *2.8 PACKAGE_DOCUMENT*                 | *✅ Hecho* | `ReceiptDocumentDataProvider`                                            |
| *2.9 GUIA_DOCUMENT*                    | *✅ Hecho* | `GuideDocumentDataProvider` *+ rate/charges sections*                    |
| *2.6 Cierre Fase 2*                    | *✅ Hecho* | *Unit tests ReportBuilder (37 tests)*                                    |


### *Orden recomendado Fase 2*

```
2.1 Validación schema (documentos)
  └── 2.7 Motor documento + WHREC_DOCUMENT (whrecId, PACKAGES)
        ├── 2.8 PACKAGE_DOCUMENT (receiptId)
        └── 2.9 GUIA_DOCUMENT (guideId, tarifa + totales)
  └── 2.2 Duplicate / default (paralelo tras 2.1)
        └── 2.6 Cierre (2.1, 2.2, 2.7, 2.8, 2.9)
```

***Próximo segmento sugerido:** **2.1** → **2.7** (WHRec) → **2.8** (paquete) → **2.9** (guía + tarifa) → **2.6**.*

---

### *Fase 2 — Segmentación (6 segmentos)*

#### *Segmento 2.1 — Validación de schema JSON (documentos)*

***Objetivo:** Rechazar configs inválidas al guardar — secciones/campos/columnas de templates* `layout: document` *(y compatibilidad con configs lista Fase 1).*


| *Entregable*            | *Detalle*                                        |
| ----------------------- | ------------------------------------------------ |
| `ReportConfigValidator` | *Valida keys de sección/columna contra template* |
| *DTO constraints*       | *Symfony Validator custom*                       |
| *Mensajes i18n*         | *Errores claros por campo*                       |


***Criterios:** PUT/POST con columna inexistente → 400 con mensaje específico.*

***Depende de:** Fase 1 completa*  
***Bloquea:** 2.2+, 2.7, 2.8, 2.9*

---

#### *Segmento 2.2 — Duplicate y default config*

***Objetivo:** Operaciones de gestión de configuraciones.*


| *Entregable*                                     | *Detalle*                                                 |
| ------------------------------------------------ | --------------------------------------------------------- |
| `POST /api/report-configurations/{id}/duplicate` | *Clona config con nuevo nombre*                           |
| `PATCH .../set-default`                          | *Marca* `is_default`*, desmarca otras del mismo template* |
| *Lógica*                                         | *Solo una default por template + maincompany*             |


***Depende de:** 2.1*

---

#### *Segmento 2.7 — Motor documento + WHREC_DOCUMENT*

***Objetivo:** Introducir* `layout: "document"` *y el template **warehouse completo** con sección **PACKAGES** (tabla de paquetes hijos + totales).*

***Referencia de diseño:** PDF* `Warehouse WR3010` *— header WH, party, nota, tabla PACKAGES multi-fila, términos, doble firma.*


| *Entregable*       | *Detalle*                                                                                                                                           |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| *Motor render*     | `DynamicReportRenderService` *v2: ramifica* `layout: list` *vs* `document`                                                                          |
| *Tipos de sección* | `grid`*,* `party`*,* `text`*,* `table`*,* `**packages_table`***,* `terms`*,* `signatures` *(base; 2.9 añade* `rate_details`*,* `charges_summary`*)* |
| *Enum + seed*      | `WHREC_DOCUMENT` *en* `ReportTemplateCodeEnum` *+ migración*                                                                                        |
| *Validator v2*     | *Extiende 2.1:* `fields[]`*,* `blocks[]`*,* `parties[]`*, columnas de* `packages_table`                                                             |
| *Provider*         | `WhrecDocumentDataProvider` *— filtro* `**whrecId*`* *(required); carga WHrec + receipts hijos*                                                     |
| *Preview JSON*     | *Secciones tipadas;* `packages_table`*:* `headers`*,* `rows`*,* `totalsRow`                                                                         |
| *PDF*              | *Twig* `whrec_document.html.twig` *(piloto; generalizar en 3.4)*                                                                                    |
| *Export*           | *Mismo Twig vía* `ReportExportService::export()`                                                                                                    |
| *Filtros*          | `whrecId` *en* `ReportPreviewFiltersDTO`                                                                                                            |


***Columnas* `packages_table` *(seed inicial, alineadas al PDF de referencia):***

`number`*,* `description`*,* `dimensionsIn`*,* `dimensionsCm`*,* `tracking`*,* `consol`*,* `value`*,* `weightLbKg`*,* `volumeCfMt3`*,* `wvol`*,* `wmax`*,* `units`

***Estructura* `default_config` *WHREC (resumen):***

```json
{
  "layout": "document",
  "branding": { "showLogo": true, "showCompanyName": true, "headerTitle": "Warehouse Receipt" },
  "sections": [
    { "key": "header_section", "type": "grid", "blocks": [ "company_block", "whrec_meta_block" ] },
    { "key": "party_section", "type": "party", "parties": [ "shipper", "consignee" ] },
    { "key": "note_section", "type": "text" },
    { "key": "packages_section", "type": "packages_table", "title": "PACKAGES", "columns": [ "number", "description", "..." ], "showTotalsRow": true },
    { "key": "terms_section", "type": "terms" },
    { "key": "signatures_section", "type": "signatures", "fields": [ "signature1", "signature2" ] }
  ]
}
```

***Header* `whrec_meta_block` *(campos distintos al paquete):*** `processedDate`*,* `processedBy`*,* `instructions`*,* `type`*,* `agent`*.*

***Prompt IA sugerido:***

> *Implementa Fase 2 Segmento 2.7: motor layout document + WHREC_DOCUMENT con packages_table, provider whrecId, Twig PDF referencia Warehouse WR3010.*

***Criterios de aceptación:***

- [ ] *Preview/export con* `whrecId` *devuelve header + party + **PACKAGES** (N filas) + totales*
- [ ] *PDF se aproxima al documento warehouse de referencia*
- [ ] *Ocultar columna en* `packages_table` *→ no aparece en PDF*
- [ ] *Templates lista Fase 1 sin regresión (mantenimiento; sin expansión)*
- [ ] *Unit tests provider + render document*

***Depende de:** 2.1*  
***Bloquea:** 2.8, 2.6*

---

#### *Segmento 2.8 — PACKAGE_DOCUMENT*

***Objetivo:** Documento de **un solo paquete** — misma base que 2.7 pero sin sección PACKAGES; tabla* `items_table` *con líneas del receipt.*

***Referencia de diseño:** PDF* `WR3010XP1` *— header paquete, party, nota, items (medidas/peso), firma simple.*


| *Entregable*     | *Detalle*                                                                                         |
| ---------------- | ------------------------------------------------------------------------------------------------- |
| *Enum + seed*    | `PACKAGE_DOCUMENT` *+ migración*                                                                  |
| *Provider*       | `ReceiptDocumentDataProvider` *— filtro* `**receiptId`** *(required)*                             |
| *Secciones*      | `header_section` *(grid: empresa | meta paquete), party, note,* `items_table`*,* `footer_section` |
| *Header paquete* | `documentNumber`*,* `receivedDate`*,* `receivedBy`*,* `barcode` *(sin processed by/type/agent)*   |
| *PDF*            | *Twig* `package_document.html.twig` *o partial compartido con WHREC*                              |
| *Reutiliza*      | *Motor render, validator y export de **2.7***                                                     |


***Estructura* `items_table` *(columnas seed):***

`itemNumber`*,* `description`*,* `weightLb`*,* `weightKg`*,* `pvol`*,* `dimensions`*,* `volumeCf`

***Prompt IA sugerido:***

> *Implementa Fase 2 Segmento 2.8: PACKAGE_DOCUMENT reutilizando motor 2.7, provider receiptId, PDF referencia WR3010XP1.*

***Criterios de aceptación:***

- [ ] *Preview/export con* `receiptId` *devuelve documento de un paquete*
- [ ] *No incluye sección PACKAGES (solo aplica a WHREC)*
- [ ] *Config editable (campos/columnas visibles) persiste en PUT*
- [ ] *WHREC_DOCUMENT de 2.7 sigue funcionando*

***Depende de:** 2.7*  
***Bloquea:** 2.9, 2.6*

---

#### *Segmento 2.9 — GUIA_DOCUMENT (guía + tarifa + totales)*

***Objetivo:** Documento de **guía/shipment** — como WHRec incluye tabla de paquetes, más secciones de **tarifa asociada** y **resumen de cargos/totales** del envío.*

***Referencia de diseño:** PDF* `SHIPMENT GUI1320132124`*.*


| *Entregable*                        | *Detalle*                                                                                                                                                                                                                |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| *Enum + seed*                       | `GUIA_DOCUMENT` *+ migración (distinto del template lista* `GUIA` *Fase 1)*                                                                                                                                              |
| *Provider*                          | `GuideDocumentDataProvider` *— filtro* `**guideId*`* *(required)*                                                                                                                                                        |
| *Datos*                             | `GuideService` *+ paquetes/units de la guía + líneas tarifa (*`TariffService` */ datos legacy de calculate)*                                                                                                             |
| *Sección* `packages_section`        | `type: packages_table`*, título "Package(s)"; columnas:* `unit`*,* `packageId`*,* `value`*,* `length`*,* `width`*,* `height`*,* `description`*,* `weightLbKg`*,* `volume`*,* `pvol` *+ fila total*                       |
| *Sección* `rate_details_section`    | `type: rate_details`*, título "Shipment Rate Details {guideNumber}"; tabla:* `tariffName`*,* `unit`*,* `quantity`*,* `unitPrice`*,* `lineTotal`*; subtotales:* `totalServices`*,* `totalShipment`                        |
| *Sección* `charges_summary_section` | `type: charges_summary`*, título "Shipment's Basic Details"; campos:* `insuredValue`*,* `declaredValue`*,* `cod`*,* `insurancePaid`*,* `otherFees`*,* `commissions`*,* `taxPaid`*,* `totalShipment`*,* `discountPercent` |
| *PDF*                               | *Twig* `guia_document.html.twig`                                                                                                                                                                                         |
| *Reutiliza*                         | *Motor render y validator de **2.7**; reemplaza mock* `GUIA` *en preview document*                                                                                                                                       |


***Estructura* `default_config` *GUIA (resumen):***

```json
{
  "layout": "document",
  "branding": { "showLogo": true, "showCompanyName": true, "headerTitle": "Shipment" },
  "sections": [
    { "key": "header_section", "type": "grid", "blocks": [ "company_block", "guide_meta_block" ] },
    { "key": "party_section", "type": "party", "parties": [ "shipper", "consignee" ] },
    { "key": "packages_section", "type": "packages_table", "title": "Package(s)", "showTotalsRow": true },
    { "key": "rate_details_section", "type": "rate_details", "title": "Shipment Rate Details" },
    { "key": "charges_summary_section", "type": "charges_summary", "title": "Shipment's Basic Details" }
  ]
}
```

***Header* `guide_meta_block`*:*** `guideNumber`*,* `shipmentDate`*,* `shippingType`*,* `processedBy`*.*

***Preview JSON (secciones nuevas):***

- `rate_details`*:* `{ title, columns[], rows[][], subtotals: { totalServices, totalShipment } }`
- `charges_summary`*:* `{ title, fields: [{ key, label, value }] }`

***Prompt IA sugerido:***

> *Implementa Fase 2 Segmento 2.9: GUIA_DOCUMENT con packages_table, rate_details y charges_summary, provider guideId, PDF referencia SHIPMENT GUI1320132124.*

***Criterios de aceptación:***

- [ ] *Preview/export con* `guideId` *incluye Package(s) + tarifa + totales*
- [ ] *Tarifa mostrada coincide con tarifa asociada a la guía (nombre, qty, price)*
- [ ] *Campos de totales (declared value, insurance, tax, total shipment) configurables visible/label*
- [ ] *Template lista* `GUIA` *Fase 1 y documentos WHREC/PACKAGE sin regresión*
- [ ] `MockReportDataProvider` *deja de usarse para* `GUIA_DOCUMENT`

***Fuera de alcance 2.9:***

- *Recalcular tarifa en tiempo real (usar valores persistidos en guía; preview tarifa existente* `/api/tariff/insurance-preview` *es referencia aparte)*
- *INVOICE/BILL document — **release futuro** (tras refinamiento WHREC / PACKAGE / GUIA)*

***Depende de:** 2.7*  
***Bloquea:** 2.6*

---

#### *Segmento 2.6 — Cierre Fase 2*

***Objetivo:** Integración y QA.*


| *Entregable*        | *Detalle*                                                                            |
| ------------------- | ------------------------------------------------------------------------------------ |
| *Tests integración* | *Preview + export por cada template nuevo*                                           |
| *Actualizar seed*   | *3 templates document (*`WHREC_DOCUMENT`*,* `PACKAGE_DOCUMENT`*,* `GUIA_DOCUMENT`*)* |
| *Doc API*           | *Filtros documento (*`whrecId`*,* `receiptId`*,* `guideId`*) en OpenAPI*             |


***Criterios Fase 2 (global):***

- [ ] `WHREC_DOCUMENT` *+* `PACKAGE_DOCUMENT` *+* `GUIA_DOCUMENT` *en catálogo con preview/export PDF*
- [ ] *Filtros documentados (*`whrecId` *vs* `receiptId` *vs* `guideId`*)*
- [ ] *Validación rechaza secciones/campos/columnas inválidos en configs documento*
- [ ] *Duplicate y set-default funcionan*
- [ ] *WHRec: PACKAGES + totales*
- [ ] *Paquete: sin PACKAGES*
- [ ] *Guía: Package(s) + rate details + charges summary*
- [ ] *Templates lista Fase 1 siguen operativos (sin nuevas features lista)*

***Depende de:** 2.1, 2.2, 2.7, 2.8, 2.9*  
***Bloquea:** Fase 3*

---

## *Release futuro — Extensiones de reportes lista (deferido)*

*Segmentos **retirados del plan activo** hasta un release posterior. La Fase 1 ya cubre listas piloto (*`PACKAGES`*,* `WAREHOUSE_RECEIPT`*,* `GUIA`*).*


| *Segmento (referencia)* | *Contenido deferido*                                                  |
| ----------------------- | --------------------------------------------------------------------- |
| *2.3*                   | *Providers lista lote 1:* `CONSOLIDATED`*,* `SHIPMENT`*,* `LOAD_UNIT` |
| *2.4*                   | *Providers lista lote 2:* `AWB_HAWB`*,* `MANIFEST`                    |
| *2.5*                   | `availableFilters` *por template lista; validación filtros dinámicos* |


***Cuándo retomar:** Tras cerrar Fase 2 (documentos) y según prioridad de negocio para exportaciones tabulares masivas.*

---

## *Fase 3 — Refinamiento documentos WHREC / PACKAGE / GUIA (2–3 semanas)*

### *Objetivo*

*Perfeccionar los **tres documentos entregados en Fase 2** — paridad preview/PDF, branding, datos de providers/formatters, estilos Twig, y QA contra PDFs de referencia.*

***En alcance Fase 3:** solo* `WHREC_DOCUMENT`*,* `PACKAGE_DOCUMENT`*,* `GUIA_DOCUMENT`*.*

***Fuera de alcance Fase 3:** nuevos templates documento (*`INVOICE_DOCUMENT`*,* `BILL_DOCUMENT`*, CONSOL doc, etc.) → ver **Release futuro — Nuevos documentos** al final de esta sección.*

### *Referencias PDF (criterio de aceptación)*


| *Template*         | *Referencia*             |
| ------------------ | ------------------------ |
| `WHREC_DOCUMENT`   | *Warehouse WR3010*       |
| `PACKAGE_DOCUMENT` | *WR3010XP1*              |
| `GUIA_DOCUMENT`    | *SHIPMENT GUI1320132124* |


### *Estado de implementación (backend Fase 3)*


| *Segmento*                  | *Estado*  | *Notas*                                                                                                               |
| --------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------- |
| *3.1 Branding schema + PDF* | *✅ Hecho* | `primaryColor`*,* `footerText`*,* `locale`*,* `showPageNumbers`*;* `ReportDocumentBrandingResolver`*; validación hex* |
| *3.2 Paridad datos/PDF*     | *✅ Hecho* | *Formatter:* `labelMetaBlock`*,* `dimensionsCm`*, totales con decimales empresa; providers WHREC/GUIA totales lb/kg*  |
| *3.3 Condicionales/totales* | *✅ Hecho* | `visibleWhen`*, secciones vacías omitidas,* `showTotalsRow`*, subtotals etiquetados*                                  |
| *3.4 PDF polish*            | *✅ Hecho* | *Logo local,* `primaryColor` *en Twig, footer + page numbers en* `ReportDocumentPdfService`                           |
| *3.5 Cache preview + QA*    | *✅ Hecho* | `ReportPreviewCacheService` *(TTL 120s); OpenAPI branding/filtros documento; **45 tests** unitarios*                  |


***Fase 3 backend:** ✅ Completa (2026-06-12).*

***Archivos principales (Fase 3):***


| *Área*          | *Rutas*                                                                                                                                 |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| *Branding*      | `ReportDocumentBrandingResolver.php`*,* `ReportConfigurationConfigValidator.php` *(branding values)*                                    |
| *Cache preview* | `ReportPreviewCacheService.php`*, wiring en* `ReportExportService.php`                                                                  |
| *Render/PDF*    | `DocumentReportRenderService.php`*,* `ReportDocumentFormatter.php`*,* `ReportDocumentPdfService.php`*,* `document.html.twig`            |
| *Tests*         | `ReportDocumentPhase3Test.php`*, ampliaciones en* `DocumentReportRenderServiceTest.php`*,* `ReportConfigurationConfigValidatorTest.php` |


---

### *Fase 3 — Segmentación (5 segmentos)*

#### *Segmento 3.1 — Extensión schema branding*

***Objetivo:** Más opciones en JSON* `config.branding` *y aplicarlas en preview/PDF de los **tres** documentos.*


| *Campo nuevo*                  | *Uso*                 |
| ------------------------------ | --------------------- |
| `primaryColor`*,* `footerText` | *PDF header/footer*   |
| `locale`                       | *Labels multi-idioma* |
| `showPageNumbers`              | *PDF*                 |


***Entregable:** Backward compatible con configs Fase 1–2; Twig* `document.html.twig` *respeta branding.*

***Depende de:** Fase 2 completa*

---

#### *Segmento 3.2 — Paridad datos y PDF (providers + formatter)*

***Objetivo:** Ajustar backend para que preview y export PDF de los **tres** templates coincidan con las referencias.*


| *Área*                        | *Detalle*                                           |
| ----------------------------- | --------------------------------------------------- |
| `WhrecDocumentDataProvider`   | *Campos, totales PACKAGES, party formatting*        |
| `ReceiptDocumentDataProvider` | *items_table, medidas, pesos*                       |
| `GuideDocumentDataProvider`   | *rate_details, charges_summary, packages*           |
| `ReportDocumentFormatter`     | *Moneda, peso, fechas, multilínea*                  |
| *Twig*                        | *Layout header/party/tablas alineado a referencias* |


***Entregable:** Tests regresión por template; smoke* `whrecId` */* `receiptId` */* `guideId`*.*

***Depende de:** 3.1*

---

#### *Segmento 3.3 — Secciones condicionales y totales (refinamiento)*

***Objetivo:** Lógica de render avanzada **solo donde los tres documentos lo necesiten**.*


| *Entregable*                  | *Detalle*                      |
| ----------------------------- | ------------------------------ |
| `visibleWhen` *en sección*    | *Ej. ocultar charges si vacío* |
| `totals` *en sección tabla*   | *subtotal, sum columns*        |
| `DocumentReportRenderService` | *Evaluación condiciones*       |


***Depende de:** 3.2*

---

#### *Segmento 3.4 — PDF polish (logo, estilos unificados)*

***Objetivo:** Pulir Twig compartido para los tres documentos — no generalizar a templates futuros aún.*


| *Entregable*         | *Detalle*                                                            |
| -------------------- | -------------------------------------------------------------------- |
| *Logo empresa*       | *Si* `branding.showLogo` *y maincompany tiene logo*                  |
| *Estilos unificados* | *Tipografía, márgenes, tablas consistentes entre WHREC/PACKAGE/GUIA* |
| `showPageNumbers`    | *Footer PDF cuando configurado*                                      |


***Nota:** Generalización Twig para INVOICE/BILL u otros tipos → **release futuro**.*

***Depende de:** 3.1, 3.2*

---

#### *Segmento 3.5 — Cache preview + cierre Fase 3*


| *Entregable*           | *Detalle*                                 |
| ---------------------- | ----------------------------------------- |
| *Cache opcional Redis* | *Key: configId + filters hash, TTL corto* |
| *QA los 3 documentos*  | *Preview + PDF vs referencias*            |
| *OpenAPI*              | *Branding extendido documentado*          |


***Criterios Fase 3 (global):***

- [x] *WHREC, PACKAGE y GUIA: preview y PDF **alineados** con referencias (backend render + Twig)*
- [x] *Branding (*`primaryColor`*, footer, page numbers) visible en PDF*
- [x] *Sin regresión Fase 1 listas ni Fase 2 CRUD/editores (45 tests ReportBuilder OK)*
- [x] ***No** se agregan templates documento nuevos en esta fase*

***Depende de:** 3.1–3.4*  
***Bloquea:** Fase 4*

---

### *Release futuro — Nuevos documentos (deferido)*


| *Contenido deferido*                  | *Notas*                                                                 |
| ------------------------------------- | ----------------------------------------------------------------------- |
| `INVOICE_DOCUMENT`*,* `BILL_DOCUMENT` | *Providers* `InvoiceDocumentDataProvider`*,* `BillDocumentDataProvider` |
| *Twig genérico multi-template*        | `dynamic_report.html.twig`*,* `ReportPdfSectionRenderer`                |
| *Filtros* `invoiceId`*,* `billId`     | *Tras validar WHREC / PACKAGE / GUIA en producción*                     |


***Cuándo retomar:** Tras cerrar Fase 3 (refinamiento) y sign-off de negocio sobre los tres documentos piloto.*

---

## *Fase 4 — Integración y migración Jasper (ongoing)*

### *Objetivo*

*Convivencia con Jasper; migración gradual sin romper reportes legacy.*

***No incluye:** apagar Jasper globalmente.*

---

### *Fase 4 — Segmentación (4 segmentos)*

#### *Segmento 4.1 — Flag engine por template* ✅ Hecho (2026-06-17)


| *Entregable*                            | *Detalle*                                                                   |
| --------------------------------------- | --------------------------------------------------------------------------- |
| *Campo* `engine` *en* `report_template` | `native` \| `jasper`; DEFAULT `native`; migración `Version20260617120000`   |
| `ReportEngineEnum`                      | `src/Enums/ReportEngineEnum.php`                                            |
| `ReportTemplate::getEngine()`           | Propiedad + getter/setter                                                   |
| `ReportExportService`                   | `assertNativeEngine()` en `preview()` y `export()`; jasper lanza LogicError |
| *Seed update*                           | *Templates legacy marcados* `jasper` *(Segmento 4.2)*                      |


***Depende de:** Fase 3 completa*

---

#### *Segmento 4.2 — Proxy Jasper en export*


| *Entregable*                | *Detalle*                                                                |
| --------------------------- | ------------------------------------------------------------------------ |
| `JasperReportExportAdapter` | *Delega a* `ReportService` *existente*                                   |
| *Mapeo*                     | `templateCode` *→ tipo Jasper (*`Receipt`*,* `WarehouseReceipt`*, etc.)* |
| *Respuesta unificada*       | *Mismo endpoint export para native y jasper*                             |


***Depende de:** 4.1*

---

#### *Segmento 4.3 — Guía de migración y herramientas*


| *Entregable*                              | *Detalle*                                       |
| ----------------------------------------- | ----------------------------------------------- |
| `docs/report-builder/JASPER_MIGRATION.md` | *Paso a paso por tipo de reporte*               |
| *Script/command opcional*                 | *Comparar output Jasper vs native (smoke test)* |
| *Checklist por cliente*                   | *Qué templates migrar primero*                  |


***Depende de:** 4.2*

---

#### *Segmento 4.4 — Observabilidad y cierre*


| *Entregable*       | *Detalle*                                        |
| ------------------ | ------------------------------------------------ |
| *Logging*          | `templateCode`*,* `engine`*, duration, rowCount* |
| *Métricas*         | *Errores export, timeout*                        |
| *Deprecation path* | *Templates jasper → native por releases*         |


***Criterios Fase 4 (global):***

- [ ] *Export funciona con* `engine: native` *y* `engine: jasper`
- [ ] *Sin regresión en reportes Jasper actuales*
- [ ] *Guía de migración publicada*

***Depende de:** 4.1–4.3*

---

## *Riesgos backend*


| *Riesgo*                         | *Mitigación*                                                        |
| -------------------------------- | ------------------------------------------------------------------- |
| *Config JSON sin validación*     | *Segmento 2.1 + Validator desde Fase 1 básico*                      |
| *Providers duplican queries*     | *Reutilizar repos de listados/export existentes*                    |
| *PDF complejo (no tabular)*      | *Fase 2 Twig documento; F3.4 polish; nuevos tipos → release futuro* |
| *Performance en exports grandes* | *Límite filas (1.6); cache (3.5); async futuro*                     |
| *IA genera fase entera*          | *Seguir segmentos de este doc*                                      |


---

## *Archivos base (referencia Fase 1)*

```
src/Entity/ReportTemplate.php
src/Entity/ReportConfiguration.php
src/Enums/ReportTemplateCodeEnum.php
src/Repository/ReportTemplateRepository.php
src/Repository/ReportConfigurationRepository.php
src/Service/Report/ReportTemplateService.php
src/Service/Report/ReportConfigurationService.php
src/Service/Report/DynamicReportRenderService.php
src/Service/Report/ReportExportService.php
src/Service/Report/ReportProviderRegistry.php
src/Service/Report/Provider/ReportDataProviderInterface.php
src/Service/Report/Provider/PackagesReportDataProvider.php
src/Service/Report/Provider/WarehouseReceiptReportDataProvider.php
src/Controller/ReportTemplateController.php
src/Controller/ReportConfigurationController.php
src/ValidationDTO/ValidationReport/...
migrations/VersionXXXXXXXX.php
```

---

## *Cronograma sugerido (con IA segmentada)*


| *Fase*   | *Segmentos*                                       | *Duración estimada* |
| -------- | ------------------------------------------------- | ------------------- |
| *Fase 1* | *1.1 – 1.7*                                       | *1–2 semanas*       |
| *Fase 2* | *2.1, 2.2, 2.7–2.9 (2.6 cierre)*                  | *3–4 semanas*       |
| *Fase 3* | *3.1 – 3.5 (refinamiento WHREC / PACKAGE / GUIA)* | *2–3 semanas*       |
| *Fase 4* | *4.1 – 4.4*                                       | *ongoing*           |


*Cada segmento: implementar → probar → PR → merge → siguiente segmento.*

---

## *Retomar trabajo (checkpoint 2026-06-17)*


| *Área*              | *Estado*     | *Siguiente paso*                                    |
| ------------------- | ------------ | --------------------------------------------------- |
| *Backend Fase 1*    | *✅ Completa* | *—*                                                 |
| *Front Fase 1*      | *✅ Completa* | *—*                                                 |
| *Backend Fase 2*    | *✅ Completa* | *—*                                                 |
| *Front Fase 2*      | *✅ Completa* | ***F3** refinamiento preview/branding (repo front)* |
| *Backend Fase 3*    | *✅ Completa* | *—*                                                 |
| *Backend F4 — 4.1* | *✅ Completa* | ***4.2** JasperReportExportAdapter*                 |


***Decisiones de producto registradas:***

1. ***Foco actual: documentos** — Fase 2 solo entrega* `layout: document` *(WHRec, paquete, guía).*
2. ***Extensiones de lista deferidas** —* `CONSOLIDATED`*,* `SHIPMENT`*,* `LOAD_UNIT`*,* `AWB_HAWB`*,* `MANIFEST` *y filtros avanzados lista → release futuro (ver sección deferida).*
3. *Fase 1 listas (*`PACKAGES`*,* `WAREHOUSE_RECEIPT`*,* `GUIA`*) permanecen como MVP; sin expansión en este ciclo.*
4. ***Tres templates documento Fase 2:***
  - `WHREC_DOCUMENT` *— warehouse + PACKAGES + totales + términos/firmas*
  - `PACKAGE_DOCUMENT` *— paquete individual (*`items_table`*)*
  - `GUIA_DOCUMENT` *— guía/shipment + Package(s) + **rate_details** + **charges_summary***
5. *Referencias PDF: warehouse* `Warehouse WR3010`*; paquete* `WR3010XP1`*; guía* `SHIPMENT GUI1320132124`*.*
6. ***Fase 3 = refinamiento** de WHREC / PACKAGE / GUIA (preview ≈ PDF, branding, QA). **No** nuevos documentos hasta sign-off.*
7. *INVOICE/BILL y Twig genérico multi-template → **release futuro** (ver Fase 3 deferido).*

