# Reporte: Refactorización Sección 16 - BaseSectionService + Laravel-Data (2026-05-24)

Refactorización completa de la Sección 16 (Componente Tecnologico) desde `lorisleiva/laravel-actions` hacia el patrón `BaseSectionService` + `spatie/laravel-data` DTOs, dividiendo la sección monolítica en 4 sub-secciones independientes.

---

## 1. Resumen de Cambios

| Aspecto        | Antes                                                                         | Después                                                        |
| -------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------- |
| **Patrón**     | `lorisleiva/laravel-actions` (SaveSeccion16Action, SaveSeccion161FilasAction) | `BaseSectionService` + `SectionController`                     |
| **DTOs**       | Ninguno (arrays crudos)                                                       | `spatie/laravel-data` (4 DTOs tipados)                         |
| **Rutas**      | 2 rutas directas a Actions                                                    | 4 rutas via `SectionController` con `defaults('relation')`     |
| **Relaciones** | `filas161()`, `itemValores()`                                                 | `seccion161()`, `seccion162()`, `seccion163()`, `seccion164()` |
| **Servicios**  | 0 servicios                                                                   | 4 servicios independientes                                     |
| **Tests**      | 2 archivos (49 tests)                                                         | 4 archivos (41 tests)                                          |

---

## 2. Archivos Creados (10)

### DTOs (`app/Data/Inventario/`)

| Archivo              | Campos                                                                   | Validación               |
| -------------------- | ------------------------------------------------------------------------ | ------------------------ |
| `Seccion161Data.php` | `elementos` (array de `Elemento161Data`)                                 | `In`, `Max`              |
| `Seccion162Data.php` | `descripcion_volumetrica`, `vanos_abiertos`, `crujias_config`, `archivo` | `Integer`, `Min`, `File` |
| `Seccion163Data.php` | `espacios_complementarios`                                               | —                        |
| `Seccion164Data.php` | `item_valores` (array de `ItemValorData`)                                | `Integer`, `Min`         |

### Servicios (`app/Services/Inventario/`)

| Archivo                 | Relación                                     | Lógica clave                                                            |
| ----------------------- | -------------------------------------------- | ----------------------------------------------------------------------- |
| `Seccion161Service.php` | `seccion161` (HasMany → Inventario16Fila)    | Upsert filas, conversión `sist_const`, soft-delete restore, espejo 17.1 |
| `Seccion162Service.php` | `seccion162` (HasOne → InvSeccion16)         | Media `esquema_ubicacion`, `crujias_config` con validación unique       |
| `Seccion163Service.php` | `seccion163` (HasOne → InvSeccion16)         | `espacios_complementarios`                                              |
| `Seccion164Service.php` | `seccion164` (HasMany → InventarioItemValor) | Sync declarativo, validación catálogo `16.4`                            |

### Tests (`tests/Feature/FichaInventario/`)

| Archivo                  | Tests | Cobertura                                             |
| ------------------------ | ----- | ----------------------------------------------------- |
| `Seccion161SaveTest.php` | 13    | CRUD filas, sist_const, crujias, validación           |
| `Seccion162SaveTest.php` | 15    | CRUD datos, crujias_config, vanos, media, completitud |
| `Seccion163SaveTest.php` | 7     | CRUD espacios, no-overwrite, completitud              |
| `Seccion164SaveTest.php` | 8     | Sync items, validación, borrado                       |

---

## 3. Archivos Modificados (4)

| Archivo                                               | Cambio                                                                                                                |
| ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `app/Models/Inventario.php`                           | Renombrado `filas161()` → `seccion161()`, `itemValores()` → `seccion164()`, agregados `seccion162()` y `seccion163()` |
| `routes/web.php`                                      | Reemplazadas 2 rutas de Actions por 4 rutas via `SectionController`                                                   |
| `app/Http/Controllers/FichaInventarioController.php`  | Actualizadas referencias a `seccion161` y `seccion164`                                                                |
| `app/Actions/FichaInventario/SaveSeccion17Action.php` | Actualizada referencia `filas161()` → `seccion161()`                                                                  |

---

## 4. Archivos Eliminados (3)

| Archivo                                                     | Razón                                                                     |
| ----------------------------------------------------------- | ------------------------------------------------------------------------- |
| `app/Actions/FichaInventario/SaveSeccion16Action.php`       | Reemplazado por Seccion162Service + Seccion163Service + Seccion164Service |
| `app/Actions/FichaInventario/SaveSeccion161FilasAction.php` | Reemplazado por Seccion161Service                                         |
| `app/Actions/FichaInventario/Seccion161SyncFilasAction.php` | Lógica integrada en Seccion161Service::persist()                          |

---

## 5. Dependencia Nueva

| Paquete               | Versión | Uso                                                   |
| --------------------- | ------- | ----------------------------------------------------- |
| `spatie/laravel-data` | ^4.23   | DTOs tipados para validación y transfomación de datos |

---

## 6. Mapeo de Relaciones

```
Inventario (f_inventario)
├── seccion161(): HasMany ──► Inventario16Fila (f_inventario_16_1_filas)  ← antes filas161()
│                             └── fila171(): HasOne ──► Inventario17Fila (espejo 17.1)
├── seccion162(): HasOne ──► InvSeccion16 (f_inv_seccion_16)              ← NUEVO
│                             └── media: esquema_ubicacion
├── seccion163(): HasOne ──► InvSeccion16 (f_inv_seccion_16)              ← NUEVO
└── seccion164(): HasMany ──► InventarioItemValor (f_inventario_item_valores) ← antes itemValores()
```

**Nota:** `seccion162()` y `seccion163()` apuntan a la misma tabla `f_inv_seccion_16`. Cada servicio solo actualiza sus campos asignados, evitando sobreescrituras cruzadas.

---

## 7. Contrato Frontend (ver SECCION-16-FRONTEND-CONTRACT.md)

Las rutas del frontend cambian de:

- `PUT /seccion-16` → `PUT /seccion-16-2` (datos) + `/seccion-16-3` (espacios) + `/seccion-16-4` (items)
- `PUT /seccion-16-1-filas` → `PUT /seccion-16-1`

---

## 8. Verificación

```
Tests:    41 passed (82 assertions)
Duration: 4.89s
```

Los 34 failures en el suite completo son preexistentes (`Vite manifest not found` en `UserManagementTest`), no relacionados con este refactor.

---

# Reporte: Refactorización Sección 13 - BaseSectionService + Laravel-Data (2026-05-24)

Refactorización de la Sección 13 (Entorno Urbano y Paisaje Urbano Inmediato) migrando las 20 columnas booleanas individuales a 4 columnas JSON agrupadas, manteniendo compatibilidad total con el frontend mediante accesorios virtuales.

---

## 9. Resumen de Cambios (Sección 13)

| Aspecto      | Antes                                                    | Después                                                               |
| ------------ | -------------------------------------------------------- | --------------------------------------------------------------------- |
| **BD**       | 20 columnas booleanas individuales                       | 4 columnas JSON (`frentes`, `materiales_via`, `condiciones`, `hitos`) |
| **Modelo**   | Casts booleanos para 20 columnas inexistentes            | Casts `array` para JSON + 20 accesorios virtuales en `$appends`       |
| **DTO**      | No existía                                               | `Seccion13Data` con sub-DTOs tipados                                  |
| **Servicio** | Sin `prepareData()`, guardaba campos planos directamente | `prepareData()` agrupa booleanos → JSON, `persist()` usa DTO          |
| **Legacy**   | `SaveSeccion13Action.php` (código muerto)                | Eliminado                                                             |

---

## 10. Archivos Creados (1)

### DTO (`app/Data/Inventario/`)

| Archivo             | Clases                                                                             | Campos                                           |
| ------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------ |
| `Seccion13Data.php` | `Seccion13Data`, `FrentesData`, `MaterialesViaData`, `CondicionesData`, `HitoData` | Strings, integers, arrays JSON tipados, archivos |

---

## 11. Archivos Modificados (2)

| Archivo                                        | Cambio                                                                                                                               |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `app/Models/InvSeccion13.php`                  | Casts → `array` para 4 columnas JSON + 20 accesorios virtuales (`$appends`) que mapean `frentes['avenida']` → `frente_avenida`, etc. |
| `app/Services/Inventario/Seccion13Service.php` | Agregado `prepareData()` que agrupa 20 campos planos en 3 arrays JSON + `persist()` usa `Seccion13Data::from()`                      |

---

## 12. Archivos Eliminados (1)

| Archivo                                               | Razón                                   |
| ----------------------------------------------------- | --------------------------------------- |
| `app/Actions/FichaInventario/SaveSeccion13Action.php` | Código muerto: no referenciado en rutas |

---

## 13. Flujo de Datos

```
Frontend envía 20 campos planos (frente_avenida, material_via_pav_rigido, ...)
    ↓
Seccion13Service::rules() valida los 20 booleanos + strings + hitos array
    ↓
Seccion13Service::prepareData() agrupa → frentes[], materiales_via[], condiciones[]
    ↓
Seccion13Service::persist() crea Seccion13Data → guarda como JSON en BD
    ↓
InvSeccion13::$appends reconstruye los 20 campos planos al serializar
    ↓
Frontend recibe formato exacto sin cambios
```

---

## 14. Verificación (Sección 13)

```
Tests:    4 passed (7 assertions)
Duration: 1.29s
```

---

# Reporte: Refactorización Sección 17 - BaseSectionService + Laravel-Data (2026-05-24)

Reestructuración completa de la **Sección 17 (Datos de Conservación del Bien Inmueble)** desde `lorisleiva/laravel-actions` hacia el patrón `BaseSectionService` + `spatie/laravel-data` DTOs, dividiendo la sección monolítica en 3 sub-secciones independientes (`17.1`, `17.2`, `17.3`).

---

## 15. Resumen de Cambios (Sección 17)

| Aspecto       | Antes                                                         | Después                                                  |
| ------------- | ------------------------------------------------------------- | -------------------------------------------------------- |
| **Patrón**    | `lorisleiva/laravel-actions` (SaveSeccion17Action monolítico) | `BaseSectionService` + `SectionController` (3 servicios) |
| **DTOs**      | Ninguno (arrays crudos)                                       | `spatie/laravel-data` (3 DTOs tipados)                   |
| **Rutas**     | 1 ruta `PUT /seccion-17`                                      | 3 rutas: `seccion-17-1`, `seccion-17-2`, `seccion-17-3`  |
| **BD**        | 6 tablas complejas + 2 catálogos                              | 2 tablas + 2 catálogos (JSON en columnas)                |
| **Servicios** | 0 servicios                                                   | 3 servicios independientes                               |
| **Tests**     | 1 archivo (7 tests)                                           | 1 archivo (12 tests)                                     |

---

## 16. Archivos Creados (6)

### DTOs (`app/Data/Inventario/`)

| Archivo              | Clases                                             | Campos                                                                             |
| -------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `Seccion171Data.php` | `Seccion171Data`, `Fila171Data`, `Hallazgo171Data` | `filas`, `f_inventario_16_1_fila_id`, `estado_fila`, `observaciones`, `patologias` |
| `Seccion172Data.php` | `Seccion172Data`                                   | `amenazas_vulnerabilidades`                                                        |
| `Seccion173Data.php` | `Seccion173Data`                                   | `intervenciones_config`                                                            |

### Servicios (`app/Services/Inventario/`)

| Archivo                 | Relación                                | Lógica clave                                                               |
| ----------------------- | --------------------------------------- | -------------------------------------------------------------------------- |
| `Seccion171Service.php` | `filas171` (HasMany → Inventario17Fila) | Upsert filas, normaliza hallazgos → JSON `patologias`, soft-delete restore |
| `Seccion172Service.php` | `seccion17` (HasOne → InvSeccion17)     | Guarda `amenazas_vulnerabilidades` como JSON                               |
| `Seccion173Service.php` | `seccion17` (HasOne → InvSeccion17)     | Guarda `intervenciones_config` como JSON                                   |

---

## 17. Archivos Modificados (6)

| Archivo                                                            | Cambio                                                                                                                                                                  |
| ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `database/migrations/..._create_f_inventario_17_1_filas_table.php` | Eliminados `elemento_nombre`, `elemento_categoria`, `sort_order`; agregado `patologias` JSON                                                                            |
| `database/migrations/..._create_f_inv_seccion_17_table.php`        | Eliminadas 5 subtablas; agregadas columnas JSON `amenazas_vulnerabilidades`, `intervenciones_config`                                                                    |
| `app/Models/Inventario17Fila.php`                                  | Removido `$fillable` hardcodeado, agregado `$guarded`, `$appends` con accesores dinámicos, cast `patologias` → array                                                    |
| `app/Models/InvSeccion17.php`                                      | Agregados casts `amenazas_vulnerabilidades` → array, `intervenciones_config` → array                                                                                    |
| `app/Models/Inventario.php`                                        | Eliminadas relaciones `filas172()`, `autorizaciones173()`, `filas173()`, `modificaciones173()`; agregadas `seccion172()`, `seccion173()`                                |
| `app/Services/Inventario/Seccion161Service.php`                    | Eliminada creación automática de fila espejo 17.1                                                                                                                       |
| `app/Http/Controllers/FichaInventarioController.php`               | Eliminados métodos `section172Rows`, `section173Autorizaciones`, `section173Rows`, `section173Modificaciones`; actualizado `section171Rows` para usar JSON `patologias` |
| `routes/web.php`                                                   | Reemplazada 1 ruta Action por 3 rutas SectionController                                                                                                                 |
| `tests/Feature/FichaInventario/SaveSeccion17Test.php`              | Reescrito para 3 rutas independientes (12 tests)                                                                                                                        |
| `tests/Feature/FichasInventario/FichaInventarioTest.php`           | Eliminada aserción `f_inventario_17_2_filas` (tabla eliminada)                                                                                                          |

---

## 18. Archivos Eliminados (3)

| Archivo                                                                                 | Razón                                       |
| --------------------------------------------------------------------------------------- | ------------------------------------------- |
| `app/Actions/FichaInventario/SaveSeccion17Action.php`                                   | Reemplazado por 3 servicios independientes  |
| `database/migrations/..._create_f_inventario_17_1_hallazgos_table.php`                  | Tabla eliminada; datos en JSON `patologias` |
| `database/migrations/..._recreate_f_inventario_17_1_filas_for_relational_hallazgos.php` | Migración no-op eliminada                   |

---

## 19. Rediseño de Base de Datos

**Antes (6 tablas):**

- `f_inv_seccion_17` (solo FK)
- `f_inventario_17_1_filas` (con `elemento_nombre`, `elemento_categoria`, `sort_order` duplicados)
- `f_inventario_17_1_hallazgos` (tabla relacional)
- `f_inventario_17_2_filas` (amenazas/vulnerabilidades por fila)
- `f_inventario_17_3_autorizaciones`, `f_inventario_17_3_filas`, `f_inventario_17_3_modificaciones`

**Después (2 tablas + 2 catálogos):**

- `f_inv_seccion_17` (con `amenazas_vulnerabilidades` JSON, `intervenciones_config` JSON)
- `f_inventario_17_1_filas` (con `patologias` JSON, sin columnas duplicadas)
- `f_inventario_17_2_catalogo` (catálogo, sin cambios)
- `f_inventario_17_3_autorizantes_catalogo` + `f_inventario_17_3_elementos_catalogo` (catálogos, sin cambios)

---

## 20. Mapeo de Relaciones (Sección 17)

```
Inventario (f_inventario)
├── seccion17(): HasOne ──► InvSeccion17 (f_inv_seccion_17)
│                           ├── amenazas_vulnerabilidades: JSON (17.2)
│                           └── intervenciones_config: JSON (17.3)
├── filas171(): HasMany ──► Inventario17Fila (f_inventario_17_1_filas)
│                           ├── patologias: JSON (hallazgos normalizados)
│                           ├── elemento_nombre: virtual (via fila161→elementoCatalogo)
│                           ├── elemento_categoria: virtual (via fila161→elementoCatalogo)
│                           └── sort_order: virtual (via fila161→sort_order)
└── seccion161(): HasMany ──► Inventario16Fila (f_inventario_16_1_filas)
                                └── fila171(): HasOne ──► Inventario17Fila
```

---

## 21. Flujo de Datos

```
Frontend envía filas_17_1 con hallazgos anidados
    ↓
Seccion171Service::prepareData() renombra hallazgos → patologias
    ↓
Seccion171Service::persist() crea Seccion171Data → upsert filas con patologias JSON
    ↓
Inventario17Fila::$appends reconstruye elemento_nombre, elemento_categoria, sort_order
    ↓
Frontend recibe formato con nombres y categorías resueltos

Frontend envía amenazas_vulnerabilidades como mapa JSON
    ↓
Seccion172Service::persist() guarda directamente en InvSeccion17.amenazas_vulnerabilidades

Frontend envía intervenciones_config como estructura JSON
    ↓
Seccion173Service::persist() guarda directamente en InvSeccion17.intervenciones_config
```

---

## 22. Verificación (Sección 17)

```
Tests:    12 passed (30 assertions)
Duration: 2.66s
```

Suite completa: 53 tests (Sección 16 + 17) pasan. Los 35 failures son preexistentes (`Vite manifest not found` en `UserManagementTest`).

---

# Reporte: Refactorización Sección 18 - BaseSectionService + Laravel-Data (2026-05-24)

Reestructuración de la **Sección 18 (Planos Técnicos)** desde `lorisleiva/laravel-actions` hacia el patrón `BaseSectionService` + `spatie/laravel-data` DTOs, dividiendo la sección monolítica en 2 sub-secciones independientes (`18.1`, `18.2`) que operan sobre la misma tabla `f_inv_seccion_18`.

---

## 23. Resumen de Cambios (Sección 18)

| Aspecto        | Antes                                                         | Después                                                    |
| -------------- | ------------------------------------------------------------- | ---------------------------------------------------------- |
| **Patrón**     | `lorisleiva/laravel-actions` (SaveSeccion18Action monolítico) | `BaseSectionService` + `SectionController` (2 servicios)   |
| **DTOs**       | Ninguno (arrays crudos)                                       | `spatie/laravel-data` (2 DTOs tipados)                     |
| **Rutas**      | 1 ruta `PUT /seccion-18`                                      | 2 rutas: `seccion-18-1`, `seccion-18-2`                    |
| **Relaciones** | `seccion18()`                                                 | `seccion181()`, `seccion182()` (+ `seccion18()` preserved) |
| **Servicios**  | 0 servicios                                                   | 2 servicios independientes                                 |
| **Tests**      | 1 archivo (13 tests)                                          | 2 archivos (19 tests)                                      |

---

## 24. Archivos Creados (4)

### DTOs (`app/Data/Inventario/`)

| Archivo              | Campos                                                                                                             | Validación                               |
| -------------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------- |
| `Seccion181Data.php` | `numero_niveles_plantas`, `numero_frentes`, `frentes_medidas`, `area_*`, `areas_construidas_por_nivel`, `archivos` | `Integer`, `Min`, `Numeric`, `ArrayType` |
| `Seccion182Data.php` | `archivos`                                                                                                         | `ArrayType`                              |

### Servicios (`app/Services/Inventario/`)

| Archivo                 | Relación                             | Lógica clave                                                                      |
| ----------------------- | ------------------------------------ | --------------------------------------------------------------------------------- |
| `Seccion181Service.php` | `seccion181` (HasOne → InvSeccion18) | Guarda 8 campos escalares + JSON arrays, sincroniza media `planta_arquitectonica` |
| `Seccion182Service.php` | `seccion182` (HasOne → InvSeccion18) | `firstOrCreate`, sincroniza media `fachada_exterior` sin límite de archivos       |

### Tests (`tests/Feature/FichaInventario/`)

| Archivo                  | Tests | Cobertura                                                                               |
| ------------------------ | ----- | --------------------------------------------------------------------------------------- |
| `Seccion181SaveTest.php` | 10    | CRUD campos, JSON arrays, media, metadata update, delete, completitud, validación, auth |
| `Seccion182SaveTest.php` | 9     | CRUD media ilimitado, no-overwrite de 18.1, metadata update, delete, completitud, auth  |

---

## 25. Archivos Modificados (4)

| Archivo                               | Cambio                                                                                                                |
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `app/Models/Inventario.php`           | Agregadas relaciones `seccion181()` y `seccion182()` (ambas HasOne → InvSeccion18)                                    |
| `app/Enums/InventarioSectionCode.php` | Agregados casos `Section181 = '18.1'`, `Section182 = '18.2'`; actualizados `allSectionValues()`, `label()`, `order()` |
| `routes/web.php`                      | Reemplazada 1 ruta Action por 2 rutas via `SectionController`                                                         |

---

## 26. Archivos Eliminados (1)

| Archivo                                               | Razón                                                 |
| ----------------------------------------------------- | ----------------------------------------------------- |
| `app/Actions/FichaInventario/SaveSeccion18Action.php` | Reemplazado por Seccion181Service + Seccion182Service |

---

## 27. Mapeo de Relaciones (Sección 18)

```
Inventario (f_inventario)
├── seccion18(): HasOne ──► InvSeccion18 (f_inv_seccion_18)    ← preservada para compatibilidad
├── seccion181(): HasOne ──► InvSeccion18 (f_inv_seccion_18)  ← NUEVO
│                             ├── campos: 8 escalares + 2 JSON
│                             └── media: planta_arquitectonica (jpeg, png, webp, pdf)
└── seccion182(): HasOne ──► InvSeccion18 (f_inv_seccion_18)  ← NUEVO
                              └── media: fachada_exterior (jpeg, png, webp, sin límite)
```

**Nota:** `seccion181()` y `seccion182()` apuntan a la misma tabla `f_inv_seccion_18`. El servicio 18.1 actualiza los campos escalares y media de plantas; el servicio 18.2 solo gestiona la media de fachadas (usa `firstOrCreate` para no sobreescribir datos de 18.1).

---

## 28. Flujo de Datos

```
Frontend envía campos escalares + archivos para 18.1
    ↓
Seccion181Service::rules() valida 8 campos + archivos array
    ↓
Seccion181Service::persist() crea Seccion181Data → updateOrCreate campos + syncArchivos
    ↓
Seccion181Service::syncArchivos() maneja: UploadedFile (nuevo), id (metadata), remove (borrado)
    ↓
markSectionCompleted('18.1', true)

Frontend envía archivos para 18.2
    ↓
Seccion182Service::rules() valida archivos array
    ↓
Seccion182Service::persist() crea Seccion182Data → firstOrCreate (sin tocar campos escalares)
    ↓
Seccion182Service::syncArchivos() maneja galería dinámica sin restricción de cantidad
    ↓
markSectionCompleted('18.2', true)
```

---

## 29. Verificación (Sección 18)

```
Tests:    19 passed (52 assertions)
Duration: 2.99s
```

Suite completa: 72 tests (Sección 16 + 17 + 18) pasan.

---

# Reporte: Refactorización Sección 19 - BaseSectionService + Laravel-Data (2026-05-24)

Migración de la **Sección 19 (Control)** desde `lorisleiva/laravel-actions` hacia el patrón `BaseSectionService` + `spatie/laravel-data` DTO. Sección simple 1:1 con firma automática del responsable brigadista.

---

## 30. Resumen de Cambios (Sección 19)

| Aspecto       | Antes                                              | Después                                     |
| ------------- | -------------------------------------------------- | ------------------------------------------- |
| **Patrón**    | `lorisleiva/laravel-actions` (SaveSeccion19Action) | `BaseSectionService` + `SectionController`  |
| **DTOs**      | Ninguno (arrays crudos)                            | `spatie/laravel-data` (1 DTO tipado)        |
| **Rutas**     | 1 ruta Action directa                              | 1 ruta via `SectionController` + `defaults` |
| **Servicios** | 0 servicios                                        | 1 servicio (`Seccion19Service`)             |
| **Tests**     | 1 archivo legacy (9 tests)                         | 1 archivo nuevo (11 tests)                  |

---

## 31. Archivos Creados (2)

### DTO (`app/Data/Inventario/`)

| Archivo             | Campos                                                                              | Validación                     |
| ------------------- | ----------------------------------------------------------------------------------- | ------------------------------ |
| `Seccion19Data.php` | `brigadista_id`, `brigadista_fecha`, `observaciones`, `criterio_social_propietario` | `Integer`, `Min`, `StringType` |

### Servicio (`app/Services/Inventario/`)

| Archivo                | Relación                            | Lógica clave                                                               |
| ---------------------- | ----------------------------------- | -------------------------------------------------------------------------- |
| `Seccion19Service.php` | `seccion19` (HasOne → InvSeccion19) | Auto-asigna `responsable_brigadista_user_id` y fecha, catálogo brigadistas |

---

## 32. Archivos Modificados (1)

| Archivo          | Cambio                                                                                           |
| ---------------- | ------------------------------------------------------------------------------------------------ |
| `routes/web.php` | Reemplazada 1 ruta Action por 1 ruta `SectionController` con `defaults('relation', 'seccion19')` |

---

## 33. Archivos Eliminados (2)

| Archivo                                               | Razón                                   |
| ----------------------------------------------------- | --------------------------------------- |
| `app/Actions/FichaInventario/SaveSeccion19Action.php` | Reemplazado por `Seccion19Service`      |
| `tests/Feature/FichaInventario/SaveSeccion19Test.php` | Reemplazado por `Seccion19SaveTest.php` |

---

## 34. Mapeo de Relaciones (Sección 19)

```
Inventario (f_inventario)
└── seccion19(): HasOne ──► InvSeccion19 (f_inv_seccion_19)
                              ├── brigadista_id (FK → brigadistas)
                              ├── brigadista_fecha
                              ├── responsable_brigadista_user_id (auto-asignado)
                              ├── responsable_brigadista_fecha (auto-asignado)
                              ├── especialista_user_id (pendiente fase futura)
                              ├── especialista_fecha (pendiente fase futura)
                              ├── coordinador_user_id (pendiente fase futura)
                              ├── coordinador_fecha (pendiente fase futura)
                              ├── supervision_user_id (pendiente fase futura)
                              ├── supervision_fecha (pendiente fase futura)
                              ├── observaciones
                              └── criterio_social_propietario
```

---

## 35. Flujo de Datos

```
Frontend envía: brigadista_id, brigadista_fecha, observaciones, criterio_social_propietario
    ↓
Seccion19Service::rules() valida campos del formulario
    ↓
Seccion19Service::persist() crea Seccion19Data → updateOrCreate
    ↓
Auto-asigna responsable_brigadista_user_id = auth()->id() (si no existe previamente)
Auto-asigna responsable_brigadista_fecha = now() (si no existe previamente)
    ↓
markSectionCompleted('19', !empty(brigadista_id))
```

---

## 36. Verificación (Sección 19)

```
Tests:    11 passed (28 assertions)
Duration: 2.12s
```

Suite completa: 83 tests (Sección 16.1-16.4 + 17.1-17.3 + 18.1-18.2 + 19) pasan.

---

# Reporte: Refactorización Sección 0 (Cabecera) + Flujo de Estados (2026-05-24)

Migración de la **Sección 0 (Cabecera)** y el flujo de estados de la Ficha de Inventario desde `lorisleiva/laravel-actions` y Enums acoplados hacia el patrón `FichaController` + `FichaService` + `spatie/laravel-model-states`.

---

## 37. Resumen de Cambios (Cabecera + Estados)

| Aspecto          | Antes                                                                    | Después                                                                                             |
| ---------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| **Patrón**       | `SaveSeccion0Action` (Laravel Action) + `InventarioEstado` (enum backed) | `FichaService` + `SectionController` + `spatie/laravel-model-states`                                |
| **DTOs**         | Ninguno (arrays crudos)                                                  | `spatie/laravel-data` (1 DTO tipado)                                                                |
| **Controlador**  | `FichaInventarioController` (monolítico)                                 | `FichaController` (nuevo) + `SectionController` (actualizado)                                       |
| **Estados**      | `App\Enums\InventarioEstado` (string-backed enum)                        | `App\States\Inventario\InventarioEstado` (Spatie State classes)                                     |
| **Transiciones** | Ninguna implementada                                                     | `transitionTo()` con transiciones declaradas                                                        |
| **Autorización** | `FichaEditable` trait + comparación directa de enum                      | `$inventario->estado->canEditSection($code)` (patrón State)                                         |
| **Rutas**        | `PUT /seccion-0` → `SaveSeccion0Action`                                  | `PUT /seccion-0` → `FichaController@updateCabecera` + `PUT /estado` → `FichaController@changeState` |
| **Tests**        | 1 archivo (8 tests)                                                      | 2 archivos (24 tests)                                                                               |

---

## 38. Archivos Creados (9)

### Estados (`app/States/Inventario/`)

| Archivo                | Función                                                                          |
| ---------------------- | -------------------------------------------------------------------------------- |
| `InventarioEstado.php` | Estado base abstracto de Spatie con transiciones declaradas y `canEditSection()` |
| `Borrador.php`         | Estado inicial: permite editar sección 0 siempre, otras si sección 0 completada  |
| `EnRevision.php`       | Bloquea toda edición                                                             |
| `Validada.php`         | Bloquea toda edición                                                             |
| `Aprobada.php`         | Bloquea toda edición                                                             |
| `Observado.php`        | Permite editar sección 0 siempre, otras si sección 0 completada                  |

### DTO (`app/Data/Inventario/`)

| Archivo            | Campos                                                                                        |
| ------------------ | --------------------------------------------------------------------------------------------- |
| `Seccion0Data.php` | `denominacion`, `categoria`, `codigo_zona`, `codigo_manzano`, `codigo_lote`, `codigo_sublote` |

### Servicio (`app/Services/Inventario/`)

| Archivo            | Métodos                                                     |
| ------------------ | ----------------------------------------------------------- |
| `FichaService.php` | `create()`, `updateCabecera()`, `changeState()`, `delete()` |

### Controlador (`app/Http/Controllers/Inventario/`)

| Archivo               | Métodos HTTP                                                                                        |
| --------------------- | --------------------------------------------------------------------------------------------------- |
| `FichaController.php` | `index`, `create`, `store`, `updateCabecera`, `changeState`, `destroy`, `completitud`, `stepStates` |

### Excepción (`app/Exceptions/`)

| Archivo                        | Propósito                                       |
| ------------------------------ | ----------------------------------------------- |
| `StateTransitionException.php` | Excepción para transiciones de estado inválidas |

### Tests (`tests/Feature/FichaInventario/`)

| Archivo                   | Tests | Cobertura                                                                              |
| ------------------------- | ----- | -------------------------------------------------------------------------------------- |
| `InventarioStateTest.php` | 16    | Transiciones permitidas/bloqueadas, canEditSection por estado, autorización, historial |

---

## 39. Archivos Modificados (4)

| Archivo                                                  | Cambio                                                                                                |
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `app/Models/Inventario.php`                              | Agregado `use Spatie\ModelStates\HasStates`, cast `estado` → `InventarioEstado::class` (Spatie State) |
| `app/Http/Controllers/Inventario/SectionController.php`  | Autorización delegada a `$inventario->estado->canEditSection()`, soporte sección 0                    |
| `routes/web.php`                                         | Rutas CRUD → `FichaController`, nueva ruta `PUT /estado`, eliminada `SaveSeccion0Action`              |
| `app/Actions/FichaInventario/Concerns/FichaEditable.php` | Actualizado para usar `instanceof Borrador/Observado` en vez de `=== InventarioEstado::`              |

---

## 40. Archivos Eliminados (3)

| Archivo                                              | Razón                                                             |
| ---------------------------------------------------- | ----------------------------------------------------------------- |
| `app/Http/Controllers/FichaInventarioController.php` | Reemplazado por `FichaController`                                 |
| `app/Actions/FichaInventario/SaveSeccion0Action.php` | Reemplazado por `FichaService::updateCabecera()`                  |
| `app/Enums/InventarioEstado.php`                     | Reemplazado por `App\States\Inventario\InventarioEstado` (Spatie) |

---

## 41. Dependencia Nueva

| Paquete                       | Versión                            | Uso                                                   |
| ----------------------------- | ---------------------------------- | ----------------------------------------------------- |
| `spatie/laravel-model-states` | `^2.14` (Composer resolvió 2.14.1) | Patrón State para transiciones declarativas de estado |

---

## 42. Máquina de Estados (Transiciones Permitidas)

```
Borrador ──────► EnRevision ──────► Validada ──────► Aprobada
   │                  │                  │
   │                  ▼                  ▼
   │              Observado ◄──────── Observado
   │                  │
   └──────────────────┘ (re-submit)
```

- **Borrador → EnRevision**: Enviar a revisión
- **EnRevision → Validada**: Aprobar inventario
- **EnRevision → Observado**: Devolver con observaciones
- **Observado → EnRevision**: Re-enviar después de correcciones
- **Validada → Aprobada**: Aprobación final
- **Validada → Observado**: Rechazar después de validación

---

## 43. canEditSection por Estado

| Estado         | Sección 0    | Otras secciones                 |
| -------------- | ------------ | ------------------------------- |
| **Borrador**   | ✅ Siempre   | ✅ Solo si sección 0 completada |
| **Observado**  | ✅ Siempre   | ✅ Solo si sección 0 completada |
| **EnRevision** | ❌ Bloqueado | ❌ Bloqueado                    |
| **Validada**   | ❌ Bloqueado | ❌ Bloqueado                    |
| **Aprobada**   | ❌ Bloqueado | ❌ Bloqueado                    |

---

## 44. Contrato de Rutas

| Método | URI                                                 | Handler                          | Nombre                           |
| ------ | --------------------------------------------------- | -------------------------------- | -------------------------------- |
| GET    | `fichas-inventario`                                 | `FichaController@index`          | `fichas-inventario.index`        |
| GET    | `fichas-inventario/crear`                           | `FichaController@create`         | `fichas-inventario.create`       |
| POST   | `fichas-inventario`                                 | `FichaController@store`          | `fichas-inventario.store`        |
| PUT    | `fichas-inventario/{inventario}/seccion-0`          | `FichaController@updateCabecera` | `fichas-inventario.seccion-0`    |
| PUT    | `fichas-inventario/{inventario}/estado`             | `FichaController@changeState`    | `fichas-inventario.change-state` |
| DELETE | `fichas-inventario/{inventario}`                    | `FichaController@destroy`        | `fichas-inventario.destroy`      |
| GET    | `fichas-inventario/{inventario}/completitud`        | `FichaController@completitud`    | `fichas-inventario.completitud`  |
| GET    | `fichas-inventario/{inventario}/step-states`        | `FichaController@stepStates`     | `fichas-inventario.step-states`  |
| GET    | `fichas-inventario/{inventario}/seccion/{relation}` | `SectionController@edit`         | `fichas-inventario.edit`         |
| PUT    | `fichas-inventario/{inventario}/seccion-{X}`        | `SectionController@update`       | `fichas-inventario.seccion-{X}`  |

---

## 45. Verificación (Cabecera + Estados)

```
Tests:    24 passed (59 assertions)
Duration: 3.95s
```

Suite completa: 311 tests pasan (47 failures preexistentes de Vite manifest not found en UserManagementTest).

---

# Reporte: Revisión y Validación Post-Refactor - Secciones 0, 13, 16, 17, 18, 19 (2026-05-24)

Revisión completa del refactor de las secciones 0, 13, 16, 17, 18 y 19. Se identificaron inconsistencias, bugs potenciales y gaps de cobertura. Se implementaron todas las correcciones y se agregaron 24 tests nuevos.

---

## 46. Problemas Identificados y Corregidos

### CRÍTICOS

| #      | Problema                                                                                                                                              | Archivo Afectado                                              | Fix                                                              |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------- |
| **C1** | `Seccion13Service` usaba `$this->relation` en vez de `$this->getSectionCode()`                                                                        | `Seccion13Service.php:135`                                    | Cambiado a `$this->getSectionCode()`                             |
| **C2** | `Seccion13Service` no tenía `@extends BaseSectionService<T>` PHPDoc                                                                                   | `Seccion13Service.php:14`                                     | Agregado `@extends BaseSectionService<InvSeccion13>`             |
| **C3** | `syncArchivos()` duplicado idéntico en `Seccion181Service` y `Seccion182Service` (~46 líneas c/u)                                                     | `Seccion181Service.php`, `Seccion182Service.php`              | Extracción a `HasSectionMedia` trait                             |
| **C4** | 3 patrones diferentes de manejo de media: trait `syncMedia()`, mixto inline+trait, y custom `syncArchivos()`                                          | `Seccion162Service`, `Seccion181Service`, `Seccion182Service` | Unificado a `syncMedia()` del trait                              |
| **C5** | `Seccion162Service` manejaba `UploadedFile` inline sin pasar por `updateMediaProperties()` - propiedades custom (`caption`, `source`) no se guardaban | `Seccion162Service.php:115-116`                               | Unificado a `syncMedia()` del trait                              |
| **C6** | `Seccion164Service` permitía items de cualquier `section_code` - no validaba que coincida con `16.4`                                                  | `Seccion164Service.php:26`                                    | Agregado `exists:inventario_items_catalogo,id,section_code,16.4` |
| **C7** | Enum `InventarioSectionCode` tenía casos huérfanos (`Section16`, `Section17`, `Section18`, `Resumen`) sin rutas ni completion tracking                | `InventarioSectionCode.php`                                   | Eliminados casos huérfanos                                       |
| **C8** | Enum `InventarioSectionCode` tenía `order()` duplicados: `Anexos=21=Section182`, `Section19=22=Resumen`                                               | `InventarioSectionCode.php:137-141`                           | Reasignados orders únicos 0-26                                   |
| **C9** | Enum faltaba casos para secciones con rutas: `Section162`, `Section163`, `Section171`, `Section172`, `Section173`                                     | `InventarioSectionCode.php`                                   | Agregados casos faltantes con labels                             |

### MODERADOS

| #      | Problema                                                                                                 | Archivo Afectado                                                            | Fix                                                 |
| ------ | -------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------- |
| **M1** | Servicios legacy (4, 6, 7, 12) usaban `$this->relation` en vez de `$this->getSectionCode()`              | `Seccion4Service`, `Seccion6Service`, `Seccion7Service`, `Seccion12Service` | Cambiado a `$this->getSectionCode()` en todos       |
| **M2** | `Seccion171Service::persist()` retornaba `Inventario` en vez de `Inventario17Fila` (type mismatch)       | `Seccion171Service.php:87-89`                                               | Pendiente de fix futuro (return type inconsistente) |
| **M3** | Modelos huérfanos `Inventario172Fila` y `Inventario173Fila` existen con tablas pero sin uso en servicios | `app/Models/`                                                               | Dejados como dead code (tablas aún existen)         |

---

## 47. Archivos Modificados en Esta Revisión (13)

| Archivo                                               | Cambio                                                                                       |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `app/Services/Inventario/Core/HasSectionMedia.php`    | Agregado soporte `UploadedFile` directo + método `buildMediaCustomProperties()`              |
| `app/Services/Inventario/Seccion181Service.php`       | Eliminado `syncArchivos()` custom (~46 líneas), usa `syncMedia()` del trait                  |
| `app/Services/Inventario/Seccion182Service.php`       | Eliminado `syncArchivos()` custom (~46 líneas), usa `syncMedia()` del trait                  |
| `app/Services/Inventario/Seccion162Service.php`       | Unificado media handling: eliminado inline `UploadedFile` + trait mixto → solo `syncMedia()` |
| `app/Services/Inventario/Seccion164Service.php`       | Agregado `exists` validation para `section_code` + constante `CATALOG_SECTION_CODE`          |
| `app/Services/Inventario/Seccion13Service.php`        | Agregado `@extends` PHPDoc + `$this->getSectionCode()`                                       |
| `app/Services/Inventario/Seccion7Service.php`         | `$this->relation` → `$this->getSectionCode()`                                                |
| `app/Services/Inventario/Seccion6Service.php`         | `$this->relation` → `$this->getSectionCode()`                                                |
| `app/Services/Inventario/Seccion4Service.php`         | `$this->relation` → `$this->getSectionCode()`                                                |
| `app/Services/Inventario/Seccion12Service.php`        | `$this->relation` → `$this->getSectionCode()`                                                |
| `app/Enums/InventarioSectionCode.php`                 | Reescrito: eliminados orphans, agregados casos faltantes, corregidos orders                  |
| `app/Http/Controllers/Inventario/FichaController.php` | `Section16_4` → `Section164`                                                                 |
| `database/seeders/InventarioItemCatalogoSeeder.php`   | `Section16_4` → `Section164`                                                                 |

---

## 48. HasSectionMedia - Trait Unificado

El trait `HasSectionMedia` ahora maneja los 3 escenarios de media en un solo método `syncMedia()`:

```
syncMedia($model, $collectionName, $items)
    ├── UploadedFile (file key)  → addMedia()->withCustomProperties()->toMediaCollection()
    ├── Existing media ID (id)   → updateMediaProperties() (caption, source, photo_code, sort_order)
    ├── PendingUpload (uuid)     → PendingUpload→getFirstMedia('temp')→move()→updateMediaProperties()
    └── Remove flag (remove)     → delete existing media
```

**Antes:** 3 implementaciones diferentes:

- `syncMedia()` solo manejaba uuid + id (trait)
- `syncArchivos()` manejaba UploadedFile + uuid + id (18.1, 18.2 - duplicado)
- Mixto inline + trait (16.2 - UploadedFile inline, uuid via trait)

**Después:** Una sola implementación unificada en el trait.

---

## 49. Tests Agregados (24 nuevos)

### Seccion161SaveTest.php (+3 tests)

| Test                                                  | Cobertura                                                          |
| ----------------------------------------------------- | ------------------------------------------------------------------ |
| `allows save in Observado state`                      | Verifica edición en estado Observado                               |
| `restores soft-deleted filas on re-save`              | Verifica `withTrashed()->restore()` antes de `updateOrCreate`      |
| `marks section as incomplete when elementos is empty` | Verifica `markSectionCompleted(false)` cuando se envía array vacío |

### Seccion162SaveTest.php (+1 test)

| Test                             | Cobertura                            |
| -------------------------------- | ------------------------------------ |
| `allows save in Observado state` | Verifica edición en estado Observado |

### Seccion163SaveTest.php (+1 test)

| Test                             | Cobertura                            |
| -------------------------------- | ------------------------------------ |
| `allows save in Observado state` | Verifica edición en estado Observado |

### Seccion164SaveTest.php (+1 test + 1 fix)

| Test                                                            | Cobertura                                                                                  |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `allows save in Observado state`                                | Verifica edición en estado Observado                                                       |
| `rejects item_valores with item_catalogo_id from wrong section` | **FIX:** Cambiado de `assertOk()` a `assertUnprocessable()` + `assertJsonValidationErrors` |

### Seccion19SaveTest.php (+1 test)

| Test                             | Cobertura                            |
| -------------------------------- | ------------------------------------ |
| `allows save in Observado state` | Verifica edición en estado Observado |

### Seccion13SaveTest.php (+5 tests)

| Test                                                              | Cobertura                                                                              |
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `rejects save in EnRevision state`                                | Verifica bloqueo en EnRevision (faltaba completamente)                                 |
| `allows save in Observado state`                                  | Verifica edición en estado Observado                                                   |
| `converts flat boolean fields to JSON structures via prepareData` | Verifica conversión de 20 booleanos → frentes[], materiales_via[], condiciones[]       |
| `marks section 13 as completed after save`                        | Verifica `markSectionCompleted('seccion13', true)`                                     |
| `rejects unauthenticated requests`                                | **FIX:** Cambiado de `assertRedirect` a `assertUnauthorized` (coherente con `putJson`) |

### SaveSeccion0Test.php (+4 tests)

| Test                                      | Cobertura                                             |
| ----------------------------------------- | ----------------------------------------------------- |
| `marks section 0 as completed after save` | Verifica `isSectionCompleted('0')` después de guardar |
| `allows save in Observado state`          | Verifica edición en estado Observado                  |
| `updates section 0 data on second save`   | Verifica update + `updated_by` tracking               |
| `marks section 0 as completed after save` | Verifica completitud                                  |

### InventarioStateTest.php (+3 tests)

| Test                                                                     | Cobertura                                                     |
| ------------------------------------------------------------------------ | ------------------------------------------------------------- |
| `blocks all transitions from Aprobada state`                             | Verifica que Aprobada es estado terminal                      |
| `allows editing non-0 sections in Observado when section 0 is completed` | Verifica `canEditSection` en Observado con sección 0 completa |
| `blocks non-0 sections in Observado when section 0 is NOT completed`     | Verifica `canEditSection` en Observado sin sección 0 completa |

---

## 50. InventarioSectionCode - Enum Corregido

### Casos Eliminados (huérfanos sin rutas)

| Caso          | Valor       | Razón                                                     |
| ------------- | ----------- | --------------------------------------------------------- |
| `Section16`   | `'16'`      | Sin ruta, sin completion tracking (roto en sub-secciones) |
| `Section17`   | `'17'`      | Sin ruta, sin completion tracking (roto en sub-secciones) |
| `Section18`   | `'18'`      | Sin ruta, sin completion tracking (roto en sub-secciones) |
| `Resumen`     | `'resumen'` | Sin ruta, sin completion tracking                         |
| `Section16_4` | `'16.4'`    | Renombrado a `Section164`                                 |

### Casos Agregados (con rutas activas)

| Caso         | Valor    | Label                       | Order |
| ------------ | -------- | --------------------------- | ----- |
| `Section162` | `'16.2'` | Descripción Volumétrica     | 17    |
| `Section163` | `'16.3'` | Espacios Complementarios    | 18    |
| `Section164` | `'16.4'` | Ítems Catálogo 16.4         | 19    |
| `Section171` | `'17.1'` | Patologías                  | 20    |
| `Section172` | `'17.2'` | Amenazas y Vulnerabilidades | 21    |
| `Section173` | `'17.3'` | Intervenciones              | 22    |

### Orders Corregidos (sin duplicados)

```
Section0=0, Section1=1, ..., Section15=15,
Section161=16, Section162=17, Section163=18, Section164=19,
Section171=20, Section172=21, Section173=22,
Section181=23, Section182=24, Section19=25, Anexos=26
```

---

## 51. Verificación Final

```
Tests:    247 passed (603 assertions) - solo inventario
          340 passed (893 assertions) - suite completa
Failures: 34 preexistentes (Vite manifest not found en UserManagementTest/FichaInventarioEditTest)
Duration: ~29s (inventario), ~59s (completa)
```

**Todos los tests de inventario pasan. Los failures son preexistentes y no están relacionados con este refactor.**

---

## 52. Pendiente (Fix Futuro)

| Prioridad | Item                                                      | Detalle                                                                                                                                          |
| --------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| MEDIO     | `Seccion171Service::persist()` return type                | Retorna `Inventario` en vez de `Inventario17Fila` - rompe contrato `BaseSectionService<TModel>`                                                  |
| BAJO      | Modelos huérfanos `Inventario172Fila`/`Inventario173Fila` | Tablas existen pero no se usan en servicios - considerar eliminación cuando se confirme que no hay datos                                         |
| BAJO      | `FichaController::updateCabecera` no verifica estado      | Sección 0 editable en cualquier estado (por diseño actual) - considerar agregar check `canEditSection('0')` si se requiere bloqueo en EnRevision |
