Saltar al contenido principal

Calibración y tablas de aforo

Las tablas de aforo (strapping tables) traducen la altura medida del líquido en un volumen, y son el corazón de cualquier cálculo de custodia: si la tabla está mal o no es trazable, el volumen no sirve. Esta página documenta el flujo 6 paso a paso — desde la calibración del dispositivo (con su cadena HMAC y certificado SENCAMER) hasta la importación, revisión, comparación y publicación con separación de funciones (SoD) de una tabla de aforo.

Precondición y roles

Quién importa y quién publica

El perfil ingeniero (engineering:edit) importa tablas de aforo, ve las versiones y compara los diffs. La publicación (engineering:publish) la hace un ingeniero o un supervisor. La regla de oro de control interno se aplica en la publicación: el publicador no puede ser el mismo usuario que hizo la importación (separación de funciones — SoD).

Las rutas de este flujo cuelgan del detalle del tanque:

RutaPara qué
/tanks/[id]/strapping/importAsistente de importación (wizard de 4 pasos).
/tanks/[id]/strapping/versionsLista de versiones de la tabla de aforo del tanque.
/tanks/[id]/strapping/versions/[v]Booklet (libreta de aforo) de una versión.
/tanks/[id]/strapping/versions/[v]/diff/[to]Comparación entre dos versiones.

Calibración del dispositivo (cadena HMAC)

Antes de hablar de la tabla de aforo conviene situar la calibración del dispositivo, porque ambas alimentan la trazabilidad de grado custody. Desde la pestaña Calibración del detalle de un dispositivo, registrar una calibración sube el PDF del certificado SENCAMER a MinIO y crea un evento inmutable: TankOS calcula el hash SHA-256 del PDF y, sobre una cadena canónica que enlaza cada evento con el anterior (prevEventHash), un HMAC-SHA256 encadenado. El resultado es un historial append-only y a prueba de manipulación.

La cadena de calibración es inmutable

A nivel de base de datos se revocaron las operaciones UPDATE y DELETE sobre los eventos de calibración: una vez registrado, un evento no se puede modificar ni borrar. Cualquier intento de alterar la historia rompe la cadena HMAC y queda en evidencia. Esto sostiene la trazabilidad exigida para transferencia custody.

Calibración del dispositivo vs. tabla de aforo

La calibración del dispositivo (el certificado del radar) y la tabla de aforo (la geometría del tanque traducida a volumen) son cosas distintas. El detalle completo de la cadena HMAC del dispositivo está en dispositivos y radares; aquí nos centramos en la tabla de aforo.

Captura pendiente

Screenshot pendiente del barrido diferido — ver 61-DEFERRED-SWEEP.md (pestaña Calibración con el historial de la cadena HMAC y la carga del certificado).

Importar una tabla de aforo (wizard de 4 pasos)

La importación de una tabla de aforo es un asistente de cuatro pasos en /tanks/[id]/strapping/import (StrappingImportWizard). Cada paso vive en la URL (?step=upload|confirm|preview|commit):

PasoQué hacesDetalle
1. SubirCargas la tabla (.csv, .xlsx, .json) y, opcionalmente, el PDF del certificado (máx. 2 archivos, 50 MB en total).Se registra un StrappingImportAttempt en estado dry_run; el servicio intenta detectar el formato automáticamente.
2. ConfirmarRevisas los chips detectedForm y detectedLocale y la confianza de la detección; si hace falta, sobreescribes la forma de almacenamiento y el denominador fraccionario.storageForm: flat (nivel→volumen), main_fractional (pies + fracción) o incremental_factors (factores incrementales). declaredFractionalDenominator: 8 (1/8") o 16 (1/16") — crítico para la interpolación SC#1 MPMS.
3. Vista previaExaminas la StrappingErrorTable con los diagnósticos del dry-run: filas parseadas, advertencias y errores.Las filas con error se marcan en rojo. El botón Continuar queda deshabilitado si la confianza es < 0,8 sin override explícito, o si hay errores en el dry-run.
4. CommitConfirmas la importación.El servicio verifica que el SHA-256 del archivo coincide con el del dry-run, crea la fila StrappingTable con effectiveFrom = NULL (= pendiente de publicación) y pasa el intento a committed.
El commit crea una versión pendiente, no la activa

Importar y confirmar no pone la tabla en producción. La nueva versión nace en estado pendiente (effectiveFrom = NULL); recién se vuelve activa cuando alguien la publica — y esa publicación tiene su propia puerta de SoD (más abajo).

Captura pendiente

Screenshot pendiente del barrido diferido — ver 61-DEFERRED-SWEEP.md (los 4 pasos del asistente de importación de strapping: Subir, Confirmar, Vista previa con StrappingErrorTable, Commit).

Lista de versiones

La lista de versiones (/tanks/[id]/strapping/versions) muestra todas las tablas de aforo del tanque con su número de versión, estado (badge activa / pendiente / sustituida), vigencia (desde / hasta) y quién la creó. Desde aquí se lanza una nueva importación, se publica una versión pendiente y se accede al diff entre versiones.

Versiones de tabla de aforo

Las filas en estado pendiente ofrecen el botón Publicar (un Server Action inline); el enlace ver diff abre la comparación con otra versión.

Booklet de la tabla de aforo

El booklet de una versión (/tanks/[id]/strapping/versions/[v]) presenta la tabla de aforo como una libreta de aforo estilo recibo, en grilla mono de ancho fijo, igual que la documentación de calibración impresa.

Booklet de la tabla de aforo

El booklet se compone de:

  • Meta-strip de provenance (12 celdas): metadatos de origen de la tabla (quién importó, cuándo, hashes).
  • Tabla principal en grilla mono de ancho fijo, en una de las tres formas de almacenamiento: main + fractional, flat o incremental_factors.
  • Panel de ejemplo trabajado: una interpolación de muestra para ilustrar la lectura de la tabla.
  • Folds colapsables: critical_zone, bottom_table y roof_correction.
  • Footer de provenance (8 celdas): payload_hash y metadatos de importación y publicación.
Las unidades del booklet están fijadas por régimen

El booklet nunca usa las preferencias del usuario para las alturas: el régimen del tanque manda. Régimen SI ⇒ milímetros y metros cúbicos; régimen US-MPMS ⇒ pies-pulgadas-fracción y barriles. Esto evita que una preferencia de visualización contamine la lectura de un documento de calibración.

Comparar versiones (diff viewer)

El diff viewer (/tanks/[id]/strapping/versions/[v]/diff/[to]) compara dos versiones de la tabla y se organiza en tres tarjetas:

  1. Metadatos modificados — lista de los campos de cabecera que cambiaron entre versiones.
  2. Gráfico ECharts — un overlay de volumen vs. altura de ambas versiones, para ver la divergencia de la curva de un vistazo.
  3. Tabla de buckets delta — diez tramos con su Δm³ y Δ%; los buckets con cambios significativos llevan un acento de advertencia (no bloqueante).

El diff es la herramienta para justificar una recalibración antes de publicarla: muestra exactamente en qué tramos del tanque cambia el volumen.

Captura pendiente

Screenshot pendiente del barrido diferido — ver 61-DEFERRED-SWEEP.md (diff viewer: metadatos, gráfico ECharts de volumen vs. altura y tabla de buckets delta).

Publicar una versión (puerta SoD)

Publicar es el paso que pone una versión en producción y cierra la versión activa anterior. Es también donde se aplica la regla de control interno más importante de este flujo.

  1. Desde la lista de versiones, el botón Publicar de una fila pendiente lanza el Server Action (POST /tanks/:id/strapping/versions/:v/publish, autoridad engineering:publish).
  2. El servicio serializa las publicaciones concurrentes con SELECT FOR UPDATE.
  3. Si ya existe una versión activa, le pone effectiveTo = NOW() (la cierra) antes de activar la nueva.
  4. La nueva versión recibe effectiveFrom = NOW() y pasa a ser la activa del tanque.
  5. Se escribe una fila de auditoría con action = 'PUBLISH'.
Separación de funciones: el publicador ≠ el importador

El usuario que hizo el commit de la importación no puede ser quien publica esa misma versión. El servidor lo verifica con assertSeparationOfDuties: si coinciden, la operación falla con SeparationOfDutiesErrorHTTP 403. No existe bypass (el código SOD_BYPASS_AUTHORITY se retiró en el Plan 06-10). En la lista de versiones, una violación de SoD se muestra como un error inline con acento rojo. La consecuencia práctica: la importación y la publicación de una tabla de aforo siempre las firman dos personas distintas.

La versión publicada es inmutable

Una vez publicada, el effectiveFrom de la versión es inmutable (lo garantiza un trigger de base de datos): solo se puede cerrar la versión (poniéndole effectiveTo) al publicar otra. Nunca se edita una tabla activa "en caliente".

Estados de la tabla de aforo

Una tabla de aforo atraviesa tres estados a lo largo de su vida:

EstadoeffectiveFromeffectiveToSignificado
PendienteNULLNULLImportada y confirmada, pero aún no publicada. No participa en cálculos.
ActivatimestampNULLPublicada y vigente. Es la tabla que usa el motor de cálculo.
HistóricatimestamptimestampFue activa, y luego una publicación posterior la cerró.
Una sola tabla activa por tanque

En todo momento existe como máximo una versión activa por tanque (lo garantizan el SELECT FOR UPDATE de la publicación y la restricción de unicidad @@unique([tankId, version])). Por convención, las versiones se retienen 5 años.

Páginas relacionadas

  • El detalle de la calibración del dispositivo (cadena HMAC, certificado, pestañas) está en dispositivos y radares.
  • El acceso a la tabla de aforo activa desde la configuración del tanque está en la pestaña Tablas de aforo del editor de tanque.
  • Las plantillas de strapping compartidas entre tanques se gestionan en catálogos.