TRANSPORT HUBRevision documental

Auditoria historica y esquema actual

El snapshot inicial se conserva completo. El esquema vigente es el anexo de DATABASE_CHANGELOG, con 53 tablas y 16 migraciones.

Auditoria completa PostgreSQL y modelo historico

Exportacion para revision: 05/11/2026, Europe/Madrid. Contenido completo de los informes fuente, no resumen. No se alteran los originales. Los nombres personales no imprescindibles se sustituyen por etiquetas; los ejemplos tecnicos y la estructura se conservan. No se exportan credenciales ni evidencias crudas. Las conclusiones, contadores y autorizaciones de cada informe son HISTORICOS: no habilitan operaciones. El encargo actual prioriza exclusivamente enumeracion por fechas. La fecha mas reciente y el suplemento de esta revision prevalecen sobre planes anteriores.

Fuentes completas incluidas

Fuente original en VMSHA-256 original
[RUTA INTERNA OMITIDA]3ffe59689f0d98368d6d2d676202850cb6397976ef63b3a046761a4d34824ad3
[RUTA INTERNA OMITIDA]3218f8f0c1878b8a8d4b274c65c0de121268b1ea1b8a5b2dfdae5926bb6ae02a
[RUTA INTERNA OMITIDA]3144a6404b3812ec7801f1c5ddd8de27576dbeb05a77cb3a95023e0f2da07cf9

---

Documento fuente: 06_POSTGRESQL_AUDIT.md

Auditoria PostgreSQL real

05/11/2026. SELECT en transacciones read-only; sin DDL, INSERT/UPDATE, migraciones, importaciones ni reconciliaciones de indices documentales.

Inventario

PostgreSQL 15.19 Debian, base transport_hub de 10 MB, esquema de usuario public. 39 tablas, 386 columnas, 59 indices y 103 restricciones catalogadas. 13 migraciones aplicadas, desde 001_phase_0_5_schema hasta 013_iclientes_production. Inventario completo por columna/tipo/null/default, indices y restricciones en el anexo 16; JSON de evidencia estructural sin filas de secretos o datos comerciales en el dossier tecnico.

GrupoTablas y filas reales
Usuarios/configadmin_users 2 (ambos admin activos), app_config 26, connector_credentials 1, iclientes_credentials 2
Auditoriaapi_requests 21, api_usage_daily 7, connection_tests 23, app_logs 2, technical_lessons 8
Catalogos/plataformacarriers 6, carrier_capabilities 8, known_api_endpoints 9, gls_event_codes 336, gls_product_matrix 24, gls_environments 3
Documentaciondocuments 31, knowledge_articles 31, official_documents 16
Laboratorioiclientes_lab_trials 3 (TEST aceptada, TEST borrador, PRODUCCION aceptada), queries 2, labels 4
Impresionprint_printers 1, print_jobs 6, print_derivatives 5
Politicascache_resources 7, enrichment_profiles 4, api_quota_policies 1
Nucleo vacioshipments 0, parcels 0, tracking_events 0, synchronizations 0
Futuro vacioalerts, cod_records, cod_settlements, invoices, invoice_lines, bank_movements, gls_allowed_services_cache: 0

Los auxiliares puntuales generaron evidencia/API requests sin crear queries del laboratorio: queries=2 no es todo el historial de consultas. Ninguna fila shipments confirma una expedicion porque el repositorio aun no se ingiere.

Relaciones e indices: fortalezas y carencias

Carriers -> shipments/parcels/events/synchronizations; parcels -> shipments; tracking_events -> parcels. Hay FKs y PKs, pero tracking_events no tiene shipment_id directo y las claves carrier son nullable con ON DELETE SET NULL. Eso no basta para historico inmutable con ambito de cuenta/entorno.

Shipments solo tiene id, carrier_id, external_reference, status, source_system y timestamps. No hay cuentas, numero operativo, IDs tipados, cuatro ejes de estado, destinatario, datos comerciales o revisiones. No hay unicidad fuerte carrier/cuenta/entorno/tipo/valor, ni deduplicacion de eventos.

Shipments/tracking_events solo tienen indice PK. api_requests tiene indice provider/product/environment/occurred_at, pero no carrier_account_id. Las cuotas/agregados tampoco discriminan cuenta. No declarar el modelo definitivo ya preparado por existir nombres de tablas.

Documents y knowledge_articles son indices de articulos tecnicos por slug/path, no almacenes de POD/envios. No sobrecargarlos sin distinguir semantica. El catalogo oficial separa originales del proveedor con hashes/procedencia. La KB se descubre en ficheros: 54 articulos antes de esta entrega, frente a 31 en SQL. Es una deuda de indexacion, no perdida de esos ficheros.

Acceso y operacion

Conexion de app por socket local y peer como transport-hub. Rol no superuser, sin CREATEDB ni CREATEROLE. El arranque ejecuta apply_migrations con ese rol; propiedad/permisos DDL deben separarse del rol runtime en fase autorizada. No se han probado permisos destructivos para verificarlo.

Listen localhost, SSL activado para TCP local; puerto 5432 solo loopback. max_connections=100, shared_buffers=128 MiB, autovacuum on, wal_level replica, archive_mode off. Son valores observados, no recomendacion de tuning ni prueba de backup/replicacion. No se midio rendimiento de cargas grandes.

Reutilizacion propuesta

Extender nucleo existente mediante futuras migraciones revisadas; mantener usuarios/config/catalogos/auditoria y evidencias. Anadir solo conceptos que faltan: cuentas, IDs/referencias, snapshots/comercial, observaciones/revisiones, documentos de envio y operaciones/jobs segun diseno. Evitar otro nucleo GLS. Laboratorio no debe convertirse en tabla operativa mediante UPDATEs ad hoc.

Antes de poblar: backup restaurable, reglas de identidad y estados, migracion reversible, pruebas con datos sinteticos e ingesta offline de evidencia autorizada. Ninguna de estas acciones se realiza en esta auditoria.

---

Documento fuente: 08_HISTORICAL_REPOSITORY_DESIGN.md

Repositorio historico persistente: propuesta

No DDL ni implementacion. Evolucionar la unica base transport_hub y el nucleo existente. No limitar a 100 registros ni aplicar purgas automaticas por antiguedad. Conservacion comercial completa, con decisiones explicitas de gobierno de datos/obligaciones aplicables; capacidad indefinida no es una orden de conservar PII eternamente sin revision.

Agrupaciones y relaciones necesarias

ConceptosEntidad propuesta / reutilizacion
Carriercarriers existente, sin implementar otros proveedores.
Cuenta/entorno/secretoscarrier_accounts nueva; IDs de cuenta y secret_ref, nunca UID/Secret en claro; adapter al mecanismo actual.
ExpedicionExtender shipments, no tabla GLS paralela. ID TH estable; carrier_account_id, environment y numero externo visible.
Identificadoresshipment_identifiers con namespace/tipo/valor/cuenta; indices de lookup y unicidad solo de IDs fuertes en su ambito.
Referencias comercialesshipment_references: pedido, factura, ERP, TH y C/0/S/otros GLS, con tipo/valor/fuente. Multiples referencias por envio y viceversa.
Remitente/destinatario/contactosSnapshot por rol ligado a envio, no libreta de clientes paralela a intranet. Centros de expedicion configurables futuros, snapshot inmutable de lo usado.
Servicio/condicionesCodigos/literales originales y snapshot comercial dentro del agregado; tablas de catalogo solo donde eviten duplicacion real.
Bultosparcels existente, IDs por bulto, pesos/medidas declarados y observados con fuente.
Eventos/incidencias/entregastracking_events ampliada con shipment_id, tipo/namespace y revisiones; vistas/proyecciones, no tabla por cada literal.
Reembolsocod_records existente ampliable; importes declarados/moneda/procedencia. No inferir cobro/liquidacion por entrega.
Documentos envio/POD/etiquetasshipment_documents como metadata/relacion a activos privados; reutilizar cifrado/hash/lectura de originales. documents/knowledge_articles actuales siguen siendo indices tecnicos, no otro nucleo de envios.
Operaciones administrativasshipment_operations: intencion/aceptacion/rechazo/desconocido y evidencias; separadas de api_requests (una operacion puede tener varios HTTP).
Observacionesshipment_observations append-only, snapshot normalizado versionado y referencia al original cifrado.
Sincronizacion/importacionExtender synchronizations + items/checkpoints/leases si hacen falta; un motor comun, no nuevo sistema por conector.
Cuotas/auditoria/erroresapi_requests/api_usage_daily/api_quota_policies/alerts existentes, con cuenta/origen/coste y estado de captura/ingesta.

No se decide crear 25 tablas por 25 conceptos. El detalle fisico y migracion se revisara tras aprobar identidad, privacidad y consultas principales.

Identidad y unicidad

Numero completo GLS es la primera columna operativa y campo de API, no la PK. Si GLS lo devuelve se preserva literal; no construir agencia+CODEXP para cualquier envio sin regla validada. Codigos como cadenas, ceros intactos. Clave fuerte: carrier/cuenta/entorno/tipo/valor; CODEXP no globalmente unico. GUID, barcode y numero de bulto se tipan y correlacionan con evidencia.

No fusionar por nombre, direccion, albaran o referencia no unica. Devoluciones pueden heredar referencias y tener ID propio. Relacion padre/hija tipada, no sobrescribir el original. Conflictos de identificadores van a revision, no upsert silencioso. Cuenta anterior nunca comparte UID por suposicion.

Campos funcionales y comerciales

Shipment: fecha original expedicion y observaciones, servicio/horario/portes, bultos/peso/moneda, pais, centro remitente, origen transport_hub/gls_extranet/ imported/unknown y grado de confianza. Lookup API no demuestra canal de alta.

Snapshot de destinatario: nombre, direccion, CP, poblacion, provincia, pais, telefono, movil, correo y observaciones, todos con fuente y campos presentes. Null/desconocido/no devuelto no equivale a cadena vacia o cero. Nunca inventar contactos que no entregue GLS/intranet. Direccion snapshot no cambia al editar despues un centro o cliente en intranet.

Referencias pedido/factura/ERP no crean facturas ni conciliacion; intranet conserva autoridad sobre sus datos comerciales. Prioridad de fuentes configurable por campo, sin sobreescribir ERP con una respuesta parcial GLS. Reembolso declarado con decimal exacto y moneda original; estado de cobro, importe cobrado y conciliacion desconocidos hasta evidencia especifica.

Cuatro dimensiones de estado

DimensionValores conceptuales y reglas
Administrativocreada, anulacion_solicitada, anulacion_aceptada, anulacion_rechazada, desconocida. No confundir estado del request HTTP con resultado de negocio.
Logisticograbada, recogida, en_transito, en_reparto, entregada, incidencia, devuelta, desconocido; derivados de codigos/tipos/versiones acreditados.
Cicloabierta, finalizada, cerrada, desconocida; terminalidad respaldada por fuente/motivo, no por antiguedad.
Resultadoentregada, anulada, devuelta, otros, pendiente/desconocido. Cierre definitivo no asigna entregada.

TH-P-001 valida: admin aceptada por Anula0; logistica API inmediata grabada; resultado anulada y ciclo cerrado comunicados desde Extranet despues. Almacenar cada observacion y su nivel de evidencia, no fabricar evento API5. La reduccion de bultos/peso a 0 comunicada de Extranet es snapshot posterior, no borrado del valor declarado original 1/1 ni prueba de ausencia de costes.

Acontecimientos y correcciones

Conservar tipo, codigo y descripcion originales, API/catalogo/namespace, fecha literal y timezone conocida o incertidumbre, agencia/instalacion, atributos complementarios, fecha de recuperacion e ID externo si existe. ESTADO, ENTREGA, POD, INCIDENCIA, SOLUCION, FACTURA y demas tipos no comparten un espacio numerico. LocationCode no es estado. Evento administrativo posterior no devuelve una entregada a pendiente por ser el mas reciente.

ID externo estable permite enlazar revisiones. Si falta, huella canonica por envio/tipo/codigo/fecha/instalacion identifica ocurrencia candidata y hash de contenido distingue cambios. No deduplicar solo por codigo. Una correccion genera revision/supersedes y nueva observacion, no UPDATE destructivo del acontecimiento historico. Si dos ocurrencias pueden coincidir sin ID estable, conservar incertidumbre/original y evitar fusion automatica.

Los primeros/ultimos vistos, fecha efectiva, fecha carrier y fecha de ingesta son distintos. Versionar parser/mapeo y reprocesar originales localmente sin volver a gastar llamadas. Desconocidos conservan datos, no se descartan.

Una recuperacion nueva de un evento antiguo no reabre automaticamente un envio cerrado: ordenar por evidencia/fecha efectiva, no solo fecha de HTTP. TH-P-001 exige conservar Anula0 y el cierre Extranet posterior aunque una respuesta inmediata aun repita el evento GRABADO del dia anterior. Prioridad entre fuentes y resolucion de conflictos debe ser explicita y versionada.

Captura, ingesta e integridad

HTTP real -> captura original cifrada duradera -> auditoria -> validacion/ parser tipado -> transaccion de agregado/eventos/referencias/snapshot. Si falla BD/parser, inbox duradera para reprocesado LOCAL; no repetir HTTP para obtener de nuevo una respuesta ya conservada. Registrar recibido, procesado, aplicado y error por separado. Toda respuesta util autorizada alimenta el repositorio, incluidas altas/consultas manuales, con completitud.

No ingestar desde la copia sanitizada: el sanitizador actual limita listas y oculta PII/fechas que si son necesarias al modelo. Original cifrado es la evidencia autorizada; log/copia tecnica no es fuente completa de negocio. Vacio de lookup no prueba borrado; no eliminar una observacion positiva previa.

Activos privados

Original bytes inmutable, SHA-256, MIME detectado, tamano, cuenta/envio, operacion/fuente, fechas, formato, numero de paginas/dimensiones cuando aplica, estado de integridad, clave/version de cifrado y ruta privada. Nunca ruta arbitraria proporcionada por cliente ni descarga publica del filesystem.

Deduplicar binarios por hash dentro de dominio autorizado; multiples vinculos metadata al mismo activo sin filtrar existencia entre cuentas. Version nueva de POD/etiqueta crea activo nuevo, no reemplaza original. Referencias URL privadas/firmadas no se confunden con POD descargado/verificado.

Indices, privacidad y retencion

Indices futuros sobre cuenta/entorno/numero, IDs fuertes, fecha, estado, terminalidad, next_sync_at y eventos envio/fecha. Referencias tipadas con indice de busqueda, no unicidad universal. Paginacion local estable por fecha+ID. Particionar solo con volumen/medicion, no una tabla por mes ahora.

Telefonos/movil/email y direccion sensible requieren cifrado/acceso por campo; nombre/CP/poblacion para busqueda parcial necesitan una decision explicita: propuesta de columnas operativas minimas indexables con roles restringidos, cifrado de volumen/backups, resto sensible cifrado. Fernet no permite ILIKE sobre su ciphertext. No prometer busqueda parcial cifrada sin diseno aparte.

Historico sin purga automatica. TTL de frescura no borra registros ni prueba retencion de GLS. Gobierno de datos define revisiones, anonimizar/eliminar si corresponde y evidencia de la decision, sin ocultar trazabilidad tecnica.

---

Documento fuente: 16_POSTGRESQL_SCHEMA_INVENTORY.md

Inventario completo PostgreSQL observado

05/11/2026. Solo lectura de information_schema/pg_catalog. 39 tablas,386 columnas,59 indices,103 restricciones; definiciones documentales, NO script de migracion.

admin_users

Filas observadas: 2.

ColumnaTipoNullableDefault
idbigintNOnextval('admin_users_id_seq'::regclass)
usernametextNO-
password_hashtextNO-
created_attimestamp with time zoneNOnow()
last_login_attimestamp with time zoneYES-
display_nametextNO''::text
emailtextNO''::text
roletextNO'admin'::text
activebooleanNOtrue
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

alerts

Filas observadas: 0.

ColumnaTipoNullableDefault
idbigintNOnextval('alerts_id_seq'::regclass)
severitytextNO'info'::text
scopetextNO'system'::text
titletextNO-
messagetextNO-
statustextNO'open'::text
created_attimestamp with time zoneNOnow()
resolved_attimestamp with time zoneYES-

Indices:

Restricciones:

api_quota_policies

Filas observadas: 1.

ColumnaTipoNullableDefault
producttextNO-
environmenttextNO-
periodtextNO-
limit_countintegerNO-
limit_sourcetextNO-
reserve_countintegerNO0
warn_thresholdsjsonbNO'[50, 75, 90, 100]'::jsonb
activebooleanNOtrue
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

api_requests

Filas observadas: 21.

ColumnaTipoNullableDefault
idbigintNOnextval('api_requests_id_seq'::regclass)
providertextNO-
producttextNO-
environmenttextNO-
endpoint_logicaltextNO-
methodtextNO-
occurred_attimestamp with time zoneNOnow()
http_codeintegerYES-
duration_msintegerNO0
resulttextNO-
items_requestedintegerNO0
items_returnedintegerNO0
origintextNO-
actortextYES-
correlation_iduuidYES-
quota_computednumericYES-
error_sanitizedtextYES-
details_jsonjsonbNO'{}'::jsonb

Indices:

Restricciones:

api_usage_daily

Filas observadas: 7.

ColumnaTipoNullableDefault
usage_datedateNO-
producttextNO-
environmenttextNO-
requestsintegerNO0
successesintegerNO0
errorsintegerNO0
items_processedintegerNO0
avg_duration_msnumericNO0
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

app_config

Filas observadas: 26.

ColumnaTipoNullableDefault
keytextNO-
valuetextNO-
typetextNO'string'::text
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

app_logs

Filas observadas: 2.

ColumnaTipoNullableDefault
idbigintNOnextval('app_logs_id_seq'::regclass)
leveltextNO-
eventtextNO-
messagetextNO-
user_idbigintYES-
details_jsonjsonbNO'{}'::jsonb
created_attimestamp with time zoneNOnow()

Indices:

Restricciones:

app_migrations

Filas observadas: 13.

ColumnaTipoNullableDefault
versiontextNO-
applied_attimestamp with time zoneNOnow()

Indices:

Restricciones:

bank_movements

Filas observadas: 0.

ColumnaTipoNullableDefault
idbigintNOnextval('bank_movements_id_seq'::regclass)
movement_datedateYES-
concepttextYES-
amountnumericYES-
currencytextNO'EUR'::text
matched_statustextNO'unmatched'::text
created_attimestamp with time zoneNOnow()

Indices:

Restricciones:

cache_resources

Filas observadas: 7.

ColumnaTipoNullableDefault
idbigintNOnextval('cache_resources_id_seq'::regclass)
resource_keytextNO-
carrier_codetextNO'generic'::text
resource_nametextNO-
origintextNO'CACHE'::text
ttl_secondsintegerNO0
policytextNO'manual'::text
last_obtained_attimestamp with time zoneYES-
expires_attimestamp with time zoneYES-
enabledbooleanNOtrue
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

carrier_capabilities

Filas observadas: 8.

ColumnaTipoNullableDefault
idbigintNOnextval('carrier_capabilities_id_seq'::regclass)
carrier_idbigintNO-
capabilitytextNO-
statustextNO'planned'::text
depends_ontextNO''::text
notestextNO''::text
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

carriers

Filas observadas: 6.

ColumnaTipoNullableDefault
idbigintNOnextval('carriers_id_seq'::regclass)
codetextNO-
nametextNO-
statustextNO'planned'::text
notestextNO''::text
created_attimestamp with time zoneNOnow()
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

cod_records

Filas observadas: 0.

ColumnaTipoNullableDefault
idbigintNOnextval('cod_records_id_seq'::regclass)
shipment_idbigintYES-
parcel_idbigintYES-
carrier_idbigintYES-
amountnumericYES-
currencytextNO'EUR'::text
statustextNO'pending'::text
created_attimestamp with time zoneNOnow()
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

cod_settlements

Filas observadas: 0.

ColumnaTipoNullableDefault
idbigintNOnextval('cod_settlements_id_seq'::regclass)
carrier_idbigintYES-
settlement_referencetextYES-
period_startdateYES-
period_enddateYES-
amountnumericYES-
statustextNO'pending'::text
created_attimestamp with time zoneNOnow()
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

connection_tests

Filas observadas: 23.

ColumnaTipoNullableDefault
idbigintNOnextval('connection_tests_id_seq'::regclass)
test_typetextNO-
providertextNO-
environmenttextNO-
started_attimestamp with time zoneNO-
finished_attimestamp with time zoneNO-
resulttextNO-
http_codeintegerYES-
sanitized_messagetextNO-
duration_msintegerNO-
details_jsonjsonbNO'{}'::jsonb
launched_bybigintYES-
endpoint_logicaltextYES-
diagnostic_run_iduuidYES-
classificationtextYES-

Indices:

Restricciones:

connector_credentials

Filas observadas: 1.

ColumnaTipoNullableDefault
providertextNO-
environmenttextNO-
client_id_ciphertexttextNO-
client_secret_ciphertexttextNO-
created_attimestamp with time zoneNOnow()
updated_attimestamp with time zoneNOnow()
last_validated_attimestamp with time zoneYES-
validation_statustextNO'configured_not_tested'::text

Indices:

Restricciones:

documents

Filas observadas: 31.

ColumnaTipoNullableDefault
idbigintNOnextval('documents_id_seq'::regclass)
slugtextNO-
titletextNO-
pathtextNO-
document_typetextNO'markdown'::text
categorytextNO'General'::text
checksumtextYES-
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

enrichment_profiles

Filas observadas: 4.

ColumnaTipoNullableDefault
idbigintNOnextval('enrichment_profiles_id_seq'::regclass)
carrier_codetextNO'generic'::text
leveltextNO-
descriptiontextNO''::text
estimated_extra_callsintegerNO0
rules_jsonjsonbNO'{}'::jsonb
activebooleanNOtrue
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

gls_allowed_services_cache

Filas observadas: 0.

ColumnaTipoNullableDefault
idbigintNOnextval('gls_allowed_services_cache_id_seq'::regclass)
origintextNO-
destinationtextNO-
contact_idtextYES-
producttextYES-
servicetextYES-
queried_attimestamp with time zoneNOnow()
response_hashtextNO-
activebooleanNOtrue

Indices:

Restricciones:

gls_environments

Filas observadas: 3.

ColumnaTipoNullableDefault
codetextNO-
nametextNO-
labeltextNO-
activebooleanNOfalse
lockedbooleanNOtrue
statetextNO'sin_validar'::text
last_validated_attimestamp with time zoneYES-
oauth_urlsjsonbNO'{}'::jsonb
base_urlsjsonbNO'{}'::jsonb
enabled_apisjsonbNO'[]'::jsonb
backend_authorizationjsonbNO'{}'::jsonb
limits_jsonjsonbNO'{}'::jsonb
config_jsonjsonbNO'{}'::jsonb
observationstextNO''::text
created_attimestamp with time zoneNOnow()
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

gls_event_codes

Filas observadas: 336.

ColumnaTipoNullableDefault
codetextNO-
languagetextNO-
descriptiontextNO-
first_seen_attimestamp with time zoneNOnow()
last_seen_attimestamp with time zoneNOnow()
activebooleanNOtrue
raw_hashtextNO-

Indices:

Restricciones:

gls_product_matrix

Filas observadas: 24.

ColumnaTipoNullableDefault
producttextNO-
environmenttextNO-
statustextNO'sin_validar'::text
last_test_idbigintYES-
evidencetextNO''::text
pendingtextNO''::text
credential_ref_providertextYES-
credential_ref_environmenttextYES-
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

iclientes_credentials

Filas observadas: 2.

ColumnaTipoNullableDefault
environmenttextNO-
secret_reftextNO-
descriptiontextNO''::text
created_attimestamp with time zoneNOnow()
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

iclientes_lab_labels

Filas observadas: 4.

ColumnaTipoNullableDefault
iduuidNO-
trial_iduuidNO-
mime_typetextNO-
size_bytesintegerNO-
sha256textNO-
encrypted_pathtextNO-
created_attimestamp with time zoneNOnow()
image_widthintegerYES-
image_heightintegerYES-
declared_dpijsonbYES-
source_metadatajsonbNO'{}'::jsonb

Indices:

Restricciones:

iclientes_lab_queries

Filas observadas: 2.

ColumnaTipoNullableDefault
iduuidNO-
trial_iduuidNO-
created_byintegerNO-
nonce_hashtextNO-
statetextNO'sending'::text
created_attimestamp with time zoneNOnow()
finished_attimestamp with time zoneYES-
api_request_idbigintYES-
http_statusintegerYES-
duration_msintegerYES-
evidence_originaltextYES-
evidence_sanitizedtextYES-
summaryjsonbNO'{}'::jsonb
operationtextNO'GetExpCli'::text

Indices:

Restricciones:

iclientes_lab_trials

Filas observadas: 3.

ColumnaTipoNullableDefault
iduuidNO-
environmenttextNO'test'::text
referencetextNO-
created_byintegerNO-
form_ciphertexttextNO-
confirm_hashtextNO-
statetextNO'draft'::text
created_attimestamp with time zoneNOnow()
submitted_attimestamp with time zoneYES-
finished_attimestamp with time zoneYES-
api_request_idbigintYES-
http_statusintegerYES-
duration_msintegerYES-
result_returntextYES-
guidtextYES-
codexptextYES-
barcodetextYES-
evidence_originaltextYES-
evidence_sanitizedtextYES-
summaryjsonbNO'{}'::jsonb

Indices:

Restricciones:

invoice_lines

Filas observadas: 0.

ColumnaTipoNullableDefault
idbigintNOnextval('invoice_lines_id_seq'::regclass)
invoice_idbigintYES-
shipment_idbigintYES-
descriptiontextYES-
amountnumericYES-
details_jsonjsonbNO'{}'::jsonb

Indices:

Restricciones:

invoices

Filas observadas: 0.

ColumnaTipoNullableDefault
idbigintNOnextval('invoices_id_seq'::regclass)
carrier_idbigintYES-
invoice_numbertextYES-
invoice_datedateYES-
amountnumericYES-
statustextNO'pending'::text
created_attimestamp with time zoneNOnow()
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

knowledge_articles

Filas observadas: 31.

ColumnaTipoNullableDefault
idbigintNOnextval('knowledge_articles_id_seq'::regclass)
slugtextNO-
titletextNO-
pathtextNO-
categorytextNO'General'::text
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

known_api_endpoints

Filas observadas: 9.

ColumnaTipoNullableDefault
idbigintNOnextval('known_api_endpoints_id_seq'::regclass)
carrier_codetextNO'generic'::text
producttextNO-
endpoint_keytextNO-
methodtextNO-
pathtextNO-
environmenttextNO'sandbox'::text
authenticationtextNO'OAuth 2.0 client_credentials'::text
descriptiontextNO''::text
parameters_jsonjsonbNO'{}'::jsonb
examples_jsonjsonbNO'{}'::jsonb
limitationstextNO''::text
quota_knowntextNO''::text
current_statustextNO'sin_validar'::text
last_test_idbigintYES-
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

official_documents

Filas observadas: 16.

ColumnaTipoNullableDefault
idbigintNOnextval('official_documents_id_seq'::regclass)
carrier_codetextNO'generic'::text
integration_familytextNO-
original_nametextNO-
mime_typetextNO-
size_bytesbigintNO-
sha256textNO-
incorporated_attimestamp with time zoneNOnow()
versiontextYES-
document_datedateYES-
provenancetextNO-
descriptiontextNO''::text
statustextNO'incorporado'::text
physical_pathtextNO-
is_vendor_officialbooleanNOtrue
original_or_derivativetextNO'original'::text
derivative_of_idbigintYES-
downloadablebooleanNOtrue
created_attimestamp with time zoneNOnow()
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

parcels

Filas observadas: 0.

ColumnaTipoNullableDefault
idbigintNOnextval('parcels_id_seq'::regclass)
shipment_idbigintYES-
carrier_idbigintYES-
tracking_numbertextYES-
carrier_tracking_idtextYES-
statustextNO'unknown'::text
last_event_attimestamp with time zoneYES-
created_attimestamp with time zoneNOnow()
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

print_derivatives

Filas observadas: 5.

ColumnaTipoNullableDefault
iduuidNO-
label_iduuidNO-
methodtextNO-
original_sha256textNO-
payload_sha256textNO-
payload_bytesintegerNO-
encrypted_pathtextNO-
recipejsonbNO-
created_attimestamp with time zoneNOnow()

Indices:

Restricciones:

print_jobs

Filas observadas: 6.

ColumnaTipoNullableDefault
iduuidNO-
label_iduuidNO-
trial_iduuidNO-
printer_idtextNO-
derivative_iduuidYES-
methodtextNO-
copiesintegerNO-
nonce_hashtextNO-
created_byintegerYES-
actortextNO-
original_sha256textNO-
parametersjsonbNO-
statetextNO'preparing'::text
created_attimestamp with time zoneNOnow()
started_attimestamp with time zoneYES-
finished_attimestamp with time zoneYES-
duration_msintegerYES-
backend_idtextYES-
errortextYES-
physical_resulttextNO'unreviewed'::text
reviewed_attimestamp with time zoneYES-
reviewed_byintegerYES-

Indices:

Restricciones:

print_printers

Filas observadas: 1.

ColumnaTipoNullableDefault
idtextNO-
nametextNO-
hosttextNO-
portintegerNO-
queuetextNO-
enabledbooleanNOtrue
max_copiesintegerNO5
thresholdintegerNO128
default_methodtextNO'cups'::text
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

shipments

Filas observadas: 0.

ColumnaTipoNullableDefault
idbigintNOnextval('shipments_id_seq'::regclass)
carrier_idbigintYES-
external_referencetextYES-
statustextNO'draft'::text
source_systemtextYES-
created_attimestamp with time zoneNOnow()
updated_attimestamp with time zoneNOnow()

Indices:

Restricciones:

synchronizations

Filas observadas: 0.

ColumnaTipoNullableDefault
idbigintNOnextval('synchronizations_id_seq'::regclass)
carrier_idbigintYES-
sync_typetextNO-
statustextNO'pending'::text
started_attimestamp with time zoneYES-
finished_attimestamp with time zoneYES-
summary_jsonjsonbNO'{}'::jsonb
created_attimestamp with time zoneNOnow()

Indices:

Restricciones:

technical_lessons

Filas observadas: 8.

ColumnaTipoNullableDefault
idbigintNOnextval('technical_lessons_id_seq'::regclass)
lesson_keytextNO-
titletextNO-
bodytextNO-
severitytextNO'info'::text
carrier_codetextNO'generic'::text
sourcetextNO'system'::text
evidence_test_idbigintYES-
created_attimestamp with time zoneNOnow()

Indices:

Restricciones:

tracking_events

Filas observadas: 0.

ColumnaTipoNullableDefault
idbigintNOnextval('tracking_events_id_seq'::regclass)
parcel_idbigintYES-
carrier_idbigintYES-
event_codetextYES-
normalized_statustextYES-
event_attimestamp with time zoneYES-
raw_hashtextYES-
details_jsonjsonbNO'{}'::jsonb
created_attimestamp with time zoneNOnow()

Indices:

Restricciones:

Migraciones aplicadas

No hay DDL ejecutado. Para reutilizacion/carencias ver informe06; para modelo futuro informe08.

---

Suplemento: aptitud para fechas y conciliacion

05/11/2026. Diseno, no implementacion ni DDL. El anexo anterior contiene el esquema REAL completo, no el modelo propuesto. 39 tablas / 386 columnas / 59 indices / 103 restricciones / 13 migraciones. Ninguna migracion nueva.

Evaluacion por necesidad

NecesidadSituacion actualAjuste propuesto para siguiente fase
Fechas operativasshipments solo created_at/updated_at TH; no fecha GLSSeparar fechas y procedencia; indices por cuenta/fecha/ID.
Destinatario + fecha aproximadaNo snapshot operativo en shipmentsSnapshot inmutable, texto de busqueda autorizado/indexado y control de acceso.
Estado e incidenciasstatus generico sin mapping versionadoEstados administrativos/logisticos/resultados separados y eventos originales.
Historico completoNucleo vacio; laboratorio/evidencias no son todo el historicoIngesta trazable, cobertura por ventana y retencion gobernada, sin purga automatica.
Varias cuentas GLSNo carrier_account_id en nucleo/cuotascarrier_accounts con entorno/agencia/cliente/secret_ref; claves scoped.
Varios carrierscarriers existe, FK nullableReutilizar core y adapters; no crear repositorio paralelo GLS.
IDs y deduplicacionexternal_reference y tracking_number sin unicidad de cuentaIDs tipados y unicidad carrier/cuenta/entorno/tipo/valor donde este acreditada.
Incrementalsynchronizations vacia, sin checkpoints/completitudItems/runs/cobertura/watermarks por tipo y cuenta; solape y replay local.
EventosSolo parcel_id, sin unicidad de ocurrencia robustashipment_id, namespace, version, fecha efectiva/captura e identidad de ocurrencia.
POD progresivodocuments indexa Markdown; labels laboratorio independientesActivos privados, hashes, enlaces a envio/cuenta y estado de recuperacion/integridad.

No cabe afirmar que las tablas existentes ya soporten la busqueda operativa completa. Si se comienza despues de autorizacion, reutilizar identidad/core y ampliar sin dependencia de un nombre SOAP concreto: el adapter produce resultados/cobertura.

Fechas que deben distinguirse

Campo conceptual propuestoSignificado / fuenteTratamiento
th_created_atAlta del registro en THNo usar como fecha GLS ni para un backfill historico.
gls_recorded_atGrabacion efectiva en GLSSolo poblar si hay campo/evento inequívoco documentado.
shipment_dateFecha expedicion/servicioConservar literal, tipo DATE si solo fecha y semantica de origen.
picked_up_atRecogidaEvento acreditado; no deducir de etiqueta/GRABADO.
first_movement_atPrimer movimiento logisticoEvento acreditado; excluir eventos meramente administrativos.
delivered_atEntrega efectivaNo confundir prevista FPEntrega con efectiva ni entrega de retorno con ida.
cancelled_atAnulacion efectivaDiferente de solicitud/aceptacion HTTP y posterior cierre definitivo.
last_event_atUltimo acontecimiento efectivoNo significa entrega ni fecha de consulta; puede ser FACTURA.
last_successful_sync_atUltima aplicacion valida de datos remotosUn intento fallido no actualiza esta fecha de frescura.
last_sync_attempt_atUltimo intento, tambien fallidoSeparado para diagnostico/error/reanudacion.

Cada dato lleva fuente, campo original, observacion, precision y timezone/calidad. Un campo sin dato permanece NULL/desconocido, no se rellena con now() ni con otra fecha. Guardar instantes como timestamptz UTC y presentar Europe/Madrid; si solo hay fecha sin hora no inventar medianoche como instante real. Una hora sin zona requiere politica explicita y conservar incertidumbre, incluidos cambios de horario.

Filtro por defecto propuesto: Fecha de expedicion, si GLS acredita su campo; alternativa de grabacion si esa es la semantica real del contrato. La UI debe nombrar el criterio y mostrar origen/ausencias, nunca un COALESCE silencioso entre fechas. Permitir criterios explicitos de grabacion, recogida, entrega, ultimo evento o alta TH. Para datos sin fecha operativa ofrecer vista separada, no esconderlos como si no existieran.

Para un intervalo de fechas inclusivas en la UI, convertir localmente al rango [inicio del primer dia, inicio del dia siguiente al ultimo) en Europe/Madrid. Ejemplo de contraste: 04/11/2025-04/12/2025 seria [2025-11-04T00:00+01:00, 2025-12-05T00:00+01:00). Esto es una propuesta SQL local, NO un payload GLS ni afirmacion de que Extranet o GetEnvios utilicen esos extremos. Para agosto el offset cambia; calcular con zona IANA, no restar 24 horas fijas.

Indices y consultas futuras

No se ejecuta CREATE INDEX/EXTENSION ni se optimiza una carga inexistente.

Conciliacion de conjuntos sin duplicados

Crear en una fase futura un concepto de ejecucion/ventana: cuenta, entorno, fuente, criterio temporal, inicio/fin, timezone, filtros de estados/canales, timestamp, numero de paginas, cursor/total, hash de IDs, version de contrato y grado de cobertura. Cobertura = desconocida / parcial / completa / fallo; un HTTP 200 no implica completa.

R = IDs de GLS de una ventana completamente recuperada; L = IDs locales del mismo ambito y criterio. R-L = pendientes de importar; R interseccion L = candidatos a refresco/verificacion; L-R = discrepancias que deben investigarse, NO bajas automaticas. Si R es parcial/desconocido, L-R no permite afirmar ausencia remota. No usar un limite de pagina o una cache vencida como conjunto completo. Los 61 de Extranet son referencia de contraste, no contenido de R hasta recibir los IDs por una fuente autorizada.

Clasificar tambien registro incompleto (faltan ficha/eventos/fechas), estado obsoleto (segun politica SLA y ultima sync correcta), documento pendiente y conflicto de identidad. Deduplicar por ID fuerte scoped; relacionar devoluciones, sin fusion por nombre/referencia. Toda ingesta atomica e idempotente; parser aplicable a originales cifrados, no copias sanitizadas truncadas. Un fallo posterior a la captura se reejecuta localmente sin HTTP.

Un checkpoint se avanza solo al consolidar una ventana completa. Distinguir watermark de descubrimiento, de refresco y de POD. Reservar solape retrospectivo configurable para publicaciones tardias/cambios, sin fijar arbitrariamente cuantos dias bastan. Si GLS solo filtra fecha de expedicion, refrescar tambien activos antiguos fuera de ayer; no confiar en ese filtro como feed de modificaciones ni omitir incidencias antiguas.

Condiciones para comenzar sin redisenar despues

Modelo, consultas PostgreSQL, deduplicacion, parser offline, capturas/replay y estados de completitud pueden desarrollarse tras otra aprobacion. Antes: backup/restauracion probados, migracion revisada, semantica de identidad/fechas, datos sinteticos y privacidad. El adapter debe distinguir discover(window) (capacidad pendiente) de refresh_known(ids) (capacidad documentada), con resultado y cobertura explicitos. No habilitar discover ni prometer historico completo hasta contrato/prueba GLS. No implementar ahora; la peticion actual se limita a evaluar y documentar.

Actualizacion aplicada de etapa D

DATABASE_CHANGELOG

Etapa D - migracion 018_carrier_execution

Esquema aditivo, sin borrar ni duplicar el repositorio existente.

17 a 18 probado con cuatro migradores concurrentes y filas anteriores intactas. Backup previo restaurado en base temporal: 54 tablas, 111 indices, hashes y recuentos coincidentes. Tras la migracion se han verificado 56 tablas y 115 indices. El resultado aplicado se comprueba en STAGE_D_REPORT y evidencia privada. Rollback de aplicacion al codigo anterior, conservando el esquema aditivo. No ejecutar DOWN ni restaurar la BD activa sin revision y autorizacion.