Saltar al contenido principal

Gauging tickets

El gauging ticket (/gauging) es el documento custody-grade que agrupa un dip completonivel, temperatura y agua libre— en un único registro auditable. A diferencia del aforo puntual, el ticket trata sus tres lecturas como una unidad indivisible: se crean, se envían y se aprueban todas juntas o ninguna. Esta página está orientada al operador (que captura y envía) y al supervisor (que aprueba, rechaza o anula): cómo registrar y aprobar un ticket paso a paso.

Qué NO es esta página

El gauging ticket comparte la máquina de estados del aforo manual, pero agrupa varias lecturas como un documento atómico. No debe confundirse con:

  • el aforo manual, que registra una sola cantidad por vez (sin el contrato atómico de todo-o-nada del ticket); ni con
  • la muestra de laboratorio, que aporta la densidad y el BSW —propiedades que el ticket nunca captura (MAN-07)—; ni con
  • el custody batch de transferencia (lote de movimiento custody), que tiene su propio ticket correlativo gap-free y se documenta aparte (próximamente).
El number del ticket NO es el correlativo gap-free de custodia

Al crear el ticket, el sistema le asigna un number humano legible mediante una secuencia simple. No es el contador correlativo gap-free (sin huecos) de los tickets de transferencia custody — ese vive en el módulo de Custodia y obedece reglas distintas. El number del gauging ticket solo sirve para identificarlo de forma legible.

Quién hace qué

RolCrear / enviar (manual:submit)Aprobar / rechazar (manual:approve)Anular (manual:void)
Operador
Supervisor

El operador captura el dip completo y envía el ticket. El supervisor lo aprueba o rechaza desde la cola de aprobación, y es el único que puede anular un ticket ya aprobado.

El ciclo de vida (5 estados)

El gauging ticket reutiliza el mismo reductor de 5 estados del aforo manual: DRAFT, SUBMITTED, APPROVED, REJECTED, VOIDED. La diferencia clave es que cada transición se aplica al header y a todas sus filas en una sola transacción (ver Header atómico all-or-none más abajo).

Estado origenAcciónEstado destinoAutoridadGuardia / SoD
DRAFTEnviarSUBMITTEDmanual:submit
SUBMITTEDAprobarAPPROVEDmanual:approveSoD: actor ≠ operador del ticket
SUBMITTEDRechazarREJECTEDmanual:approveSoD: actor ≠ operador del ticket
APPROVEDAnularVOIDEDmanual:voidvoidActorGuard: supervisor + SoD

El diagrama completo de transiciones se documenta en la página de aforos manuales (mismo reductor); aquí basta la tabla compacta. Cualquier otro par (estado, acción) devuelve INVALID_TRANSITION.

Header atómico all-or-none

Este es el concepto custody-grade central del gauging ticket. Un ticket es un header GaugingTicket con varias filas ManualReading (una por cantidad: nivel, temperatura, agua libre). El sistema las trata como una sola unidad:

  • Al crear el ticket, el servicio escribe el header GaugingTicket + todas las filas ManualReading atomicamente —en una única transacción de base de datos—. Si algo falla, no queda ni el header ni las filas.
  • Al enviar (DRAFT → SUBMITTED), la transición se aplica al header y a todas las filas en una sola transacción.
  • Al aprobar (SUBMITTED → APPROVED), la aprobación es all-or-none: el header y todas las filas pasan a APPROVED en una sola transacción, después de validar todas las filas con el reductor antes de escribir (validate-all-before-write).
No existen tickets parcialmente aprobados

Un ticket con el header en APPROVED pero alguna fila aún en SUBMITTED —o al revés— sería "custody-nonsense" (D-09): un documento de custodia que afirma cosas contradictorias. Por eso la aprobación es all-or-none y se valida todo antes de escribir nada. O entran las tres lecturas, o no entra ninguna.

Paso a paso

1. Capturar el dip completo (DRAFT)

Desde el CTA "Nuevo aforo" se abre /gauging/new, sección S1 — Aforo (formulario de dip estándar MPMS 3.1A). El operador elige el tanque, la fecha/hora de lectura (readingTs) y captura las tres cantidades en una sola acción:

CantidadCampoUnidad (SI)
Nivellevelmm
Temperaturatemperature°C
Agua librefreeWatermm

El formulario compone internamente un array readings: [{quantity:'level'}, {quantity:'temperature'}, {quantity:'freeWater'}] que viaja en un solo POST.

La densidad y el BSW NUNCA se capturan en el ticket

El gauging ticket agrupa solo lo que se mide físicamente en el tanque (nivel, temperatura, agua libre). La densidad y el BSW vienen del laboratorio (MAN-07) — se registran y aprueban en la muestra de laboratorio, no aquí.

Validación de la fecha de lectura

readingTs no puede ser futura. Los valores se almacenan en unidades SI (mm para nivel y agua libre, °C para temperatura).

2. Crear el ticket (DRAFT — header + filas atómico)

Al confirmar, el sistema crea el header GaugingTicket + todas las filas ManualReading atomicamente (ver Header atómico all-or-none) y le asigna el number humano. El ticket nace en estado DRAFT.

3. Enviar (DRAFT → SUBMITTED)

Al enviar, el ticket pasa a SUBMITTED —header y todas las filas, en una sola transacción— y entra en la cola de aprobación del supervisor.

4. Cola de aprobación

El supervisor ve los tickets SUBMITTED en su cola (/gauging/approval). Solo accede quien tiene la autoridad manual:approve.

5. Aprobar o rechazar (SUBMITTED → APPROVED | REJECTED)

El supervisor apruebaall-or-none, todas las filas a la vez— o rechaza el ticket, sujeto a la separación de funciones (ver abajo). Un rechazo exige una razón (mínimo 10 caracteres).

6. Anular (APPROVED → VOIDED)

Un ticket aprobado se anula —vía exclusiva del supervisor, con una razón obligatoria y sujeta a la SoD—. El ticket anulado deja de alimentar el cálculo.

Override de radar (S2 — warning mask)

/gauging/new contiene dos formularios distintos que nunca se fusionan (D-02): la sección S1 — Aforo (el dip de los pasos anteriores) y la sección S2 — Override de radar. El override es una herramienta custody-grade de alto valor: permite enmascarar un sensor en falla sustituyendo su lectura por un valor registrado a mano.

Una lectura de override se guarda con lifecycle:'persistent' (CreateManualReadingBody). A diferencia del dip puntual, una lectura persistente no tiene ventana de staleness (no "caduca" sola): está diseñada precisamente para mantener enmascarado un sensor mientras está averiado.

El aviso "enmascara el sensor" aparece ANTES del submit

El override es una decisión deliberada con consecuencias custody. Por eso, en S2, antes del CTA de envío, el formulario muestra un aviso explícito de que esa lectura enmascara el sensor. El operador confirma sabiendo lo que hace; no es un efecto secundario silencioso.

Un override activo se ve después como el banner ROJO "OVERRIDE ACTIVO · RADAR ENMASCARADO" en el detalle de tanque (Fold B). La única forma de limpiarlo / anularlo es con la autoridad manual:void (a través del OverrideReasonModal documentado en esa misma página) — no expira por sí solo.

Captura pendiente

Screenshot pendiente del barrido diferido — ver 61-DEFERRED-SWEEP.md (/gauging/new S2 override warning mask).

Separación de funciones (gauger ≠ ticketer)

SoD servidor-side — un intento directo devuelve 403

La regla de custodia es gauger ≠ ticketer: quien afora no puede aprobar ni anular su propio ticket. En concreto:

  • Aprobar / rechazar: el actor no puede ser el operador que creó el ticket (actor.userId ≠ ticket.operatorId).
  • Anular: el anulador no puede ser el operador original, y solo el supervisor puede anular.

La regla se aplica en el servidor@EnforceSoD + interceptor + reductor— antes de la mutación. El UI lo espeja (oculta el botón si serías tú mismo), pero el servidor es la autoridad: un llamado directo al endpoint, saltándose el UI, recibe 403.

Las API keys no pueden aprobar ni anular

Como refuerzo de la separación de funciones, las API keys tienen prohibidas las autoridades manual:approve y manual:void mediante una allowlist verificada por un CHECK en base de datos. Aprobar y anular son acciones humanas, no automatizables vía credencial de máquina.

Detalle del ticket

El detalle del ticket (/gauging/[ticketId]) muestra:

  • el header: número, tanque, operador, estado y timestamp de lectura;
  • las lecturas por cantidad: nivel, temperatura y agua libre;
  • la nota de aprobación atómica (recordatorio de que header y filas se aprobaron juntos);
  • para los tickets SUBMITTED, un botón Rechazar con un campo de razón (mínimo 10 caracteres).

Si el mismo operador que creó el ticket intenta rechazarlo, recibe el error 403 SoD inline —el servidor rechaza la acción por la regla gauger ≠ ticketer—.

Captura pendiente

Screenshot pendiente del barrido diferido — ver 61-DEFERRED-SWEEP.md (/gauging/[ticketId] detalle con lecturas).

Workspace y cola de aprobación

El workspace (/gauging) organiza los aforos en 2 pestañas (pending / approved) y expone el CTA "Nuevo aforo".

Workspace de aforos con tickets

La cola de aprobación (/gauging/approval) lista los tickets SUBMITTED pendientes, con acciones Aprobar / Rechazar inline sujetas a la SoD (solo manual:approve).

Captura pendiente — requiere sembrar datos

La captura de la cola de aprobación de gauging salió vacía (sin tickets SUBMITTED al momento del barrido). Honrando que una cola vacía no ilustra la feature, no se embebe aquí: queda pendiente de re-captura con datos sembrados —enviar un ticket como operador y revisarlo como supervisor— ver 61-DEFERRED-SWEEP.md (/gauging/approval cola con tickets SUBMITTED, GAU-06).

Páginas relacionadas

  • El aforo manual —la lectura puntual de una sola cantidad— comparte el mismo reductor de 5 estados, sin el contrato atómico de todo-o-nada del ticket.
  • La densidad y el BSW que el ticket no captura provienen de la muestra de laboratorio.
  • El override activo (banner rojo "OVERRIDE ACTIVO · RADAR ENMASCARADO") y la cadena de procedencia del cálculo se ven en el detalle de tanque.
  • El custody batch de transferencia (lote de movimiento con ticket correlativo gap-free) se documenta aparte (próximamente).