F6.1 · Auditoría de consistencia y manuales (#32)

Consolida invariantes F6.1, corrige terminología de contexto operativo, agrega contratos transversales y crea manuales de programador/usuario. Deploy permanece pausado.
This commit is contained in:
2026-09-09 14:58:46 -03:00
committed by GitHub
12 changed files with 1056 additions and 18 deletions
+24 -5
View File
@@ -2,9 +2,28 @@
Repositorio del sistema DH Inspección V2.
## Estructura
## Componentes
- `api-v3/`: API v3.
- `web-v2/`: aplicación web.
- `scripts/`: utilidades operativas y de despliegue.
- `docs/`: documentación técnica.
- `api-v3/`: API NestJS/TypeORM y contratos de dominio.
- `web-v2/`: aplicación WEB React/Vite.
- `android-app/`: aplicación Android para trabajo de campo.
- `scripts/`: utilidades operativas, preflight y despliegue.
- `docs/`: documentación funcional y técnica.
## Documentación vigente
- [Manual del Programador](docs/MANUAL_PROGRAMADOR.md)
- [Manual de Usuario](docs/MANUAL_USUARIO.md)
- [Modelo canónico de Inventarios F6.1](docs/F6_INVENTORY_MODEL.md)
- [Auditoría de consistencia F6.1](docs/AUDITORIA_CONSISTENCIA_F6_1.md)
## Contratos que no deben romperse
- Inventario físico: **Departamento → Área → Yacimiento → Instalación → Subinstalación**.
- Empresa es un maestro independiente; la Operadora se relaciona temporalmente con Área.
- Una Inspección puede tener múltiples Actas, pero sólo un Acta `DRAFT` simultánea.
- Hallazgos directos: Yacimiento, Instalación y Subinstalación, siempre con `OTROS` disponible.
- La APK puede abrir una Inspección reutilizando el lifecycle canónico del backend.
- GEDO/IF oficializa el Informe, pero no activa por sí solo el vencimiento No urgente.
Las reglas detalladas y el procedimiento de cambio seguro están en los manuales enlazados arriba.
@@ -9,6 +9,8 @@ import {
MinLength,
} from 'class-validator';
// Invariante de dominio: Empresa es un maestro independiente. La jerarquía física
// se expresa sólo como Departamento → Área → Yacimiento → Instalación → Subinstalación.
export const INVENTORY_STRUCTURE_KINDS = [
'EMPRESA',
'DEPARTAMENTO',
@@ -30,6 +30,8 @@ export enum InspectionDeadlineBasis {
GEDO_DATE = 'GEDO_DATE',
}
// Una Inspección puede acumular múltiples Actas. El índice parcial de DRAFT es
// deliberado: permite el historial multi-Acta y bloquea dos borradores simultáneos.
@Entity({ name: 'inspection_acts' })
@Index('uq_inspection_acts_one_draft_per_visit', ['visitId'], { unique: true, where: "status = 'DRAFT'" })
@Index('uq_inspection_acts_year_number', ['actYear', 'actNumber'], { unique: true })
@@ -295,11 +295,13 @@ export class InspectionVisitLifecycleService {
}
}
// El contexto operativo de una Inspección es Área + Operadora vigente. El
// Yacimiento pertenece al alcance físico y nunca reemplaza al Área raíz.
private async validatePlanningContext(manager: EntityManager, visit: InspectionVisit): Promise<void> {
if (!visit.operationalAreaId || !visit.operatorCompanyId || !visit.leadInspectorUserId || !visit.plannedStartAt) {
throw new ConflictException({
code: 'INSPECTION_PLANNING_INCOMPLETE',
message: 'Definí Área/Yacimiento, Operadora, Inspector responsable y fecha de inicio antes de planificar',
message: 'Definí Área, Operadora, Inspector responsable y fecha de inicio antes de planificar',
});
}
if (!visit.checklistGeneratedAt || visit.checklistGeneration < 1) {
@@ -320,7 +322,7 @@ export class InspectionVisitLifecycleService {
if (!relation) {
throw new ConflictException({
code: 'INSPECTION_OPERATOR_RELATION_INVALID',
message: 'La Operadora seleccionada no tiene una relación operativa vigente con el Área/Yacimiento',
message: 'La Operadora seleccionada no tiene una relación operativa vigente con el Área',
});
}
const [inspector] = await manager.query(`
@@ -84,6 +84,8 @@ export class InspectionVisitsController {
@Req() request: RequestWithContext,
) {
assertMobileInspector(principal);
// Android no tiene un lifecycle paralelo: crea la visita y delega las
// transiciones DRAFT → PLANNED → IN_PROGRESS a los servicios canónicos.
const created = await this.visits.create({
operationalAreaId: dto.operationalAreaId,
operatorCompanyId: dto.operatorCompanyId,
@@ -0,0 +1,71 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import test from 'node:test';
function source(path: string): string {
return readFileSync(resolve(process.cwd(), path), 'utf8');
}
test('F6.1 conserva una jerarquía territorial única y Empresa fuera del árbol físico', () => {
const structure = source('src/asset-master/inventory-structure.service.ts');
const dto = source('src/asset-master/dto/create-inventory-structure.dto.ts');
assert.match(dto, /'EMPRESA',[\s\S]*'DEPARTAMENTO',[\s\S]*'AREA',[\s\S]*'YACIMIENTO',[\s\S]*'INSTALACION',[\s\S]*'SUBINSTALACION'/);
assert.match(structure, /EMPRESA:\s*null/);
assert.match(structure, /DEPARTAMENTO:\s*null/);
assert.match(structure, /AREA:\s*'departamento'/);
assert.match(structure, /YACIMIENTO:\s*'area'/);
assert.match(structure, /INSTALACION:\s*'yacimiento'/);
assert.match(structure, /SUBINSTALACION:\s*'instalacion'/);
assert.match(structure, /Departamento → Área → Yacimiento → Instalación → Subinstalación/);
});
test('F6.1 usa Área + Operadora como contexto de planificación y no mezcla Yacimiento con Área', () => {
const lifecycle = source('src/inspection-visits/inspection-visit-lifecycle.service.ts');
const visits = source('src/inspection-visits/inspection-visits.service.ts');
assert.match(lifecycle, /El contexto operativo de una Inspección es Área \+ Operadora vigente/);
assert.doesNotMatch(lifecycle, /Área\/Yacimiento/);
assert.match(visits, /FROM area_company_relations/);
assert.match(visits, /relation\.relation_role = 'OPERATOR'/);
});
test('F6.1 mantiene múltiples Actas por Inspección pero un solo borrador simultáneo', () => {
const act = source('src/database/entities/inspection-act.entity.ts');
const lifecycle = source('src/inspection-visits/inspection-visit-lifecycle.service.ts');
assert.match(act, /uq_inspection_acts_one_draft_per_visit/);
assert.match(act, /where: "status = 'DRAFT'"/);
assert.match(act, /Una Inspección puede acumular múltiples Actas/);
assert.match(lifecycle, /Todas las Actas deben estar SELLADAS antes de cerrar la Inspección/);
});
test('F6.1 permite Hallazgos directos sólo en Yacimiento, Instalación y Subinstalación y conserva OTROS', () => {
const field = source('src/inspection-visits/field-findings.service.ts');
const resolver = source('src/inspection-findings/f3-finding-catalog-resolver.service.ts');
assert.match(field, /\['yacimiento', 'instalacion', 'subinstalacion'\]\.includes\(assetTypeCode\)/);
assert.match(field, /assetTypeCode !== 'yacimiento' && !row\.inventoryFamilyId/);
assert.match(field, /canAddAnother: true/);
assert.match(resolver, /code: 'OTHER'/);
assert.match(resolver, /label: 'OTROS'/);
});
test('F6.1 abre Inspecciones desde Android usando el lifecycle canónico', () => {
const controller = source('src/inspection-visits/inspection-visits.controller.ts');
assert.match(controller, /@Post\('mobile\/open'\)/);
assert.match(controller, /leadInspectorUserId: principal\.userId/);
assert.match(controller, /this\.lifecycle\.plan\(created\.id/);
assert.match(controller, /this\.lifecycle\.start\(created\.id/);
});
test('F6.1 separa oficialización GEDO de la activación del vencimiento no urgente', () => {
const report = source('src/inspection-reports/inspection-report-workflow.service.ts');
const act = source('src/database/entities/inspection-act.entity.ts');
assert.match(report, /GEDO oficializa el INF, pero no equivale por sí solo a la notificación/);
assert.match(report, /deadlineActivation: 'PENDING_NOTIFICATION_EVENT'/);
assert.match(act, /NOTIFICATION_DATE = 'GEDO_DATE'/);
});
+91
View File
@@ -0,0 +1,91 @@
# Auditoría de consistencia · F6.1
**Baseline revisado:** `25a63cd3d4b2c561803cc5105cce100983c8eeff`
**Alcance:** API, modelo de datos, Inventarios, Inspecciones, Actas, Hallazgos, Informes/GEDO, WEB, Android, permisos, versionado documental y CI.
## Resultado
El modelo funcional consolidado de F6.1 es coherente. No se detectó una contradicción estructural que requiera rediseñar tablas o migrar datos antes de continuar.
La auditoría sí encontró deuda de terminología/documentación y una inconsistencia de metadata generada. Las correcciones funcionales/documentales se integran en esta rama; la metadata de lockfiles se documenta como observación no funcional y debe regenerarse con npm en un corte controlado, no editarse parcialmente a mano.
## Contratos verificados
### Inventario
- Jerarquía física única: Departamento → Área → Yacimiento → Instalación → Subinstalación.
- Empresa es un maestro independiente.
- Operadora vigente se resuelve por relación temporal Área ⇄ Empresa.
- Cambiar Operadora no reparenta Inventario.
- Departamento, Área y Yacimiento no llevan familia técnica.
- Instalación y Subinstalación requieren familia técnica.
- Subinstalación debe ser compatible con la familia de la Instalación padre.
- PostgreSQL protege jerarquía y compatibilidad, además de las validaciones de API.
### Inspecciones
- Lifecycle canónico DRAFT → PLANNED → IN_PROGRESS → CLOSED.
- Planificación requiere Área, Operadora vigente, Inspector responsable, fecha/hora y checklist.
- La APK abre una Inspección reutilizando el mismo create/plan/start; no existe un lifecycle móvil paralelo.
- No hay referencias activas a `plannedEndAt` / `planned_end_at` en el código actual.
### Actas
- Una Inspección puede contener múltiples Actas.
- El constraint histórico de una sola Acta por visita fue eliminado por F1.1.
- Existe un índice parcial que permite sólo un Acta DRAFT simultánea por Inspección.
- El cierre de la Inspección exige que todas las Actas no canceladas estén SELLADAS.
- El Acta finalizada/bloqueada es inmutable.
### Hallazgos
- Destinos directos: Yacimiento, Instalación y Subinstalación.
- Departamento y Área no son destinos directos.
- OTROS / Agregar otro permanece disponible.
- Yacimiento no requiere familia técnica.
- Instalación/Subinstalación sí requieren clasificación.
- Inventario nacido en campo necesita GPS + foto antes de admitir Hallazgos.
### Informes / GEDO
- El Informe pertenece al Acta.
- La oficialización GEDO/IF es inmutable.
- Oficializar en GEDO no activa por sí solo el vencimiento No urgente.
- El valor persistido histórico `GEDO_DATE` se conserva como ABI de base; en código existe el alias semántico `NOTIFICATION_DATE` para evitar interpretar GEDO como notificación automática.
## Correcciones integradas en la auditoría
1. Se corrigió la terminología residual `Área/Yacimiento` en el lifecycle de Inspecciones. El contexto operativo queda definido inequívocamente como **Área + Operadora vigente**.
2. Se documentó en código la separación entre Empresa y la jerarquía física del Inventario.
3. Se documentó en la entidad Acta el contrato multi-Acta + único DRAFT simultáneo.
4. Se agregó un test transversal F6.1 que protege jerarquía, contexto operativo, multi-Acta, Hallazgos, apertura móvil y separación GEDO/vencimiento.
5. Se consolidó `docs/F6_INVENTORY_MODEL.md` como única referencia viva y los duplicados históricos apuntan a ella.
6. Se crearon `MANUAL_PROGRAMADOR.md` y `MANUAL_USUARIO.md`.
## Compatibilidades históricas que NO deben “limpiarse” sin migración
- Migraciones antiguas conservan nombres y reglas que fueron válidas en fases anteriores. No se modifican; migraciones posteriores son las que expresan la evolución.
- `InspectionDeadlineBasis.GEDO_DATE` comparte valor persistido con `NOTIFICATION_DATE` por compatibilidad de esquema.
- `assets.operator_company_id` puede conservar una fotografía de compatibilidad incluso cuando la relación temporal ya finalizó. La fuente de verdad vigente/histórica sigue siendo `area_company_relations`.
- F5.1 es una migración destructiva de inicio limpio y no tiene `down()` reconstructivo; su rollback es el backup PRE.
## Observación no funcional: metadata de package-lock
Los `package.json` actuales declaran las versiones de producto vigentes, pero la metadata `name/version` de la raíz de ambos `package-lock.json` todavía conserva `0.20.0-2` de un corte anterior.
Esto no cambia el grafo de dependencias ni el runtime y `npm ci` continúa siendo la barrera de instalación. Se deja explícitamente registrado para no ocultarlo.
**Tratamiento correcto:** regenerar cada lockfile con npm (`npm install --package-lock-only`) cuando se haga el próximo corte controlado de metadata/dependencias y verificar el diff completo. No editar manualmente sólo las primeras líneas del lockfile.
## Criterio de aceptación de esta auditoría
Antes de mergear esta rama deben pasar nuevamente:
- API typecheck + tests + build.
- WEB typecheck + contrato + build.
- migraciones sobre PostGIS limpio.
- preflight aislado equivalente al VPS.
- build de imágenes productivas.
- Android assemble + unit tests cuando el workflow se dispare por el PR.
La rama de documentación/consistencia no debe promoverse a `deploy` durante esta revisión. El despliegue queda deliberadamente pausado.
+131 -1
View File
@@ -1 +1,131 @@
# F6 · Modelo definitivo de Inventarios
# F6.1 · Modelo canónico de Inventarios
Este documento es la referencia funcional y técnica del modelo de Inventarios de DH Inspección V2. Ante una contradicción con documentación histórica, prevalecen este documento, las migraciones vigentes y los contratos automatizados de F6.1.
## 1. Jerarquía física
La estructura territorial y física es única:
**Departamento → Área → Yacimiento → Instalación → Subinstalación**
Reglas:
- `Departamento` es raíz.
- `Área` requiere un Departamento padre.
- `Yacimiento` requiere un Área padre.
- `Instalación` requiere un Yacimiento padre.
- `Subinstalación` requiere una Instalación padre.
- La base de datos rechaza combinaciones de padre/hijo que no respeten esta secuencia.
- El campo `parent_id` expresa exclusivamente esta pertenencia física.
## 2. Empresa no pertenece al árbol físico
`Empresa` es un maestro independiente. No es padre de Departamento, Área, Yacimiento, Instalación ni Subinstalación.
La Operadora vigente de un Área se expresa mediante `area_company_relations`, con `relation_role = 'OPERATOR'` y vigencia temporal. Esta relación es la fuente de verdad para saber qué Empresa opera un Área en una fecha determinada.
`assets.operator_company_id` puede existir como fotografía de compatibilidad del contexto actual, pero no debe utilizarse para reconstruir la historia ni para reparentar Inventario.
**Cambiar de Operadora nunca mueve la jerarquía física.** Se finaliza la relación temporal anterior y se crea la nueva relación Empresa ⇄ Área.
## 3. Contexto transversal de Área
Los elementos bajo un Área conservan `operational_area_id` como contexto transversal para consultas, planificación y trazabilidad. El Yacimiento forma parte del alcance físico, pero no sustituye al Área como contexto operativo de una Inspección.
Por lo tanto, la planificación de una Inspección trabaja con:
**Área + Operadora vigente + Inspector responsable + fecha/hora de inicio**.
## 4. Clasificación técnica
Los niveles territoriales no usan clasificación técnica:
- Departamento: sin familia técnica.
- Área: sin familia técnica.
- Yacimiento: sin familia técnica.
Los niveles físicos sí la requieren:
- Instalación: una familia de nivel `INSTALLATION`.
- Subinstalación: una familia de nivel `SUBINSTALLATION` compatible con la familia de la Instalación padre.
La compatibilidad se almacena en `inventory_family_parent_rules` y se valida también en PostgreSQL. Una misma familia de Subinstalación puede ser compatible con varias familias de Instalación.
Los campos técnicos específicos de cada familia se definen en `inventory_family_attribute_definitions` y sus valores por Inventario en `asset_inventory_attribute_values`.
## 5. Hallazgos
Un Hallazgo puede asignarse directamente a:
- Yacimiento.
- Instalación.
- Subinstalación.
No se asignan Hallazgos directos a Departamento ni Área.
`OTROS / Agregar otro` debe estar siempre disponible. Permite describir un Hallazgo que no exista todavía en el catálogo aplicable y dejarlo registrado para revisión posterior.
Para Yacimiento, la aplicabilidad del catálogo puede resolverse por tipo de Inventario y por excepciones del objeto concreto. Yacimiento no necesita una familia técnica para recibir Hallazgos.
Instalación y Subinstalación deben estar clasificadas antes de recibir un Hallazgo.
## 6. Inventario nacido en campo
La APK puede incorporar Inventario encontrado durante una Inspección. Un registro creado en campo conserva su trazabilidad de origen y, antes de permitir Hallazgos sobre él, debe tener como mínimo:
- GPS capturado para la creación.
- al menos una fotografía de campo.
- clasificación técnica cuando se trate de Instalación o Subinstalación.
La excepción transitoria que permite crear un registro `FIELD_SURVEY + DRAFT` sin familia existe únicamente para completar la transacción de alta; no habilita Hallazgos sin clasificación.
## 7. Carga manual recomendada
El orden seguro para construir una estructura desde cero es:
1. Crear las Empresas necesarias como maestros independientes.
2. Crear Departamento.
3. Crear Área dentro del Departamento.
4. Registrar la Operadora vigente del Área, si ya se conoce.
5. Crear Yacimiento dentro del Área.
6. Crear Instalación dentro del Yacimiento y seleccionar su familia técnica.
7. Crear Subinstalación dentro de la Instalación y seleccionar una familia compatible.
La importación de archivos no debe definir la arquitectura del modelo. Los datos externos se adaptan a este contrato; el contrato no se deforma para adaptarse a cada planilla.
## 8. Historia y consulta temporal
DH conserva dos conceptos separados:
- historia física/contextual del Inventario;
- historia temporal de la relación Área ⇄ Empresa.
Los cambios relevantes generan versiones y eventos de auditoría. Las consultas históricas deben utilizar la vigencia correspondiente a la fecha consultada, no sólo la fotografía actual de las columnas de `assets`.
## 9. Barreras de integridad
La consistencia no depende sólo de la interfaz. Se protege en varias capas:
- DTO y servicios de API validan niveles y padres.
- PostgreSQL valida la jerarquía canónica.
- PostgreSQL valida nivel y compatibilidad de familias técnicas.
- la planificación valida una relación Operadora ⇄ Área vigente.
- los Hallazgos validan nivel permitido, clasificación técnica y pertenencia a la Inspección/Acta.
- CI ejecuta la cadena completa de migraciones sobre PostGIS limpio y los contratos transversales.
## 10. Corte limpio F5.1
`F51CleanManualInventory1790087400000` fue un corte intencional de inicio limpio. Eliminó datos operativos/importados de desarrollo y dejó los maestros técnicos necesarios para reconstruir manualmente una estructura confiable.
Es una migración deliberadamente no reversible mediante `migration:revert`. Su recuperación depende del backup PRE del despliegue. No debe copiarse este patrón para migraciones normales sin una decisión explícita y una estrategia de restauración probada.
## 11. Regla para cambios futuros
Cualquier modificación del modelo debe mantener simultáneamente:
1. la jerarquía física canónica;
2. Empresa fuera del árbol;
3. relación temporal Área ⇄ Empresa como fuente de verdad operativa;
4. compatibilidad técnica de familias;
5. trazabilidad histórica;
6. protección en base de datos y tests, no sólo en la UI.
+3 -5
View File
@@ -1,7 +1,5 @@
# F6 · Modelo definitivo de Inventarios
# F6 · Modelo de Inventarios
La jerarquía física canónica es fija:
Este archivo se conserva por compatibilidad con referencias históricas.
Departamento → Área → Yacimiento → Instalación → Subinstalación
Empresa es un maestro independiente y se relaciona temporalmente con Área.
La documentación canónica y mantenida está en [`F6_INVENTORY_MODEL.md`](./F6_INVENTORY_MODEL.md).
+3 -5
View File
@@ -1,7 +1,5 @@
# F6 · Modelo definitivo de Inventarios
# F6 · Modelo de Inventarios
La jerarquía física canónica es fija:
Este archivo se conserva por compatibilidad con referencias históricas.
Departamento → Área → Yacimiento → Instalación → Subinstalación
Empresa es un maestro independiente y se relaciona temporalmente con Área.
La documentación canónica y mantenida está en [`F6_INVENTORY_MODEL.md`](./F6_INVENTORY_MODEL.md).
+370
View File
@@ -0,0 +1,370 @@
# Manual del Programador · DH Inspección V2
**Baseline documental:** F6.1
**API:** 0.29.0-1
**WEB:** 0.23.0-1
**Android:** 0.15.0
Este manual describe la arquitectura, las reglas que no deben romperse y el procedimiento seguro para modificar DH Inspección V2. No reemplaza los tests ni las restricciones de base de datos: los tres forman juntos el contrato del sistema.
---
## 1. Estructura del repositorio
- `api-v3/`: API NestJS + TypeORM + PostgreSQL/PostGIS.
- `web-v2/`: aplicación WEB React/Vite.
- `android-app/`: aplicación Android Kotlin/Compose.
- `scripts/`: despliegue, preflight y utilidades operativas.
- `docs/`: documentación funcional y técnica viva.
- `.github/workflows/`: CI de API/WEB/Docker y build/test de Android.
El backend expone `/api/v3`. Los cambios de dominio deben resolverse primero en API/base de datos; WEB y Android consumen ese contrato y no deben inventar reglas paralelas.
---
## 2. Invariantes de dominio
### 2.1 Inventario físico
La jerarquía canónica es:
**Departamento → Área → Yacimiento → Instalación → Subinstalación**
`parent_id` expresa exclusivamente esa jerarquía. No agregue Empresa dentro de este árbol.
### 2.2 Empresa y Operadora
`Empresa` es un maestro independiente.
La relación operativa vigente se modela en `area_company_relations`:
- Área ↔ Empresa.
- `relation_role = 'OPERATOR'` para la Operadora.
- `valid_from` / `valid_until` para vigencia.
Esta tabla es la fuente temporal de verdad. `assets.operator_company_id` es una fotografía de compatibilidad; nunca debe usarse como sustituto del historial.
Cambiar Operadora no mueve Yacimientos, Instalaciones ni Subinstalaciones.
### 2.3 Clasificación técnica
- Departamento: sin familia.
- Área: sin familia.
- Yacimiento: sin familia.
- Instalación: requiere familia `INSTALLATION`.
- Subinstalación: requiere familia `SUBINSTALLATION` compatible con la Instalación padre.
La compatibilidad está protegida por PostgreSQL, no sólo por la UI.
### 2.4 Hallazgos
Un Hallazgo directo sólo puede asignarse a:
- Yacimiento.
- Instalación.
- Subinstalación.
Departamento y Área no reciben Hallazgos directos.
`OTROS / Agregar otro` debe permanecer disponible siempre. En Yacimiento no se exige familia técnica. En Instalación/Subinstalación sí.
Si un elemento fue creado en campo, antes de admitir un Hallazgo debe tener GPS de creación y al menos una fotografía; Instalación/Subinstalación además deben quedar clasificadas.
### 2.5 Inspecciones
Lifecycle operativo:
`DRAFT → PLANNED → IN_PROGRESS → CLOSED`
La planificación requiere:
- Área.
- Operadora vigente del Área.
- Inspector responsable habilitado.
- fecha/hora prevista de inicio.
- checklist generado.
La APK puede abrir una Inspección, pero no posee un lifecycle alternativo: el endpoint móvil crea la visita y utiliza los mismos servicios `plan()` y `start()` que el resto del sistema.
### 2.6 Actas
Una Inspección puede tener **múltiples Actas**.
Reglas críticas:
- sólo puede existir un `DRAFT` simultáneo por Inspección;
- un Acta en `DRAFT` es editable;
- al finalizar se bloquea y queda inmutable;
- el sellado conserva integridad y firmas;
- no existe reapertura normal de un Acta bloqueada a borrador;
- para cerrar la Inspección todas las Actas no canceladas deben estar `SEALED`;
- el Informe pertenece al Acta, no a la Inspección completa.
La unicidad del borrador está protegida por el índice parcial `uq_inspection_acts_one_draft_per_visit`.
### 2.7 Informe, GEDO y vencimientos
Flujo conceptual:
**Acta → INF → oficialización GEDO/IF**
GEDO oficializa el Informe, pero **no equivale por sí solo a la notificación administrativa que inicia el plazo No urgente**.
Existe una compatibilidad histórica importante: el valor físico `GEDO_DATE` se conserva en el enum/base, mientras el significado funcional vigente es `NOTIFICATION_DATE`. No cambie el valor persistido en una refactorización sin una migración explícita y pruebas de compatibilidad.
---
## 3. Modelo de Inventarios
La referencia completa es [`F6_INVENTORY_MODEL.md`](./F6_INVENTORY_MODEL.md).
Puntos técnicos centrales:
- `assets.parent_id`: pertenencia física.
- `assets.operational_area_id`: contexto transversal de Área.
- `area_company_relations`: historia de Operadora.
- `inventory_families`: clasificaciones técnicas.
- `inventory_family_parent_rules`: compatibilidad Subinstalación ↔ Instalación.
- `inventory_family_attribute_definitions`: campos técnicos por familia.
- `asset_inventory_attribute_values`: valores técnicos por objeto.
- `asset_context_history` y versiones: trazabilidad contextual/histórica.
No se debe rediseñar el esquema para coincidir con una planilla importada. Los archivos externos se normalizan al modelo canónico.
---
## 4. Migraciones
### Regla principal
**Nunca modifique una migración ya aplicada.**
Si cambia esquema, constraints, seeds o reglas persistentes:
1. cree una migración nueva;
2. haga `up()` determinista;
3. agregue verificación al final cuando la regla sea crítica;
4. implemente `down()` cuando la operación sea razonablemente reversible;
5. agregue/actualice tests de contrato;
6. pruebe la cadena completa sobre PostGIS limpio.
### F5.1 es una excepción histórica
`F51CleanManualInventory1790087400000` fue un corte destructivo intencional para reiniciar los datos de dominio. Su `down()` no intenta reconstruir información eliminada: el rollback depende del backup PRE.
No repita este patrón para una migración normal sin una decisión explícita, backup validado y procedimiento de restauración.
### Guards importantes de base de datos
PostgreSQL protege, entre otras cosas:
- jerarquía Departamento → Área → Yacimiento → Instalación → Subinstalación;
- familia correcta según el nivel físico;
- compatibilidad de familia de Subinstalación con su Instalación padre;
- un solo Acta `DRAFT` por Inspección.
Las validaciones de API mejoran la experiencia, pero no reemplazan estos guards.
---
## 5. API
### Principios
- Reutilice servicios canónicos; no duplique lifecycle en controllers móviles.
- Las mutaciones relevantes deben producir auditoría.
- Los errores de dominio deben tener `code` estable y mensaje legible.
- Los estados históricos/inmutables no deben reabrirse por conveniencia de UI.
- Para relaciones temporales, valide la vigencia en la fecha correspondiente, no sólo `valid_until IS NULL` cuando el caso exige reconstrucción histórica.
### Áreas a revisar antes de tocar un flujo
- Inventario: `src/asset-master/`.
- Inspecciones: `src/inspection-visits/`.
- Actas: `src/inspection-acts/`, `src/inspection-closing/`, `src/act-administration/`.
- Hallazgos: `src/inspection-findings/` y gate móvil en `src/inspection-visits/field-findings.service.ts`.
- Informes/GEDO: `src/inspection-reports/`.
- Verificaciones: `src/inspection-verifications/`.
- Entidades/migraciones: `src/database/`.
---
## 6. WEB
La WEB es el frente principal de administración, planificación y seguimiento. Las rutas protegidas se declaran en `web-v2/src/app/App.tsx` y usan permisos para controlar acceso.
Módulos principales:
- Dashboard.
- Mapa.
- Inventarios y dossier.
- Revisión de Inventario nacido en campo.
- Inspecciones y preparación de campo.
- Actas.
- Hallazgos y planificación de verificaciones.
- Seguimiento de Actas.
- Informes.
- Historial y consulta temporal.
- Administración de usuarios, roles, auditoría, tipos y catálogo.
No esconda una regla sólo en la WEB: toda restricción de integridad debe existir también en API/DB.
---
## 7. Android
La APK está orientada a ejecución en campo.
Reglas que deben mantenerse:
- sólo un usuario con capacidad de ejecución puede iniciar/operar una Inspección;
- `Abrir inspección` selecciona Área y Operadora vigente;
- la nueva visita queda autoasignada al inspector actual;
- se genera checklist antes de iniciar;
- el backend recorre DRAFT → PLANNED → IN_PROGRESS con el lifecycle canónico;
- Inventario nuevo en campo captura evidencia mínima antes de Hallazgos;
- Hallazgos respetan los mismos niveles y catálogo que la API.
Al cambiar contratos móviles, actualice tanto Kotlin como los tests de contrato montados por el preflight de API.
---
## 8. Permisos y seguridad
El sistema utiliza permisos finos; las pantallas no deben asumir permisos por nombre visual del rol. La fuente efectiva es la matriz rol-permiso de base de datos.
Buenas prácticas:
- proteja controllers con permisos explícitos;
- no confíe en que una ruta esté oculta en la WEB;
- preserve la política especial del inspector móvil;
- mantenga auditadas las mutaciones administrativas;
- no exponga secretos ni credenciales en repositorio, logs o documentación.
---
## 9. Tests y CI
La barrera principal está en `.github/workflows/ci.yml`.
### API
Ejecuta:
```bash
npm ci
npm run typecheck
npm test
npm run build
```
### WEB
Ejecuta:
```bash
npm ci
npm run typecheck
bash ../scripts/check-f3-1-web-contract.sh
npm run build
```
### Docker / contratos
Valida:
- sintaxis de scripts shell;
- paridad del preflight de deploy;
- `docker compose config`;
- cadena de migraciones sobre PostGIS limpio;
- estado limpio F5.1 y preservación de maestros técnicos;
- segunda ejecución sin migraciones pendientes;
- preflight API aislado equivalente al VPS;
- build de imágenes productivas.
### Android
El workflow específico de Android debe completar:
- `assembleDebug`;
- unit tests;
- publicación del artefacto APK.
### Contrato transversal F6.1
`api-v3/test/unit/f6-1-project-invariants.test.ts` fija las reglas que más fácilmente podrían degradarse por refactors: jerarquía, contexto Área+Operadora, multi-Acta, Hallazgos, apertura móvil y separación GEDO/vencimiento.
---
## 10. Versionado
Cuando una entrega cambia versión, revise como mínimo:
- `api-v3/package.json`;
- `api-v3/src/version.ts`;
- `web-v2/package.json`;
- `android-app/app/build.gradle.kts` si cambia APK;
- tests que fijen metadata de versión;
- metadata raíz de `package-lock.json` regenerándola con npm cuando corresponda.
Los lockfiles son archivos generados: no los edite manualmente en forma parcial. Use npm para reconciliarlos.
---
## 11. Flujo Git y despliegue
Secuencia recomendada:
1. crear rama desde `main` estable;
2. implementar código + migración + tests + documentación;
3. abrir PR;
4. exigir API + WEB + Docker/migraciones + Android verdes cuando aplique;
5. mergear a `main`;
6. exigir nuevamente CI verde sobre el SHA de `main`;
7. recién entonces promover el SHA exacto a `deploy`;
8. esperar el watcher/deploy;
9. comprobar SHA objetivo, health, versiones y backup generado.
Nunca despliegue un SHA distinto al que pasó la barrera. Nunca use `deploy` para “probar” una rama incompleta.
Si una migración es de una sola vía, el rollback operativo es el backup PRE validado, no un `migration:revert` improvisado.
---
## 12. Checklist antes de aprobar un cambio
- ¿Respeta la jerarquía canónica?
- ¿Empresa sigue fuera del árbol físico?
- ¿Una modificación de Operadora conserva historia temporal?
- ¿La regla importante está protegida también en API/DB?
- ¿Hallazgos siguen limitados a Yacimiento/Instalación/Subinstalación?
- ¿OTROS sigue disponible?
- ¿Una Inspección puede tener varias Actas y sólo un DRAFT simultáneo?
- ¿Actas finalizadas siguen inmutables?
- ¿Informe sigue perteneciendo al Acta?
- ¿GEDO sigue separado del inicio automático del plazo No urgente?
- ¿WEB y Android consumen el mismo contrato?
- ¿Se agregó un test para la nueva regla o regresión?
- ¿CI completa está verde?
- ¿Documentación viva quedó actualizada?
---
## 13. Terminología canónica
Use consistentemente:
- **Inventario**, no “Activo” en la interfaz de usuario cuando se refiere al módulo funcional.
- **Departamento**.
- **Área**.
- **Yacimiento**.
- **Instalación**.
- **Subinstalación**.
- **Empresa / Operadora** como maestro/rol operativo, no como padre físico.
- **Inspección** para la visita operativa.
- **Acta** para el documento de campo; una Inspección puede contener varias.
- **Hallazgo** para la observación fiscalizable asociada a Inventario.
- **Informe / INF** por Acta.
- **GEDO / IF** para la oficialización documental, separada de la notificación administrativa cuando corresponda.
Si una etiqueta histórica contradice estos términos, corrija la etiqueta; no cambie el modelo para conservar una denominación obsoleta.
+353
View File
@@ -0,0 +1,353 @@
# Manual de Usuario · DH Inspección V2
**Versión funcional de referencia:** F6.1
Este manual explica el uso cotidiano del sistema. Las opciones visibles dependen de los permisos asignados a cada usuario, por lo que un Inspector, Supervisor, Director o Administrador puede ver acciones distintas.
---
## 1. Conceptos principales
DH Inspección V2 organiza el trabajo alrededor de cuatro elementos:
1. **Inventario:** dónde se inspecciona.
2. **Inspección:** la visita de trabajo.
3. **Acta:** documento generado durante la Inspección; una Inspección puede tener varias Actas.
4. **Hallazgo:** observación asociada a un elemento concreto del Inventario.
Además, cada Acta puede continuar con su **Informe (INF)**, oficialización documental y seguimiento posterior.
---
## 2. Ingreso al sistema
1. Abra la WEB de DH Inspección V2.
2. Ingrese usuario y contraseña.
3. El menú mostrará solamente los módulos habilitados por sus permisos.
Si una opción no aparece o el sistema informa que no tiene autorización, consulte al Administrador antes de intentar resolverlo creando datos alternativos.
---
## 3. Cómo se organiza el Inventario
La estructura física correcta es siempre:
**Departamento → Área → Yacimiento → Instalación → Subinstalación**
La **Empresa** no forma parte de esa cadena. Se administra por separado y se vincula al Área cuando actúa como Operadora.
Ejemplo:
- Departamento: Malargüe
- Área: Área A
- Yacimiento: Yacimiento Norte
- Instalación: Planta de Tratamiento
- Subinstalación: Separador 01
Una Empresa puede cambiar con el tiempo sin modificar esta estructura física.
---
## 4. Carga manual de Inventario
Para construir una estructura confiable desde cero, use este orden:
1. Cree las **Empresas** necesarias.
2. Cree el **Departamento**.
3. Cree el **Área** dentro del Departamento.
4. Registre la **Operadora vigente** del Área si corresponde.
5. Cree el **Yacimiento** dentro del Área.
6. Cree la **Instalación** dentro del Yacimiento.
7. Seleccione la clasificación técnica de la Instalación.
8. Cree la **Subinstalación** dentro de la Instalación.
9. Seleccione una clasificación técnica compatible para la Subinstalación.
El sistema no permite saltear niveles. Por ejemplo, una Instalación no puede depender directamente de un Área.
### Empresa y Operadora
Cuando una Empresa opera un Área, registre la relación operativa correspondiente. Si cambia la Operadora:
1. finalice la relación vigente anterior;
2. registre la nueva Operadora;
3. no mueva Yacimientos, Instalaciones ni Subinstalaciones.
El historial queda conservado para consultas temporales.
---
## 5. Consultar Inventario
Desde **Inventarios** puede navegar la estructura territorial y abrir el dossier de cada registro.
Utilice el breadcrumb para volver hacia niveles superiores. El dossier concentra, según la información disponible:
- datos actuales;
- ubicación y clasificación;
- fotografías;
- historial de cambios;
- Hallazgos;
- verificaciones;
- Actas e Informes relacionados.
Cuando necesite saber cómo estaba un elemento en una fecha anterior, use la **Consulta temporal** en lugar de interpretar sólo sus datos actuales.
---
## 6. Crear una Inspección desde la WEB
En **Inspecciones → Nueva Inspección**:
1. seleccione el **Área**;
2. seleccione una **Operadora vigente** para esa Área;
3. seleccione el **Inspector responsable**;
4. indique la fecha y hora prevista de inicio;
5. confirme la creación.
Al crearse la Inspección, el sistema genera su checklist de antecedentes. Antes de planificar, la información obligatoria debe estar completa.
Una Inspección planificada se inicia operativamente desde la APK por un Inspector habilitado y asignado.
---
## 7. Abrir una Inspección directamente desde la APK
La APK también permite comenzar una Inspección que no haya sido preparada previamente en la WEB.
Desde la pantalla principal:
1. pulse **Abrir inspección**;
2. elija el **Área**;
3. elija la **Operadora vigente**;
4. pulse **Abrir inspección ahora**.
El sistema:
- crea la Inspección;
- la asigna al Inspector que inició sesión;
- genera su checklist;
- la planifica;
- la deja **En curso**.
No elija una Empresa por aproximación: la APK muestra la relación operativa válida del Área.
---
## 8. Trabajar con Inventario durante una Inspección
Dentro de una Inspección en curso puede seleccionar Inventario existente del Área y agregarlo al trabajo de campo/Acta correspondiente.
Los niveles relevantes para Hallazgos son:
- Yacimiento;
- Instalación;
- Subinstalación.
### Inventario encontrado en campo
Si encuentra una Instalación o Subinstalación que todavía no existe, la APK puede darla de alta dentro de la jerarquía correspondiente.
Antes de poder registrar Hallazgos sobre un elemento nuevo de campo, debe quedar como mínimo con:
- GPS de creación;
- al menos una fotografía;
- clasificación técnica si es Instalación o Subinstalación.
Esto evita que un Hallazgo quede asociado a un objeto sin identificación suficiente.
---
## 9. Actas: una Inspección puede tener varias
Una misma Inspección puede generar **varias Actas**.
La regla práctica es:
- puede haber varias Actas históricas dentro de la Inspección;
- sólo puede existir **un Acta en borrador al mismo tiempo**.
Mientras un Acta está en borrador puede completar su contenido y agregar Hallazgos. Una vez finalizada/bloqueada deja de ser editable como un borrador normal.
Para continuar con una nueva Acta dentro de la misma Inspección, finalice correctamente la actual antes de abrir la siguiente.
---
## 10. Registrar Hallazgos
Los Hallazgos se asignan directamente a:
- **Yacimiento**;
- **Instalación**;
- **Subinstalación**.
No se crean Hallazgos directos sobre Departamento ni Área.
### Hallazgo de catálogo
Si el sistema muestra un Hallazgo aplicable al elemento seleccionado:
1. elija el Hallazgo del catálogo;
2. complete la descripción y datos requeridos;
3. guarde.
### OTROS / Agregar otro
La opción **OTROS** debe estar siempre disponible.
Úsela cuando el Hallazgo observado no exista en la lista disponible. Describa claramente qué se encontró para que luego pueda revisarse y normalizarse en oficina.
Después de guardar un Hallazgo puede utilizar **Agregar otro** para continuar cargando Hallazgos sobre el mismo Inventario.
### En Yacimiento
Un Yacimiento puede recibir Hallazgos aunque no tenga clasificación técnica. Cuando exista catálogo configurado para ese tipo, el sistema puede ofrecerlo; en todos los casos conserva la opción OTROS.
---
## 11. Finalizar y sellar un Acta
Antes de finalizar un Acta revise especialmente:
- Inventario incluido;
- Hallazgos;
- observaciones;
- firmas y datos requeridos por el flujo.
Al finalizar, el sistema bloquea el contenido y genera controles de integridad. Esto significa que **el Acta deja de ser editable como borrador**.
No finalice un Acta para “probar” cómo queda. Primero revise todo su contenido.
Cuando el flujo de firmas y sellado termina, el Acta queda `SELLADA` y preparada para continuar con su documentación posterior.
---
## 12. Cerrar una Inspección
Para cerrar una Inspección en curso deben cumplirse las condiciones operativas, entre ellas:
- existir al menos un Acta;
- todas las Actas no canceladas deben estar selladas;
- si la Inspección contiene verificaciones, sus resultados deben estar registrados.
Si el sistema impide el cierre, revise el mensaje mostrado: normalmente identifica el Acta o la verificación pendiente.
---
## 13. Informes por Acta
El Informe se genera y administra **por Acta**.
Por eso, si una Inspección tuvo tres Actas, el seguimiento documental no debe tratarla como si existiera un único Informe global.
Desde **Informes** puede consultar el estado del INF y continuar el flujo habilitado para su rol.
---
## 14. GEDO / IF y plazo No urgente
Registrar el identificador y PDF oficial de GEDO/IF **oficializa el Informe**, pero por sí solo no significa que haya comenzado automáticamente el plazo administrativo de una Acta No urgente.
El vencimiento debe activarse a partir del evento administrativo de notificación que corresponda al procedimiento vigente.
Si ve un Informe oficializado sin fecha de vencimiento activa, no lo corrija cargando una fecha ficticia de GEDO como notificación.
---
## 15. Seguimiento de Hallazgos
Después del trabajo de campo, un Hallazgo mantiene su historia. El seguimiento puede incluir:
- respuesta de la Empresa;
- documentos y evidencias;
- fecha de control;
- planificación de una verificación;
- resultado de verificación;
- nueva fecha si continúa pendiente;
- cierre administrativo.
No reescriba el Hallazgo original para reflejar novedades posteriores. Use los eventos de seguimiento para conservar la trazabilidad.
---
## 16. Verificaciones
Cuando un Hallazgo requiere control posterior:
1. defina la fecha de verificación cuando corresponda;
2. planifique la visita de verificación;
3. ejecute la verificación;
4. registre el resultado y evidencia;
5. si no está resuelto, reprograme según el flujo;
6. si está resuelto y la visita quedó cerrada, continúe con el cierre administrativo del Hallazgo.
El historial de verificaciones no debe borrarse para reemplazarlo por el último resultado.
---
## 17. Historial y Consulta temporal
Use **Historial** para revisar cambios y eventos registrados.
Use **Consulta temporal** cuando la pregunta sea, por ejemplo:
- “¿qué Empresa operaba esta Área en esa fecha?”;
- “¿dónde estaba ubicado este elemento?”;
- “¿qué estado tenía el Inventario en ese momento?”.
La vista actual y la vista histórica cumplen funciones distintas.
---
## 18. Revisión de Inventario nacido en campo
Los registros creados por Inspectores en la APK pueden requerir revisión posterior en oficina.
Al revisar:
- confirme nombre/código;
- confirme la ubicación jerárquica;
- verifique clasificación técnica;
- revise GPS/fotografías;
- normalice información pendiente sin perder el origen de campo.
No cree un duplicado sólo porque el nombre no coincide exactamente; primero confirme si se trata del mismo objeto.
---
## 19. Mensajes frecuentes
### “La Operadora seleccionada no tiene una relación operativa vigente con el Área”
Revise la relación Empresa ⇄ Área y su vigencia. No cambie la jerarquía física para resolverlo.
### “La Instalación/Subinstalación debe tener una clasificación técnica”
Complete la familia técnica del elemento antes de registrar el Hallazgo.
### “El Inventario creado en campo debe tener GPS y al menos una foto”
Complete la captura de campo requerida y vuelva a intentar.
### “Todas las Actas deben estar SELLADAS antes de cerrar la Inspección”
Abra la Inspección y revise cuáles Actas todavía están en borrador, bloqueadas o pendientes de sellado.
### “La inspección no tiene un Acta borrador abierta”
Cree/abra un Acta borrador para esa Inspección antes de cargar nuevos Hallazgos.
---
## 20. Buenas prácticas
- Cargue la estructura de Inventario en orden jerárquico.
- No use Empresa como padre físico.
- No duplique Inventario ante la primera diferencia de nombre.
- Use OTROS cuando el Hallazgo no exista en catálogo.
- Revise un Acta antes de finalizarla: luego queda inmutable.
- Use seguimiento y verificaciones para agregar historia; no reescriba hechos anteriores.
- Use Consulta temporal para preguntas históricas.
- Respete los mensajes de integridad del sistema en lugar de forzar datos ficticios.
Ante una duda funcional que pueda modificar jerarquía, Actas, Hallazgos o vencimientos, consulte antes de cargar datos masivamente.