Especificación de interfaz · OpenAPI 3.0
Ministerio de Hidrocarburos · Chubut

API de Certificados de Devolución - Ministerio de Hidrocarburos de Chubut

Implementa
Ministerio de Hidrocarburos de la Provincia del Chubut
Consume
SoulIT, en nombre de Terminales Marítimas Patagónicas S.A.
Versión
1.0.0-borrador
URL base
https://api.ejemplo-ministerio.chubut.gov.ar/v1 (a definir)

Introducción

API que implementa el Ministerio de Hidrocarburos de la Provincia del Chubut y consume SoulIT, en nombre de Terminales Marítimas Patagónicas S.A. (Termap), para informar los Certificados de Devolución (certificados de embarque o de entrega) que emite Termap cada vez que embarca petróleo en un buque tanque. Reemplaza el envío actual por email.

Flujo de uso

  1. SoulIT obtiene un token en POST /auth/token con las credenciales que le entrega el Ministerio.
  2. Con ese token (Authorization: Bearer <token>) llama al resto de los endpoints.
  3. Un proceso automático de SoulIT envía cada certificado una sola vez con POST /certificados-devolucion.
  4. Si un certificado ya informado se modifica en el origen, SoulIT lo elimina (DELETE /certificados-devolucion/{terminal}/{numero}) y lo vuelve a crear con los datos nuevos. No hay endpoint de actualización.

Identificación del certificado

Un certificado se identifica por terminal + número. La numeración es correlativa e independiente para cada terminal y no cambia aunque el certificado se modifique.

Eliminación lógica

DELETE no borra el certificado: lo marca como anulado, con fecha y motivo. Después de eliminarlo se puede volver a crear otro con la misma terminal y el mismo número. GET devuelve siempre la versión vigente. Como la información es solo de consulta, se acepta que un certificado quede un tiempo eliminado sin haberse vuelto a crear; SoulIT lo reintenta en su siguiente ejecución.

Estructura única

Todos los tipos de Certificado de Devolución usan la misma estructura (CertificadoDevolucion). El campo tipoLiquidacion indica el tipo:

  • GARRAFA: la calidad (agua, sedimentos, sales, densidad) se determina con una única muestra en garrafa para todo el embarque. Los tanques no traen calidad ni cantidadesDespachadas.
  • TANQUE: la calidad se determina por tanque, con muestras al inicio y al final. Cada tanque debe traer calidad y cantidadesDespachadas.

Convenciones

  • JSON en UTF-8 sobre HTTPS.
  • Los números van como números JSON, con punto decimal y sin separador de miles.
  • Las fechas van en formato ISO 8601 AAAA-MM-DD. Las fechas con hora llevan zona horaria (2023-09-26T14:34:00-03:00).
  • Unidades: volúmenes en litros (l), masas en kilogramos (kg), alturas en milímetros (mm), temperaturas en grados Celsius (°C), densidades en kg/l, porcentajes en % v/v.
  • Los volúmenes, subtotales y masas pueden ser negativos. En un tanque, el subtotal es inicial − final, y un tanque puede haber recibido crudo durante la operación.
  • Los valores llegan ya calculados por el sistema de origen. La API no debe recalcularlos ni rechazar un certificado por diferencias aritméticas o de redondeo entre campos; solo debe validar el formato.
  • Los campos marcados nullable que no tienen dato se envían como null.

Secuencia de uso

Así se comunica el proceso automático de SoulIT con la API en cada ejecución.

Endpoints

POST/auth/token

Obtener token de acceso

Valida las credenciales entregadas por el Ministerio a SoulIT y devuelve un token de acceso con vencimiento. Cuando el token vence, el cliente vuelve a llamar a este endpoint.

No requiere token.

Cuerpo

JSON con la estructura SolicitudToken.

{
  "clientId": "soulit-termap",
  "clientSecret": "********"
}

Respuestas

CódigoSignificadoCuerpo
200Credenciales válidas.Token
{
  "accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "tokenType": "Bearer",
  "expiresIn": 3600
}
400El cuerpo no cumple el formato. detalles indica cada campo con error.Error
{
  "codigo": "SOLICITUD_INVALIDA",
  "mensaje": "El certificado tiene campos inválidos.",
  "detalles": [
    {
      "campo": "tanques[0].inicial.ctpl",
      "mensaje": "Debe ser un número."
    }
  ]
}
401Credenciales inválidas.Error
{
  "codigo": "CREDENCIALES_INVALIDAS",
  "mensaje": "Las credenciales no son válidas."
}
500Error inesperado del servidor. El cliente puede reintentar más tarde.Error
POST/certificados-devolucion

Crear un certificado

Registra un Certificado de Devolución. Si ya existe un certificado vigente con la misma terminal y el mismo número, responde 409 y no crea un duplicado. SoulIT trata el 409 como "ya informado", así que puede reintentar sin riesgo de duplicar.

Requiere Authorization: Bearer <token>.

Cuerpo

JSON con la estructura CertificadoDevolucion.

Ejemplos completos en Ejemplos.

Respuestas

CódigoSignificadoCuerpo
201Certificado registrado.ConfirmacionRegistro
{
  "terminal": "CAO",
  "numero": 6,
  "version": 1,
  "fechaRecepcion": "2023-09-27T15:02:11-03:00"
}
400El cuerpo no cumple el formato. detalles indica cada campo con error.Error
{
  "codigo": "SOLICITUD_INVALIDA",
  "mensaje": "El certificado tiene campos inválidos.",
  "detalles": [
    {
      "campo": "tanques[0].inicial.ctpl",
      "mensaje": "Debe ser un número."
    }
  ]
}
401Falta el token, no es válido o venció.Error
{
  "codigo": "TOKEN_INVALIDO",
  "mensaje": "El token no es válido o está vencido."
}
409Ya existe un certificado vigente con esa terminal y ese número.Error
{
  "codigo": "CERTIFICADO_EXISTENTE",
  "mensaje": "Ya existe un certificado vigente CAO 6."
}
500Error inesperado del servidor. El cliente puede reintentar más tarde.Error
GET/certificados-devolucion/{terminal}/{numero}

Leer un certificado

Devuelve la versión vigente del certificado. Los certificados anulados no se devuelven.

Requiere Authorization: Bearer <token>.

Parámetros

NombreUbicaciónTipoDescripción
terminalruta · obligatorioCodigoTerminalCódigo de la terminal de embarque.
numeroruta · obligatorioenteroNúmero del certificado, correlativo por terminal.

Respuestas

CódigoSignificadoCuerpo
200Certificado vigente.CertificadoDevolucionRegistrado
401Falta el token, no es válido o venció.Error
{
  "codigo": "TOKEN_INVALIDO",
  "mensaje": "El token no es válido o está vencido."
}
404No existe un certificado vigente con esa terminal y ese número.Error
{
  "codigo": "CERTIFICADO_INEXISTENTE",
  "mensaje": "No existe un certificado vigente CAO 6."
}
500Error inesperado del servidor. El cliente puede reintentar más tarde.Error
DELETE/certificados-devolucion/{terminal}/{numero}

Eliminar (anular) un certificado

Marca como anulada la versión vigente del certificado y registra la fecha y el motivo. Después de eliminarlo se puede volver a crear otro con la misma terminal y el mismo número.

Requiere Authorization: Bearer <token>.

Parámetros

NombreUbicaciónTipoDescripción
terminalruta · obligatorioCodigoTerminalCódigo de la terminal de embarque.
numeroruta · obligatorioenteroNúmero del certificado, correlativo por terminal.
motivoquery · obligatoriotextoMotivo de la eliminación (por ejemplo, "Modificación del certificado en origen").

Respuestas

CódigoSignificadoCuerpo
204Certificado anulado.—
401Falta el token, no es válido o venció.Error
{
  "codigo": "TOKEN_INVALIDO",
  "mensaje": "El token no es válido o está vencido."
}
404No existe un certificado vigente con esa terminal y ese número.Error
{
  "codigo": "CERTIFICADO_INEXISTENTE",
  "mensaje": "No existe un certificado vigente CAO 6."
}
500Error inesperado del servidor. El cliente puede reintentar más tarde.Error

Modelo de datos

Una sola estructura, CertificadoDevolucion, cubre la liquidación por Garrafa y la liquidación por Tanque. En “Obligatorio”, sí, admite null indica que el campo siempre está presente, pero vale null cuando no hay dato.

CertificadoDevolucion

Certificado de Devolución (embarque). Es la misma estructura para todos los tipos de liquidación.

CampoTipoObligatorioDescripción
terminalCodigoTerminalsí
puntoVentatextosíPunto de venta. Es la primera parte del número impreso (0010 en 0010-00000006).
formato ^\d{4}$
numeroenterosíNúmero del certificado (6 en 0010-00000006), correlativo por terminal.
mínimo 1
tipoLiquidaciontexto GARRAFA TANQUEsíDetermina cómo se calculó la calidad. Ver "Estructura única" en la descripción general.
fechaEmisionfechasíFecha de emisión ("Lugar y fecha" en el certificado impreso).
fechaBillOfLadingfechasí
fechaValorfechano
buqueBuquesí
destinoDestinosí
productotextosíProducto o yacimiento de origen del crudo (por ejemplo "Cañadon Seco" o "Escalante").
inspectorMediciontextono
observacionestextono
hasta 500 caracteres
operacionDatosOperacionsí
resumenResumenCantidadessí
cargadoreslista de CargadorsíCompañías cargadoras y su participación sobre el NSV total.
mínimo 1
tanqueslista de MedicionTanquesíMediciones de tanque, en el orden en que aparecen en el certificado. El mismo número de tanque puede aparecer más de una vez.
mínimo 1
pdfbase64noA confirmar. PDF del certificado impreso, codificado en Base64. Todavía no está decidido si se envía.

Buque

CampoTipoObligatorioDescripción
nombretextosí
numeroViajetextosíNúmero de viaje. Se envía como texto para conservar el valor tal cual figura en origen.

Destino

CampoTipoObligatorioDescripción
tipotexto LOCAL EXPORTACIONnoLOCAL si el país es Argentina; en otro caso, EXPORTACION.
paistextosí
refineriatextonoRefinería de destino. Solo se informa si el tipo es LOCAL.

DatosOperacion

Estado de las cañerías y cronología de la carga. Los momentos sin dato se envían como null.

CampoTipoObligatorioDescripción
estadoCaneriasInicioEstadoCaneriassí
estadoCaneriasFinEstadoCaneriassí
desplazamientoLineabooleanosíIndica si hubo desplazamiento de línea al iniciar la carga.
amarrofecha y horano
conectofecha y horano
inicioCargafecha y horanoInicio de la carga. Si hubo desplazamiento de línea, es el inicio del desplazamiento.
finDesplazamientofecha y horanoFin del desplazamiento de línea. Solo se informa si desplazamientoLinea es verdadero.
reinicioCargafecha y horanoReinicio de la carga después del desplazamiento de línea.
suspensioneslista de SuspensionsíSuspensiones de la carga, en orden cronológico. Puede estar vacía.
finalizofecha y horano
desconectofecha y horano
zarpofecha y horano

Suspension

CampoTipoObligatorioDescripción
suspendiofecha y horasí
reiniciofecha y horano
observacionestextono

ResumenCantidades

Totales del certificado ("Resumen de Cantidades").

CampoTipoObligatorioDescripción
porcentajeAguaYSedimentosnúmerosí% de agua y sedimentos en suspensión (S&W).
porcentajeAguanúmerosí% de agua en suspensión.
porcentajeSedimentosnúmerosí% de sedimentos en suspensión.
salesGm3númerosíSales en crudo [g/m3].
govnúmerosíVolumen Bruto Observado (GOV) [l].
gsvnúmerosíVolumen Bruto Estándar (GSV) [l].
volumenAguaYSedimentosnúmerosíVolumen total de agua y sedimentos (S&Wvol) [l].
nsvnúmerosíVolumen Neto Estándar (NSV) [l].
densidadBrutaVacionúmerosíDensidad bruta en vacío [kg/l].
densidadNetaVacionúmerosíDensidad neta en vacío [kg/l].
gradoApiNetonúmerosíGravedad API neta.
gswnúmerosíMasa Bruta Estándar (GSW) [kg].
nswnúmerosíMasa Neta Estándar (NSW) [kg].

Cargador

CampoTipoObligatorioDescripción
razonSocialtextosí
cuittextono
formato ^\d{2}-\d{8}-\d$
porcentajeParticipacionnúmerosíPorcentaje de participación, con hasta 5 decimales. La suma de todos los cargadores es 100.
nsvnúmerosíVolumen Neto Estándar (NSV) que le corresponde al cargador [l].

MedicionTanque

CampoTipoObligatorioDescripción
numeroTanqueenterosí
inicialLecturaTanquesí
finalLecturaTanquesí
subtotalSubtotalTanquesí
calidadCalidadTanqueno
cantidadesDespachadasCantidadesDespachadasno

LecturaTanque

Medición del tanque al inicio o al final de la carga.

CampoTipoObligatorioDescripción
alturaLiquidaenterosíAltura líquida [mm].
alturaAguaLibreenterosíAltura de agua libre y sedimentos (FW) [mm].
tovnúmerosíVolumen Total Observado (TOV) según tabla de calibración [l].
fwnúmerosíVolumen de agua libre y sedimentos (FW) según tabla de calibración [l].
govnúmerosíVolumen Bruto Observado (GOV) [l].
ctshnúmerosíFactor de corrección por temperatura de la envolvente del tanque (CTSh).
temperaturaFluidonúmerosíTemperatura promedio del fluido [°C].
temperaturaAmbientenúmerosíTemperatura ambiente [°C].
temperaturaTablaCalibracionnúmerosíTemperatura de la tabla de calibración [°C].
ctplnúmerosíFactor de corrección por temperatura y presión del líquido (CTPL).
gsvnúmerosíVolumen Bruto Estándar (GSV) [l].
correccionTechoFlotanteCorreccionTechoFlotanteno

CorreccionTechoFlotante admite null

Solo para tanques de techo flotante; null si el techo es fijo.

CampoTipoObligatorioDescripción
tipotexto FRA FRCnoMétodo de corrección por techo flotante.
volumennúmerosíCorrección de volumen [l]. Con FRC siempre es negativa o cero.
densidadObservadanúmerosíDensidad observada a la temperatura del fluido [kg/l].

SubtotalTanque

Diferencia inicial − final del tanque.

CampoTipoObligatorioDescripción
tovnúmerosí
fwnúmerosí
govnúmerosí
gsvnúmerosí

CalidadTanque admite null

Datos de las muestras del tanque (liquidación por Tanque). Obligatorio si tipoLiquidacion es TANQUE; null si es GARRAFA.

CampoTipoObligatorioDescripción
inicialMuestraTanquesí
finalMuestraTanquesí

MuestraTanque

Valores ponderados de la muestra (compuesta y de fondo).

CampoTipoObligatorioDescripción
densidadBrutaVacionúmerosí[kg/l]
porcentajeAguanúmerosí
porcentajeSedimentosnúmerosí
salesGm3númerosí

CantidadesDespachadas admite null

Cantidades despachadas desde el tanque (liquidación por Tanque). Obligatorio si tipoLiquidacion es TANQUE; null si es GARRAFA.

CampoTipoObligatorioDescripción
gswnúmerosíMasa Bruta Estándar [kg].
nswnúmerosíMasa Neta Estándar [kg].
volumenAguaYSedimentosnúmerosíVolumen S&W [l].
salesGnúmerosíSales en crudo [g].
nsvnúmerosíVolumen Neto Estándar [l].
porcentajeAguaYSedimentosnúmerosí"Suma % S&W" en el certificado impreso.
porcentajeAguanúmerosí"Suma % BS&W" en el certificado impreso.
porcentajeSedimentosnúmerosí"% Sed. Emuls." en el certificado impreso.
densidadBrutaVacionúmerosí[kg/l]

CodigoTerminal

Terminal de embarque de Termap:

  • CAC: Caleta Córdova
  • CAO: Caleta Olivia

texto CAC CAO

EstadoCanerias

texto LLENAS PARCIALMENTE VACIAS

CertificadoDevolucionRegistrado

CampoTipoObligatorioDescripción
terminalCodigoTerminalsí
puntoVentatextosíPunto de venta. Es la primera parte del número impreso (0010 en 0010-00000006).
formato ^\d{4}$
numeroenterosíNúmero del certificado (6 en 0010-00000006), correlativo por terminal.
mínimo 1
tipoLiquidaciontexto GARRAFA TANQUEsíDetermina cómo se calculó la calidad. Ver "Estructura única" en la descripción general.
fechaEmisionfechasíFecha de emisión ("Lugar y fecha" en el certificado impreso).
fechaBillOfLadingfechasí
fechaValorfechano
buqueBuquesí
destinoDestinosí
productotextosíProducto o yacimiento de origen del crudo (por ejemplo "Cañadon Seco" o "Escalante").
inspectorMediciontextono
observacionestextono
hasta 500 caracteres
operacionDatosOperacionsí
resumenResumenCantidadessí
cargadoreslista de CargadorsíCompañías cargadoras y su participación sobre el NSV total.
mínimo 1
tanqueslista de MedicionTanquesíMediciones de tanque, en el orden en que aparecen en el certificado. El mismo número de tanque puede aparecer más de una vez.
mínimo 1
pdfbase64noA confirmar. PDF del certificado impreso, codificado en Base64. Todavía no está decidido si se envía.
versionenterosí
fechaRecepcionfecha y horasí

ConfirmacionRegistro

CampoTipoObligatorioDescripción
terminalCodigoTerminalsí
numeroenterosí
versionenterosíEmpieza en 1 y aumenta cada vez que el certificado se elimina y se vuelve a crear.
fechaRecepcionfecha y horasí

SolicitudToken

CampoTipoObligatorioDescripción
clientIdtextosíIdentificador de cliente entregado por el Ministerio.
clientSecrettextosíSecreto entregado por el Ministerio.

Token

CampoTipoObligatorioDescripción
accessTokentextosí
tokenTypetexto Bearersí
expiresInenterosíSegundos hasta el vencimiento del token.

Error

CampoTipoObligatorioDescripción
codigotextosíCódigo estable, pensado para que lo procese un programa.
mensajetextosíDescripción legible del error.
detalleslista de objectno

Ejemplos

Armados con certificados reales de Termap. Los valores negativos y la repetición de tanques son válidos.

Liquidación por Tanque - certificado 0010-00000006, buque ACHILLEAS
{
  "terminal": "CAO",
  "puntoVenta": "0010",
  "numero": 6,
  "tipoLiquidacion": "TANQUE",
  "fechaEmision": "2023-09-26",
  "fechaBillOfLading": "2023-09-26",
  "fechaValor": null,
  "buque": {
    "nombre": "ACHILLEAS",
    "numeroViaje": "381"
  },
  "destino": {
    "tipo": "LOCAL",
    "pais": "ARGENTINA",
    "refineria": null
  },
  "producto": "Cañadon Seco",
  "inspectorMedicion": null,
  "observaciones": "- Se liquida en modo manual ante la falla de los radares.",
  "operacion": {
    "estadoCaneriasInicio": "LLENAS",
    "estadoCaneriasFin": "LLENAS",
    "desplazamientoLinea": false,
    "amarro": "2023-09-26T14:34:00-03:00",
    "conecto": "2023-09-26T14:40:00-03:00",
    "inicioCarga": null,
    "finDesplazamiento": null,
    "reinicioCarga": null,
    "suspensiones": [
      {
        "suspendio": "2023-09-26T17:34:00-03:00",
        "reinicio": "2023-09-26T18:35:00-03:00",
        "observaciones": null
      }
    ],
    "finalizo": "2023-09-27T14:35:00-03:00",
    "desconecto": "2023-09-27T14:36:00-03:00",
    "zarpo": "2023-09-27T14:40:00-03:00"
  },
  "resumen": {
    "porcentajeAguaYSedimentos": 0.6,
    "porcentajeAgua": 0.575,
    "porcentajeSedimentos": 0.025,
    "salesGm3": 60,
    "gov": 34763978,
    "gsv": 34737557,
    "volumenAguaYSedimentos": 208368,
    "nsv": 34529189,
    "densidadBrutaVacio": 0.898,
    "densidadNetaVacio": 0.8974,
    "gradoApiNeto": 26.09,
    "gsw": 31194326,
    "nsw": 30985958
  },
  "cargadores": [
    {
      "razonSocial": "Y.P.F. S.A.",
      "cuit": null,
      "porcentajeParticipacion": 100,
      "nsv": 34529189
    }
  ],
  "tanques": [
    {
      "numeroTanque": 2050,
      "inicial": {
        "alturaLiquida": 15000,
        "alturaAguaLibre": 0,
        "tov": 43711373,
        "fw": 0,
        "gov": 43512105,
        "ctsh": 0.99989,
        "temperaturaFluido": 16,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 20,
        "ctpl": 0.99924,
        "gsv": 43479036,
        "correccionTechoFlotante": {
          "tipo": "FRC",
          "volumen": -194460,
          "densidadObservada": 0.898
        }
      },
      "final": {
        "alturaLiquida": 3000,
        "alturaAguaLibre": 0,
        "tov": 8943571,
        "fw": 0,
        "gov": 8748127,
        "ctsh": 0.99989,
        "temperaturaFluido": 16,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 20,
        "ctpl": 0.99924,
        "gsv": 8741479,
        "correccionTechoFlotante": {
          "tipo": "FRC",
          "volumen": -194460,
          "densidadObservada": 0.898
        }
      },
      "subtotal": {
        "tov": 34767802,
        "fw": 0,
        "gov": 34763978,
        "gsv": 34737557
      },
      "calidad": {
        "inicial": {
          "densidadBrutaVacio": 0.898,
          "porcentajeAgua": 0.6,
          "porcentajeSedimentos": 0.025,
          "salesGm3": 60
        },
        "final": {
          "densidadBrutaVacio": 0.898,
          "porcentajeAgua": 0.7,
          "porcentajeSedimentos": 0.025,
          "salesGm3": 60
        }
      },
      "cantidadesDespachadas": {
        "gsw": 31194326,
        "nsw": 30985958,
        "volumenAguaYSedimentos": 208368,
        "salesG": 60,
        "nsv": 34529189,
        "porcentajeAguaYSedimentos": 0.6,
        "porcentajeAgua": 0.575,
        "porcentajeSedimentos": 0.025,
        "densidadBrutaVacio": 0.898
      }
    }
  ],
  "pdf": null
}
Liquidación por Garrafa - buque RECOLETA (8 tanques, número ilustrativo)
{
  "terminal": "CAC",
  "puntoVenta": "0009",
  "numero": 123,
  "tipoLiquidacion": "GARRAFA",
  "fechaEmision": "2023-09-09",
  "fechaBillOfLading": "2023-09-08",
  "fechaValor": null,
  "buque": {
    "nombre": "RECOLETA",
    "numeroViaje": "99990000"
  },
  "destino": {
    "tipo": "LOCAL",
    "pais": "ARGENTINA",
    "refineria": null
  },
  "producto": "Escalante",
  "inspectorMedicion": null,
  "observaciones": null,
  "operacion": {
    "estadoCaneriasInicio": "LLENAS",
    "estadoCaneriasFin": "LLENAS",
    "desplazamientoLinea": false,
    "amarro": null,
    "conecto": null,
    "inicioCarga": null,
    "finDesplazamiento": null,
    "reinicioCarga": null,
    "suspensiones": [],
    "finalizo": null,
    "desconecto": null,
    "zarpo": null
  },
  "resumen": {
    "porcentajeAguaYSedimentos": 0.92,
    "porcentajeAgua": 0.897,
    "porcentajeSedimentos": 0.023,
    "salesGm3": 47,
    "gov": 10840549,
    "gsv": 10880333,
    "volumenAguaYSedimentos": 100099,
    "nsv": 10780234,
    "densidadBrutaVacio": 0.9138,
    "densidadNetaVacio": 0.913,
    "gradoApiNeto": 23.4,
    "gsw": 9942448,
    "nsw": 9842349
  },
  "cargadores": [
    {
      "razonSocial": "COMPAÑIAS ASOCIADAS PETROLERAS S.A.",
      "cuit": null,
      "porcentajeParticipacion": 50,
      "nsv": 5373467
    },
    {
      "razonSocial": "CAPEX S.A",
      "cuit": null,
      "porcentajeParticipacion": 50,
      "nsv": 5373467
    }
  ],
  "tanques": [
    {
      "numeroTanque": 51,
      "inicial": {
        "alturaLiquida": 3000,
        "alturaAguaLibre": 200,
        "tov": 2892925,
        "fw": 214948,
        "gov": 2677388,
        "ctsh": 0.99978,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 20,
        "ctpl": 1.00367,
        "gsv": 2687214,
        "correccionTechoFlotante": null
      },
      "final": {
        "alturaLiquida": 2000,
        "alturaAguaLibre": 100,
        "tov": 1937305,
        "fw": 119178,
        "gov": 1817727,
        "ctsh": 0.99978,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 20,
        "ctpl": 1.00367,
        "gsv": 1824398,
        "correccionTechoFlotante": null
      },
      "subtotal": {
        "tov": 955620,
        "fw": 95770,
        "gov": 859661,
        "gsv": 862816
      },
      "calidad": null,
      "cantidadesDespachadas": null
    },
    {
      "numeroTanque": 54,
      "inicial": {
        "alturaLiquida": 13001,
        "alturaAguaLibre": 200,
        "tov": 9956931,
        "fw": 217158,
        "gov": 9737630,
        "ctsh": 0.99978,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 20,
        "ctpl": 1.00367,
        "gsv": 9773367,
        "correccionTechoFlotante": null
      },
      "final": {
        "alturaLiquida": 12001,
        "alturaAguaLibre": 100,
        "tov": 9197689,
        "fw": 141029,
        "gov": 9054668,
        "ctsh": 0.99978,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 20,
        "ctpl": 1.00367,
        "gsv": 9087898,
        "correccionTechoFlotante": null
      },
      "subtotal": {
        "tov": 759242,
        "fw": 76129,
        "gov": 682962,
        "gsv": 685469
      },
      "calidad": null,
      "cantidadesDespachadas": null
    },
    {
      "numeroTanque": 55,
      "inicial": {
        "alturaLiquida": 7478,
        "alturaAguaLibre": 200,
        "tov": 7928902,
        "fw": 272188,
        "gov": 7655030,
        "ctsh": 0.99978,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 20,
        "ctpl": 1.00367,
        "gsv": 7683123,
        "correccionTechoFlotante": null
      },
      "final": {
        "alturaLiquida": 6478,
        "alturaAguaLibre": 100,
        "tov": 6877141,
        "fw": 166888,
        "gov": 6708777,
        "ctsh": 0.99978,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 20,
        "ctpl": 1.00367,
        "gsv": 6733398,
        "correccionTechoFlotante": null
      },
      "subtotal": {
        "tov": 1051761,
        "fw": 105300,
        "gov": 946253,
        "gsv": 949725
      },
      "calidad": null,
      "cantidadesDespachadas": null
    },
    {
      "numeroTanque": 56,
      "inicial": {
        "alturaLiquida": 13020,
        "alturaAguaLibre": 3500,
        "tov": 9935760,
        "fw": 2685118,
        "gov": 7249047,
        "ctsh": 0.99978,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 20,
        "ctpl": 1.00367,
        "gsv": 7275651,
        "correccionTechoFlotante": null
      },
      "final": {
        "alturaLiquida": 12020,
        "alturaAguaLibre": 4000,
        "tov": 9176460,
        "fw": 3066718,
        "gov": 6108398,
        "ctsh": 0.99978,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 20,
        "ctpl": 1.00367,
        "gsv": 6130816,
        "correccionTechoFlotante": null
      },
      "subtotal": {
        "tov": 759300,
        "fw": -381600,
        "gov": 1140649,
        "gsv": 1144835
      },
      "calidad": null,
      "cantidadesDespachadas": null
    },
    {
      "numeroTanque": 59,
      "inicial": {
        "alturaLiquida": 3500,
        "alturaAguaLibre": 200,
        "tov": 3495434,
        "fw": 203875,
        "gov": 3290835,
        "ctsh": 0.99978,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 20,
        "ctpl": 1.00367,
        "gsv": 3302912,
        "correccionTechoFlotante": null
      },
      "final": {
        "alturaLiquida": 3000,
        "alturaAguaLibre": 1000,
        "tov": 2996675,
        "fw": 24625,
        "gov": 2971396,
        "ctsh": 0.99978,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 20,
        "ctpl": 1.00367,
        "gsv": 2982301,
        "correccionTechoFlotante": null
      },
      "subtotal": {
        "tov": 498759,
        "fw": 179250,
        "gov": 319439,
        "gsv": 320611
      },
      "calidad": null,
      "cantidadesDespachadas": null
    },
    {
      "numeroTanque": 65,
      "inicial": {
        "alturaLiquida": 3000,
        "alturaAguaLibre": 100,
        "tov": 4297744,
        "fw": 225051,
        "gov": 4070942,
        "ctsh": 0.99957,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 29.4,
        "ctpl": 1.00367,
        "gsv": 4085882,
        "correccionTechoFlotante": null
      },
      "final": {
        "alturaLiquida": 4000,
        "alturaAguaLibre": 200,
        "tov": 5702353,
        "fw": 365466,
        "gov": 5334592,
        "ctsh": 0.99957,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 29.4,
        "ctpl": 1.00367,
        "gsv": 5354170,
        "correccionTechoFlotante": null
      },
      "subtotal": {
        "tov": -1404609,
        "fw": -140415,
        "gov": -1263650,
        "gsv": -1268288
      },
      "calidad": null,
      "cantidadesDespachadas": null
    },
    {
      "numeroTanque": 66,
      "inicial": {
        "alturaLiquida": 5000,
        "alturaAguaLibre": 200,
        "tov": 7057079,
        "fw": 316612,
        "gov": 6737636,
        "ctsh": 0.99958,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 28.9,
        "ctpl": 1.00367,
        "gsv": 6762363,
        "correccionTechoFlotante": null
      },
      "final": {
        "alturaLiquida": 3000,
        "alturaAguaLibre": 50,
        "tov": 4248623,
        "fw": 105997,
        "gov": 4140886,
        "ctsh": 0.99958,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 28.9,
        "ctpl": 1.00367,
        "gsv": 4156083,
        "correccionTechoFlotante": null
      },
      "subtotal": {
        "tov": 2808456,
        "fw": 210615,
        "gov": 2596750,
        "gsv": 2606280
      },
      "calidad": null,
      "cantidadesDespachadas": null
    },
    {
      "numeroTanque": 71,
      "inicial": {
        "alturaLiquida": 4000,
        "alturaAguaLibre": 50,
        "tov": 11474701,
        "fw": 115526,
        "gov": 11147923,
        "ctsh": 0.99978,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 20,
        "ctpl": 1.00367,
        "gsv": 11188836,
        "correccionTechoFlotante": {
          "tipo": "FRC",
          "volumen": -208753,
          "densidadObservada": 1
        }
      },
      "final": {
        "alturaLiquida": 2000,
        "alturaAguaLibre": 40,
        "tov": 5689401,
        "fw": 98733,
        "gov": 5589438,
        "ctsh": 0.99978,
        "temperaturaFluido": 10,
        "temperaturaAmbiente": 10,
        "temperaturaTablaCalibracion": 20,
        "ctpl": 1.00367,
        "gsv": 5609951,
        "correccionTechoFlotante": {
          "tipo": "FRC",
          "volumen": 0,
          "densidadObservada": 1
        }
      },
      "subtotal": {
        "tov": 5785300,
        "fw": 16793,
        "gov": 5558485,
        "gsv": 5578885
      },
      "calidad": null,
      "cantidadesDespachadas": null
    }
  ],
  "pdf": null
}

Archivo OpenAPI

Esta es la definición completa en formato OpenAPI 3.0. Se puede importar en Swagger Editor, Postman o en un generador de código.

openapi.yaml
openapi: 3.0.3
info:
  title: API de Certificados de Devolución - Ministerio de Hidrocarburos de Chubut
  version: 1.0.0-borrador
  description: |
    API que **implementa el Ministerio de Hidrocarburos de la Provincia del Chubut** y **consume SoulIT**,
    en nombre de Terminales Marítimas Patagónicas S.A. (Termap), para informar los **Certificados de
    Devolución** (certificados de embarque o de entrega) que emite Termap cada vez que embarca petróleo
    en un buque tanque. Reemplaza el envío actual por email.

    ## Flujo de uso
    1. SoulIT obtiene un token en `POST /auth/token` con las credenciales que le entrega el Ministerio.
    2. Con ese token (`Authorization: Bearer <token>`) llama al resto de los endpoints.
    3. Un proceso automático de SoulIT envía cada certificado una sola vez con `POST /certificados-devolucion`.
    4. Si un certificado ya informado se modifica en el origen, SoulIT lo **elimina**
       (`DELETE /certificados-devolucion/{terminal}/{numero}`) y lo **vuelve a crear** con los datos nuevos.
       No hay endpoint de actualización.

    ## Identificación del certificado
    Un certificado se identifica por **terminal + número**. La numeración es correlativa e independiente
    para cada terminal y no cambia aunque el certificado se modifique.

    ## Eliminación lógica
    `DELETE` no borra el certificado: lo marca como **anulado**, con fecha y motivo. Después de eliminarlo
    se puede volver a crear otro con la misma terminal y el mismo número. `GET` devuelve siempre la versión
    vigente. Como la información es solo de consulta, se acepta que un certificado quede un tiempo
    eliminado sin haberse vuelto a crear; SoulIT lo reintenta en su siguiente ejecución.

    ## Estructura única
    Todos los tipos de Certificado de Devolución usan la misma estructura (`CertificadoDevolucion`).
    El campo `tipoLiquidacion` indica el tipo:
    - `GARRAFA`: la calidad (agua, sedimentos, sales, densidad) se determina con una única muestra en
      garrafa para todo el embarque. Los tanques **no** traen `calidad` ni `cantidadesDespachadas`.
    - `TANQUE`: la calidad se determina por tanque, con muestras al inicio y al final. Cada tanque
      **debe** traer `calidad` y `cantidadesDespachadas`.

    ## Convenciones
    - JSON en UTF-8 sobre HTTPS.
    - Los números van como números JSON, con **punto decimal** y sin separador de miles.
    - Las fechas van en formato ISO 8601 `AAAA-MM-DD`. Las fechas con hora llevan zona horaria
      (`2023-09-26T14:34:00-03:00`).
    - Unidades: volúmenes en litros (l), masas en kilogramos (kg), alturas en milímetros (mm),
      temperaturas en grados Celsius (°C), densidades en kg/l, porcentajes en % v/v.
    - **Los volúmenes, subtotales y masas pueden ser negativos.** En un tanque, el subtotal es
      inicial − final, y un tanque puede haber recibido crudo durante la operación.
    - Los valores llegan **ya calculados** por el sistema de origen. La API no debe recalcularlos ni
      rechazar un certificado por diferencias aritméticas o de redondeo entre campos; solo debe validar
      el formato.
    - Los campos marcados `nullable` que no tienen dato se envían como `null`.
servers:
  - url: https://api.ejemplo-ministerio.chubut.gov.ar/v1
    description: URL a definir por el Ministerio
security:
  - bearerAuth: []
tags:
  - name: Autenticación
    description: Obtención del token de acceso.
  - name: Certificados de Devolución
    description: Alta, consulta y eliminación de Certificados de Devolución.

paths:
  /auth/token:
    post:
      tags: [Autenticación]
      summary: Obtener token de acceso
      operationId: obtenerToken
      description: |
        Valida las credenciales entregadas por el Ministerio a SoulIT y devuelve un token de acceso con
        vencimiento. Cuando el token vence, el cliente vuelve a llamar a este endpoint.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SolicitudToken'
            example:
              clientId: soulit-termap
              clientSecret: '********'
      responses:
        '200':
          description: Credenciales válidas.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Token'
              example:
                accessToken: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
                tokenType: Bearer
                expiresIn: 3600
        '400':
          $ref: '#/components/responses/SolicitudInvalida'
        '401':
          description: Credenciales inválidas.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                codigo: CREDENCIALES_INVALIDAS
                mensaje: Las credenciales no son válidas.
        '500':
          $ref: '#/components/responses/ErrorInterno'

  /certificados-devolucion:
    post:
      tags: [Certificados de Devolución]
      summary: Crear un certificado
      operationId: crearCertificado
      description: |
        Registra un Certificado de Devolución. Si ya existe un certificado **vigente** con la misma
        terminal y el mismo número, responde `409` y no crea un duplicado. SoulIT trata el `409` como
        "ya informado", así que puede reintentar sin riesgo de duplicar.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CertificadoDevolucion'
            examples:
              liquidacionPorTanque:
                $ref: '#/components/examples/CertificadoTanque'
              liquidacionPorGarrafa:
                $ref: '#/components/examples/CertificadoGarrafa'
      responses:
        '201':
          description: Certificado registrado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfirmacionRegistro'
              example:
                terminal: CAO
                numero: 6
                version: 1
                fechaRecepcion: '2023-09-27T15:02:11-03:00'
        '400':
          $ref: '#/components/responses/SolicitudInvalida'
        '401':
          $ref: '#/components/responses/NoAutenticado'
        '409':
          description: Ya existe un certificado vigente con esa terminal y ese número.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                codigo: CERTIFICADO_EXISTENTE
                mensaje: Ya existe un certificado vigente CAO 6.
        '500':
          $ref: '#/components/responses/ErrorInterno'

  /certificados-devolucion/{terminal}/{numero}:
    parameters:
      - $ref: '#/components/parameters/Terminal'
      - $ref: '#/components/parameters/Numero'
    get:
      tags: [Certificados de Devolución]
      summary: Leer un certificado
      operationId: leerCertificado
      description: Devuelve la versión vigente del certificado. Los certificados anulados no se devuelven.
      responses:
        '200':
          description: Certificado vigente.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CertificadoDevolucionRegistrado'
        '401':
          $ref: '#/components/responses/NoAutenticado'
        '404':
          $ref: '#/components/responses/NoEncontrado'
        '500':
          $ref: '#/components/responses/ErrorInterno'
    delete:
      tags: [Certificados de Devolución]
      summary: Eliminar (anular) un certificado
      operationId: eliminarCertificado
      description: |
        Marca como anulada la versión vigente del certificado y registra la fecha y el motivo. Después
        de eliminarlo se puede volver a crear otro con la misma terminal y el mismo número.
      parameters:
        - name: motivo
          in: query
          required: true
          description: Motivo de la eliminación (por ejemplo, "Modificación del certificado en origen").
          schema:
            type: string
            maxLength: 500
      responses:
        '204':
          description: Certificado anulado.
        '401':
          $ref: '#/components/responses/NoAutenticado'
        '404':
          $ref: '#/components/responses/NoEncontrado'
        '500':
          $ref: '#/components/responses/ErrorInterno'

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Token obtenido en `POST /auth/token`.

  parameters:
    Terminal:
      name: terminal
      in: path
      required: true
      description: Código de la terminal de embarque.
      schema:
        $ref: '#/components/schemas/CodigoTerminal'
    Numero:
      name: numero
      in: path
      required: true
      description: Número del certificado, correlativo por terminal.
      schema:
        type: integer
        minimum: 1

  responses:
    SolicitudInvalida:
      description: El cuerpo no cumple el formato. `detalles` indica cada campo con error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            codigo: SOLICITUD_INVALIDA
            mensaje: El certificado tiene campos inválidos.
            detalles:
              - campo: tanques[0].inicial.ctpl
                mensaje: Debe ser un número.
    NoAutenticado:
      description: Falta el token, no es válido o venció.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            codigo: TOKEN_INVALIDO
            mensaje: El token no es válido o está vencido.
    NoEncontrado:
      description: No existe un certificado vigente con esa terminal y ese número.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            codigo: CERTIFICADO_INEXISTENTE
            mensaje: No existe un certificado vigente CAO 6.
    ErrorInterno:
      description: Error inesperado del servidor. El cliente puede reintentar más tarde.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

  schemas:
    SolicitudToken:
      type: object
      required: [clientId, clientSecret]
      properties:
        clientId:
          type: string
          description: Identificador de cliente entregado por el Ministerio.
        clientSecret:
          type: string
          description: Secreto entregado por el Ministerio.

    Token:
      type: object
      required: [accessToken, tokenType, expiresIn]
      properties:
        accessToken:
          type: string
        tokenType:
          type: string
          enum: [Bearer]
        expiresIn:
          type: integer
          description: Segundos hasta el vencimiento del token.

    Error:
      type: object
      required: [codigo, mensaje]
      properties:
        codigo:
          type: string
          description: Código estable, pensado para que lo procese un programa.
        mensaje:
          type: string
          description: Descripción legible del error.
        detalles:
          type: array
          items:
            type: object
            required: [campo, mensaje]
            properties:
              campo:
                type: string
                description: Ruta del campo con error.
              mensaje:
                type: string

    ConfirmacionRegistro:
      type: object
      required: [terminal, numero, version, fechaRecepcion]
      properties:
        terminal:
          $ref: '#/components/schemas/CodigoTerminal'
        numero:
          type: integer
        version:
          type: integer
          description: Empieza en 1 y aumenta cada vez que el certificado se elimina y se vuelve a crear.
        fechaRecepcion:
          type: string
          format: date-time

    CodigoTerminal:
      type: string
      enum: [CAC, CAO]
      description: |
        Terminal de embarque de Termap:
        - `CAC`: Caleta Córdova
        - `CAO`: Caleta Olivia

    CertificadoDevolucion:
      type: object
      description: Certificado de Devolución (embarque). Es la misma estructura para todos los tipos de liquidación.
      required:
        - terminal
        - puntoVenta
        - numero
        - tipoLiquidacion
        - fechaEmision
        - fechaBillOfLading
        - buque
        - destino
        - producto
        - operacion
        - resumen
        - cargadores
        - tanques
      properties:
        terminal:
          $ref: '#/components/schemas/CodigoTerminal'
        puntoVenta:
          type: string
          pattern: '^\d{4}$'
          description: Punto de venta. Es la primera parte del número impreso (`0010` en `0010-00000006`).
          example: '0010'
        numero:
          type: integer
          minimum: 1
          description: Número del certificado (`6` en `0010-00000006`), correlativo por terminal.
        tipoLiquidacion:
          type: string
          enum: [GARRAFA, TANQUE]
          description: Determina cómo se calculó la calidad. Ver "Estructura única" en la descripción general.
        fechaEmision:
          type: string
          format: date
          description: Fecha de emisión ("Lugar y fecha" en el certificado impreso).
        fechaBillOfLading:
          type: string
          format: date
        fechaValor:
          type: string
          format: date
          nullable: true
        buque:
          $ref: '#/components/schemas/Buque'
        destino:
          $ref: '#/components/schemas/Destino'
        producto:
          type: string
          description: Producto o yacimiento de origen del crudo (por ejemplo "Cañadon Seco" o "Escalante").
        inspectorMedicion:
          type: string
          nullable: true
        observaciones:
          type: string
          nullable: true
          maxLength: 500
        operacion:
          $ref: '#/components/schemas/DatosOperacion'
        resumen:
          $ref: '#/components/schemas/ResumenCantidades'
        cargadores:
          type: array
          minItems: 1
          description: Compañías cargadoras y su participación sobre el NSV total.
          items:
            $ref: '#/components/schemas/Cargador'
        tanques:
          type: array
          minItems: 1
          description: |
            Mediciones de tanque, en el orden en que aparecen en el certificado. El mismo número de
            tanque puede aparecer más de una vez.
          items:
            $ref: '#/components/schemas/MedicionTanque'
        pdf:
          type: string
          format: byte
          nullable: true
          description: |
            **A confirmar.** PDF del certificado impreso, codificado en Base64. Todavía no está decidido
            si se envía.

    CertificadoDevolucionRegistrado:
      allOf:
        - $ref: '#/components/schemas/CertificadoDevolucion'
        - type: object
          required: [version, fechaRecepcion]
          properties:
            version:
              type: integer
            fechaRecepcion:
              type: string
              format: date-time

    Buque:
      type: object
      required: [nombre, numeroViaje]
      properties:
        nombre:
          type: string
          example: ACHILLEAS
        numeroViaje:
          type: string
          description: Número de viaje. Se envía como texto para conservar el valor tal cual figura en origen.
          example: '381'

    Destino:
      type: object
      required: [pais]
      properties:
        tipo:
          type: string
          enum: [LOCAL, EXPORTACION]
          nullable: true
          description: '`LOCAL` si el país es Argentina; en otro caso, `EXPORTACION`.'
        pais:
          type: string
          example: ARGENTINA
        refineria:
          type: string
          nullable: true
          description: Refinería de destino. Solo se informa si el tipo es `LOCAL`.

    DatosOperacion:
      type: object
      description: |
        Estado de las cañerías y cronología de la carga. Los momentos sin dato se envían como `null`.
      required: [estadoCaneriasInicio, estadoCaneriasFin, desplazamientoLinea, suspensiones]
      properties:
        estadoCaneriasInicio:
          $ref: '#/components/schemas/EstadoCanerias'
        estadoCaneriasFin:
          $ref: '#/components/schemas/EstadoCanerias'
        desplazamientoLinea:
          type: boolean
          description: Indica si hubo desplazamiento de línea al iniciar la carga.
        amarro:
          type: string
          format: date-time
          nullable: true
        conecto:
          type: string
          format: date-time
          nullable: true
        inicioCarga:
          type: string
          format: date-time
          nullable: true
          description: Inicio de la carga. Si hubo desplazamiento de línea, es el inicio del desplazamiento.
        finDesplazamiento:
          type: string
          format: date-time
          nullable: true
          description: Fin del desplazamiento de línea. Solo se informa si `desplazamientoLinea` es verdadero.
        reinicioCarga:
          type: string
          format: date-time
          nullable: true
          description: Reinicio de la carga después del desplazamiento de línea.
        suspensiones:
          type: array
          description: Suspensiones de la carga, en orden cronológico. Puede estar vacía.
          items:
            $ref: '#/components/schemas/Suspension'
        finalizo:
          type: string
          format: date-time
          nullable: true
        desconecto:
          type: string
          format: date-time
          nullable: true
        zarpo:
          type: string
          format: date-time
          nullable: true

    EstadoCanerias:
      type: string
      enum: [LLENAS, PARCIALMENTE, VACIAS]

    Suspension:
      type: object
      required: [suspendio]
      properties:
        suspendio:
          type: string
          format: date-time
        reinicio:
          type: string
          format: date-time
          nullable: true
        observaciones:
          type: string
          nullable: true

    ResumenCantidades:
      type: object
      description: Totales del certificado ("Resumen de Cantidades").
      required:
        - porcentajeAguaYSedimentos
        - porcentajeAgua
        - porcentajeSedimentos
        - salesGm3
        - gov
        - gsv
        - volumenAguaYSedimentos
        - nsv
        - densidadBrutaVacio
        - densidadNetaVacio
        - gradoApiNeto
        - gsw
        - nsw
      properties:
        porcentajeAguaYSedimentos:
          type: number
          description: '% de agua y sedimentos en suspensión (S&W).'
        porcentajeAgua:
          type: number
          description: '% de agua en suspensión.'
        porcentajeSedimentos:
          type: number
          description: '% de sedimentos en suspensión.'
        salesGm3:
          type: number
          description: Sales en crudo [g/m3].
        gov:
          type: number
          description: Volumen Bruto Observado (GOV) [l].
        gsv:
          type: number
          description: Volumen Bruto Estándar (GSV) [l].
        volumenAguaYSedimentos:
          type: number
          description: Volumen total de agua y sedimentos (S&Wvol) [l].
        nsv:
          type: number
          description: Volumen Neto Estándar (NSV) [l].
        densidadBrutaVacio:
          type: number
          description: Densidad bruta en vacío [kg/l].
        densidadNetaVacio:
          type: number
          description: Densidad neta en vacío [kg/l].
        gradoApiNeto:
          type: number
          description: Gravedad API neta.
        gsw:
          type: number
          description: Masa Bruta Estándar (GSW) [kg].
        nsw:
          type: number
          description: Masa Neta Estándar (NSW) [kg].

    Cargador:
      type: object
      required: [razonSocial, porcentajeParticipacion, nsv]
      properties:
        razonSocial:
          type: string
        cuit:
          type: string
          nullable: true
          pattern: '^\d{2}-\d{8}-\d$'
        porcentajeParticipacion:
          type: number
          description: Porcentaje de participación, con hasta 5 decimales. La suma de todos los cargadores es 100.
        nsv:
          type: number
          description: Volumen Neto Estándar (NSV) que le corresponde al cargador [l].

    MedicionTanque:
      type: object
      required: [numeroTanque, inicial, final, subtotal]
      properties:
        numeroTanque:
          type: integer
        inicial:
          $ref: '#/components/schemas/LecturaTanque'
        final:
          $ref: '#/components/schemas/LecturaTanque'
        subtotal:
          $ref: '#/components/schemas/SubtotalTanque'
        calidad:
          $ref: '#/components/schemas/CalidadTanque'
        cantidadesDespachadas:
          $ref: '#/components/schemas/CantidadesDespachadas'

    LecturaTanque:
      type: object
      description: Medición del tanque al inicio o al final de la carga.
      required:
        - alturaLiquida
        - alturaAguaLibre
        - tov
        - fw
        - gov
        - ctsh
        - temperaturaFluido
        - temperaturaAmbiente
        - temperaturaTablaCalibracion
        - ctpl
        - gsv
      properties:
        alturaLiquida:
          type: integer
          description: Altura líquida [mm].
        alturaAguaLibre:
          type: integer
          description: Altura de agua libre y sedimentos (FW) [mm].
        tov:
          type: number
          description: Volumen Total Observado (TOV) según tabla de calibración [l].
        fw:
          type: number
          description: Volumen de agua libre y sedimentos (FW) según tabla de calibración [l].
        gov:
          type: number
          description: Volumen Bruto Observado (GOV) [l].
        ctsh:
          type: number
          description: Factor de corrección por temperatura de la envolvente del tanque (CTSh).
        temperaturaFluido:
          type: number
          description: Temperatura promedio del fluido [°C].
        temperaturaAmbiente:
          type: number
          description: Temperatura ambiente [°C].
        temperaturaTablaCalibracion:
          type: number
          description: Temperatura de la tabla de calibración [°C].
        ctpl:
          type: number
          description: Factor de corrección por temperatura y presión del líquido (CTPL).
        gsv:
          type: number
          description: Volumen Bruto Estándar (GSV) [l].
        correccionTechoFlotante:
          $ref: '#/components/schemas/CorreccionTechoFlotante'

    CorreccionTechoFlotante:
      type: object
      nullable: true
      description: 'Solo para tanques de techo flotante; `null` si el techo es fijo.'
      required: [volumen, densidadObservada]
      properties:
        tipo:
          type: string
          enum: [FRA, FRC]
          nullable: true
          description: Método de corrección por techo flotante.
        volumen:
          type: number
          description: Corrección de volumen [l]. Con FRC siempre es negativa o cero.
        densidadObservada:
          type: number
          description: Densidad observada a la temperatura del fluido [kg/l].

    SubtotalTanque:
      type: object
      description: Diferencia inicial − final del tanque.
      required: [tov, fw, gov, gsv]
      properties:
        tov:
          type: number
        fw:
          type: number
        gov:
          type: number
        gsv:
          type: number

    CalidadTanque:
      type: object
      nullable: true
      description: Datos de las muestras del tanque (liquidación por Tanque). Obligatorio si `tipoLiquidacion` es `TANQUE`; `null` si es `GARRAFA`.
      required: [inicial, final]
      properties:
        inicial:
          $ref: '#/components/schemas/MuestraTanque'
        final:
          $ref: '#/components/schemas/MuestraTanque'

    MuestraTanque:
      type: object
      description: Valores ponderados de la muestra (compuesta y de fondo).
      required: [densidadBrutaVacio, porcentajeAgua, porcentajeSedimentos, salesGm3]
      properties:
        densidadBrutaVacio:
          type: number
          description: '[kg/l]'
        porcentajeAgua:
          type: number
        porcentajeSedimentos:
          type: number
        salesGm3:
          type: number

    CantidadesDespachadas:
      type: object
      nullable: true
      description: Cantidades despachadas desde el tanque (liquidación por Tanque). Obligatorio si `tipoLiquidacion` es `TANQUE`; `null` si es `GARRAFA`.
      required:
        - gsw
        - nsw
        - volumenAguaYSedimentos
        - salesG
        - nsv
        - porcentajeAguaYSedimentos
        - porcentajeAgua
        - porcentajeSedimentos
        - densidadBrutaVacio
      properties:
        gsw:
          type: number
          description: Masa Bruta Estándar [kg].
        nsw:
          type: number
          description: Masa Neta Estándar [kg].
        volumenAguaYSedimentos:
          type: number
          description: Volumen S&W [l].
        salesG:
          type: number
          description: Sales en crudo [g].
        nsv:
          type: number
          description: Volumen Neto Estándar [l].
        porcentajeAguaYSedimentos:
          type: number
          description: '"Suma % S&W" en el certificado impreso.'
        porcentajeAgua:
          type: number
          description: '"Suma % BS&W" en el certificado impreso.'
        porcentajeSedimentos:
          type: number
          description: '"% Sed. Emuls." en el certificado impreso.'
        densidadBrutaVacio:
          type: number
          description: '[kg/l]'

  examples:
    CertificadoTanque:
      summary: Liquidación por Tanque - certificado 0010-00000006, buque ACHILLEAS
      value:
        terminal: CAO
        puntoVenta: '0010'
        numero: 6
        tipoLiquidacion: TANQUE
        fechaEmision: '2023-09-26'
        fechaBillOfLading: '2023-09-26'
        fechaValor: null
        buque:
          nombre: ACHILLEAS
          numeroViaje: '381'
        destino:
          tipo: LOCAL
          pais: ARGENTINA
          refineria: null
        producto: Cañadon Seco
        inspectorMedicion: null
        observaciones: '- Se liquida en modo manual ante la falla de los radares.'
        operacion:
          estadoCaneriasInicio: LLENAS
          estadoCaneriasFin: LLENAS
          desplazamientoLinea: false
          amarro: '2023-09-26T14:34:00-03:00'
          conecto: '2023-09-26T14:40:00-03:00'
          inicioCarga: null
          finDesplazamiento: null
          reinicioCarga: null
          suspensiones:
            - suspendio: '2023-09-26T17:34:00-03:00'
              reinicio: '2023-09-26T18:35:00-03:00'
              observaciones: null
          finalizo: '2023-09-27T14:35:00-03:00'
          desconecto: '2023-09-27T14:36:00-03:00'
          zarpo: '2023-09-27T14:40:00-03:00'
        resumen:
          porcentajeAguaYSedimentos: 0.600
          porcentajeAgua: 0.575
          porcentajeSedimentos: 0.025
          salesGm3: 60
          gov: 34763978
          gsv: 34737557
          volumenAguaYSedimentos: 208368
          nsv: 34529189
          densidadBrutaVacio: 0.8980
          densidadNetaVacio: 0.8974
          gradoApiNeto: 26.09
          gsw: 31194326
          nsw: 30985958
        cargadores:
          - razonSocial: Y.P.F. S.A.
            cuit: null
            porcentajeParticipacion: 100.00000
            nsv: 34529189
        tanques:
          - numeroTanque: 2050
            inicial:
              alturaLiquida: 15000
              alturaAguaLibre: 0
              tov: 43711373
              fw: 0
              gov: 43512105
              ctsh: 0.99989
              temperaturaFluido: 16.00
              temperaturaAmbiente: 10.00
              temperaturaTablaCalibracion: 20.00
              ctpl: 0.99924
              gsv: 43479036
              correccionTechoFlotante:
                tipo: FRC
                volumen: -194460
                densidadObservada: 0.8980
            final:
              alturaLiquida: 3000
              alturaAguaLibre: 0
              tov: 8943571
              fw: 0
              gov: 8748127
              ctsh: 0.99989
              temperaturaFluido: 16.00
              temperaturaAmbiente: 10.00
              temperaturaTablaCalibracion: 20.00
              ctpl: 0.99924
              gsv: 8741479
              correccionTechoFlotante:
                tipo: FRC
                volumen: -194460
                densidadObservada: 0.8980
            subtotal:
              tov: 34767802
              fw: 0
              gov: 34763978
              gsv: 34737557
            calidad:
              inicial:
                densidadBrutaVacio: 0.8980
                porcentajeAgua: 0.600
                porcentajeSedimentos: 0.025
                salesGm3: 60
              final:
                densidadBrutaVacio: 0.8980
                porcentajeAgua: 0.700
                porcentajeSedimentos: 0.025
                salesGm3: 60
            cantidadesDespachadas:
              gsw: 31194326
              nsw: 30985958
              volumenAguaYSedimentos: 208368
              salesG: 60
              nsv: 34529189
              porcentajeAguaYSedimentos: 0.600
              porcentajeAgua: 0.575
              porcentajeSedimentos: 0.025
              densidadBrutaVacio: 0.8980
        pdf: null

    CertificadoGarrafa:
      summary: Liquidación por Garrafa - buque RECOLETA (8 tanques, número ilustrativo)
      value:
        terminal: CAC
        puntoVenta: '0009'
        numero: 123
        tipoLiquidacion: GARRAFA
        fechaEmision: '2023-09-09'
        fechaBillOfLading: '2023-09-08'
        fechaValor: null
        buque:
          nombre: RECOLETA
          numeroViaje: '99990000'
        destino:
          tipo: LOCAL
          pais: ARGENTINA
          refineria: null
        producto: Escalante
        inspectorMedicion: null
        observaciones: null
        operacion:
          estadoCaneriasInicio: LLENAS
          estadoCaneriasFin: LLENAS
          desplazamientoLinea: false
          amarro: null
          conecto: null
          inicioCarga: null
          finDesplazamiento: null
          reinicioCarga: null
          suspensiones: []
          finalizo: null
          desconecto: null
          zarpo: null
        resumen:
          porcentajeAguaYSedimentos: 0.920
          porcentajeAgua: 0.897
          porcentajeSedimentos: 0.023
          salesGm3: 47
          gov: 10840549
          gsv: 10880333
          volumenAguaYSedimentos: 100099
          nsv: 10780234
          densidadBrutaVacio: 0.9138
          densidadNetaVacio: 0.9130
          gradoApiNeto: 23.40
          gsw: 9942448
          nsw: 9842349
        cargadores:
          - razonSocial: COMPAÑIAS ASOCIADAS PETROLERAS S.A.
            cuit: null
            porcentajeParticipacion: 50.00000
            nsv: 5373467
          - razonSocial: CAPEX S.A
            cuit: null
            porcentajeParticipacion: 50.00000
            nsv: 5373467
        tanques:
          - numeroTanque: 51
            inicial: {alturaLiquida: 3000, alturaAguaLibre: 200, tov: 2892925, fw: 214948, gov: 2677388, ctsh: 0.99978, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 20.00, ctpl: 1.00367, gsv: 2687214, correccionTechoFlotante: null}
            final: {alturaLiquida: 2000, alturaAguaLibre: 100, tov: 1937305, fw: 119178, gov: 1817727, ctsh: 0.99978, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 20.00, ctpl: 1.00367, gsv: 1824398, correccionTechoFlotante: null}
            subtotal: {tov: 955620, fw: 95770, gov: 859661, gsv: 862816}
            calidad: null
            cantidadesDespachadas: null
          - numeroTanque: 54
            inicial: {alturaLiquida: 13001, alturaAguaLibre: 200, tov: 9956931, fw: 217158, gov: 9737630, ctsh: 0.99978, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 20.00, ctpl: 1.00367, gsv: 9773367, correccionTechoFlotante: null}
            final: {alturaLiquida: 12001, alturaAguaLibre: 100, tov: 9197689, fw: 141029, gov: 9054668, ctsh: 0.99978, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 20.00, ctpl: 1.00367, gsv: 9087898, correccionTechoFlotante: null}
            subtotal: {tov: 759242, fw: 76129, gov: 682962, gsv: 685469}
            calidad: null
            cantidadesDespachadas: null
          - numeroTanque: 55
            inicial: {alturaLiquida: 7478, alturaAguaLibre: 200, tov: 7928902, fw: 272188, gov: 7655030, ctsh: 0.99978, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 20.00, ctpl: 1.00367, gsv: 7683123, correccionTechoFlotante: null}
            final: {alturaLiquida: 6478, alturaAguaLibre: 100, tov: 6877141, fw: 166888, gov: 6708777, ctsh: 0.99978, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 20.00, ctpl: 1.00367, gsv: 6733398, correccionTechoFlotante: null}
            subtotal: {tov: 1051761, fw: 105300, gov: 946253, gsv: 949725}
            calidad: null
            cantidadesDespachadas: null
          - numeroTanque: 56
            inicial: {alturaLiquida: 13020, alturaAguaLibre: 3500, tov: 9935760, fw: 2685118, gov: 7249047, ctsh: 0.99978, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 20.00, ctpl: 1.00367, gsv: 7275651, correccionTechoFlotante: null}
            final: {alturaLiquida: 12020, alturaAguaLibre: 4000, tov: 9176460, fw: 3066718, gov: 6108398, ctsh: 0.99978, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 20.00, ctpl: 1.00367, gsv: 6130816, correccionTechoFlotante: null}
            subtotal: {tov: 759300, fw: -381600, gov: 1140649, gsv: 1144835}
            calidad: null
            cantidadesDespachadas: null
          - numeroTanque: 59
            inicial: {alturaLiquida: 3500, alturaAguaLibre: 200, tov: 3495434, fw: 203875, gov: 3290835, ctsh: 0.99978, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 20.00, ctpl: 1.00367, gsv: 3302912, correccionTechoFlotante: null}
            final: {alturaLiquida: 3000, alturaAguaLibre: 1000, tov: 2996675, fw: 24625, gov: 2971396, ctsh: 0.99978, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 20.00, ctpl: 1.00367, gsv: 2982301, correccionTechoFlotante: null}
            subtotal: {tov: 498759, fw: 179250, gov: 319439, gsv: 320611}
            calidad: null
            cantidadesDespachadas: null
          - numeroTanque: 65
            inicial: {alturaLiquida: 3000, alturaAguaLibre: 100, tov: 4297744, fw: 225051, gov: 4070942, ctsh: 0.99957, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 29.40, ctpl: 1.00367, gsv: 4085882, correccionTechoFlotante: null}
            final: {alturaLiquida: 4000, alturaAguaLibre: 200, tov: 5702353, fw: 365466, gov: 5334592, ctsh: 0.99957, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 29.40, ctpl: 1.00367, gsv: 5354170, correccionTechoFlotante: null}
            subtotal: {tov: -1404609, fw: -140415, gov: -1263650, gsv: -1268288}
            calidad: null
            cantidadesDespachadas: null
          - numeroTanque: 66
            inicial: {alturaLiquida: 5000, alturaAguaLibre: 200, tov: 7057079, fw: 316612, gov: 6737636, ctsh: 0.99958, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 28.90, ctpl: 1.00367, gsv: 6762363, correccionTechoFlotante: null}
            final: {alturaLiquida: 3000, alturaAguaLibre: 50, tov: 4248623, fw: 105997, gov: 4140886, ctsh: 0.99958, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 28.90, ctpl: 1.00367, gsv: 4156083, correccionTechoFlotante: null}
            subtotal: {tov: 2808456, fw: 210615, gov: 2596750, gsv: 2606280}
            calidad: null
            cantidadesDespachadas: null
          - numeroTanque: 71
            inicial: {alturaLiquida: 4000, alturaAguaLibre: 50, tov: 11474701, fw: 115526, gov: 11147923, ctsh: 0.99978, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 20.00, ctpl: 1.00367, gsv: 11188836, correccionTechoFlotante: {tipo: FRC, volumen: -208753, densidadObservada: 1.0000}}
            final: {alturaLiquida: 2000, alturaAguaLibre: 40, tov: 5689401, fw: 98733, gov: 5589438, ctsh: 0.99978, temperaturaFluido: 10.00, temperaturaAmbiente: 10.00, temperaturaTablaCalibracion: 20.00, ctpl: 1.00367, gsv: 5609951, correccionTechoFlotante: {tipo: FRC, volumen: 0, densidadObservada: 1.0000}}
            subtotal: {tov: 5785300, fw: 16793, gov: 5558485, gsv: 5578885}
            calidad: null
            cantidadesDespachadas: null
        pdf: null