Skip to content

Terminal v3 (pricing de lanes de flete)

Producto de inteligencia de tarifas de flete por lane (hub origen → corredor destino). Un pipeline de SQL versionado por release materializa la tabla terminal_v3.datarelease_<fecha> en solvento-risk-analytics-prod a partir de los modelos dbt de Syntage. Se construye a mano corriendo un make.

Terminal arrastra historia y deprecaciones (leer antes de tocar)

Terminal pasó por muchas versiones con manejo de versiones/escala irregular, y quedó ruido de cosas deprecadas no señalizadas. Lo vigente y oficial es este repo (terminal). Está deprecado (no usar como referencia):

  • El cálculo de terminal en dbt-etls (models/terminal_client/, models/terminal_market/, tabla …terminal.client_dbt) — eliminado del repo el 2026-09-11.
  • El export a Supabase vía Terminal_Uploader — apagado el 2026-09-11 (Supabase borrado, front de Lovable despublicado, scheduler eliminado). Ver Apagado Terminal → Supabase.
  • Versiones/repos viejos de "terminal" dispersos.

La vía de consumo viva es Metabase (corregido 2026-07-25)

El front de Terminal consume cards de Metabase a través de solvento-api. Los IDs efectivos por ambiente viven en flux/app-config (apps/<env>/solvento-api-values.yaml, variables DATA_ACCESS_QUESTION_ID_*), no en los defaults del repo solvento-api.

Hasta el 2026-07-25 esta ficha decía lo contrario (Metabase deprecado, Supabase vivo). Era incorrecto y mandó a diagnosticar por el camino equivocado. Los releases/*/metabase/ sí siguen siendo legado, pero eso es otra cosa: se refiere al despliegue documental del modelo, no al consumo.

¿Llegaste hasta acá desde…?

Si ves… Eso sale de… La lógica está en…
Tarifa / precio por km / intervalos por lane solvento-risk-analytics-prod.terminal_v3.datarelease_<fecha> repo terminal, releases/<fecha>/sql/
Volumen estimado de una lane (corregido por rezago SAT) terminal_v3.datarelease_<fecha> (columnas volume_estimated, …) releases/<fecha>/sql/02_…_volume_maturation.sql (modelo Weibull)
Tarifas de lanes en el front de la app cards de Metabase del modelo del release, servidas por solvento-api IDs por ambiente en flux/app-config → apps/<env>/solvento-api-values.yaml
El front muestra datos viejos (un mes que ya debería haber pasado) Puede ser la tabla o el ID de la card. Ver Si falla / está desactualizado Primero verificar MAX(departure_month) en BQ; recién después mirar los question IDs
terminal.client_dbt en dbt-etls Eliminado el 2026-09-11 Ya no existe — ver Apagado Terminal → Supabase

Resumen

Campo Valor
Qué es Pricing de lanes de flete (hub → destino): tarifa promedio, precio/km, intervalos, volumen
Salida solvento-risk-analytics-prod.terminal_v3.datarelease_<YYYYMMDD> (+ _customers, _volume_weekly/monthly)
Patrón Pipeline de SQL versionado por release, corrido a mano (make) — no hay scheduler que lo construya
Lee de dbt-analytics (analytics_engineering_core.fact_syntage_invoices*, _intermediate.int_carta_porte), analiticas.precios_diesel, syntage.distancias_municipios
Grano de datarelease una fila por (departure_month, hub_origin, destination)
Criticidad 🟡 Media (producto analítico; build manual)
Consumo Front de la app vía solvento-api → cards de Metabase. IDs por ambiente en flux/app-config
Autor/dueño Angel Gutierrez · coordinación de datos: Martin
Repo terminal · config de consumo: flux/app-config

Diagrama de linaje

graph LR
    RAW[("solvento-data-prod.syntage<br/>invoices · cp_* · distancias_municipios")]
    DSL[("analiticas.precios_diesel<br/><i>diesel/banxico scrapers</i>")]

    subgraph an["dbt-analytics · solvento-risk-analytics-prod"]
        INT["analytics_engineering_intermediate<br/>int_carta_porte"]
        CORE["analytics_engineering_core<br/>fact_syntage_invoices(_items)"]
    end

    subgraph term["repo terminal · make &lt;fecha&gt; (manual)"]
        S0["00_release · approved_lanes + datarelease"]
        S1["01_..._customers · variante clientes"]
        S2["02_..._volume_maturation · Weibull"]
    end

    OUT[("solvento-risk-analytics-prod<br/>terminal_v3.datarelease_&lt;fecha&gt;")]

    subgraph cons["consumo · vía viva"]
        MB["Cards de Metabase<br/>modelo del release"]
        API["solvento-api<br/>DATA_ACCESS_QUESTION_ID_*"]
        FRONT["Front de Terminal"]
    end

    CFG["flux/app-config<br/>apps/&lt;env&gt;/solvento-api-values.yaml"]

    DEP["❌ dbt-etls terminal_client<br/>❌ Supabase vía Terminal_Uploader<br/>APAGADO 2026-09-11"]

    RAW --> INT --> S0
    RAW --> CORE --> S0
    DSL --> S0
    S0 --> S1
    S0 --> S2 --> OUT
    OUT --> MB --> API --> FRONT
    CFG -.->|define el ID por ambiente| API
    OUT -.->|ya no| DEP

    classDef store fill:#bfdbfe,stroke:#1e40af,color:#000;
    classDef logic fill:#dcfce7,stroke:#15803d,color:#000;
    classDef dep fill:#fee2e2,stroke:#b91c1c,color:#000;
    class RAW,DSL,OUT store;
    class INT,CORE,S0,S1,S2 logic;
    class DEP dep;

Modelo de versiones (releases)

Cada versión es una carpeta releases/<YYYYMMDD>/:

releases/<YYYYMMDD>/
├── RELEASE_NOTES.md   ← changelog, lanes agregadas/desactivadas, issues conocidos
├── sql/               ← definiciones de tablas/vistas BigQuery (el pipeline)
├── analysis/          ← notebooks Jupyter
└── metabase/          ← documentación de modelo (LEGADO — es el despliegue documental
                          lo deprecado; el CONSUMO por Metabase está vivo)

Los SQL canónicos de un release siguen el orden:

  1. 00_release_<fecha>.sql → approved_lanes_<fecha> (universo de cartas porte clasificadas en lanes) y la tabla principal datarelease_<fecha>.
  2. 01_release_<fecha>_customers.sql → variante para clientes (datarelease_<fecha>_customers), usada por el análisis lane consistency clients vs market.
  3. 02_release_<fecha>_volume_maturation.sql → modelo Weibull que estima el volumen real corrigiendo el rezago de archivo del SAT, y re-escribe datarelease_<fecha> con columnas volume_estimated, month_maturity_pct, etc.

Releases presentes hoy: 20260410, 20260421, 20260507, 20260515, 20260624. ⚠️ El README solo lista 20260410 (está desactualizado — parte del ruido).

Refrescar los datos SIN lanzar un release nuevo (lo más común) ⭐

Si no hay cambio de lógica, no hace falta crear un release nuevo: se re-corre el mismo. Verificado el 2026-07-25.

Por qué funciona:

  • Los 9 statements de los 3 SQL son create or replace table → idempotentes, mismos nombres de tabla.
  • El corte de datos es 100% dinámico: cutoff_logic calcula last_visible_month desde CURRENT_DATE('America/Mexico_City'). No hay fechas hardcodeadas (la única, en el 00, está comentada).
  • Como los nombres de tabla no cambian, nada aguas abajo se toca: las cards de Metabase y los question IDs de app-config siguen funcionando. Cero re-deploy.
cd terminal
# 1. respaldo gratis del estado actual (CLONE no copia bytes)
bq query --use_legacy_sql=false \
 "CREATE TABLE \`solvento-risk-analytics-prod.tmp.datarelease_<fecha>_bkp_<hoy>\`
  CLONE \`solvento-risk-analytics-prod.terminal_v3.datarelease_<fecha>\`"
# 2. probar en sandbox y validar MAX(departure_month)
make <fecha>-core DATASET=tmp
# 3. publicar
make <fecha>-core DATASET=terminal_v3

El sufijo de fecha deja de significar 'fecha de los datos'

Al re-correr, datarelease_20260624 pasa a contener datos más nuevos que su nombre. Tratá el sufijo como versión del modelo, no como fecha de carga, y dejá constancia en el RELEASE_NOTES del release re-corrido. Si esto se automatiza, conviene exponer una vista estable (datarelease_current) y apuntar el consumo ahí.

Cuándo sí hace falta un release nuevo: cuando cambia la lógica (SQL, taxonomía de lanes, calibración de consistencia). Solo por refrescar datos, no.

Cómo lanzar una nueva versión

  1. Crear la carpeta releases/<NUEVA_FECHA>/ con su sql/ (lo más simple: copiar el último release y renombrar los 3 SQL al nuevo sufijo, ajustando la fecha de corte de datos). Agregar RELEASE_NOTES.md.
  2. Los SQL usan el placeholder {{BQ_DATASET}} en los nombres calificados (solvento-risk-analytics-prod.{{BQ_DATASET}}.tabla). scripts/run_sql.py lo reemplaza; solo se permite tmp o terminal_v3.
  3. Probar en sandbox (tmp) antes de publicar:
    make setup                       # una vez: venv + requirements
    make <NUEVA_FECHA> DATASET=tmp    # corre todos los .sql en tmp
    # o solo los canónicos:
    make <NUEVA_FECHA>-core DATASET=tmp
    
  4. Publicar a producción (dataset terminal_v3):
    make <NUEVA_FECHA> DATASET=terminal_v3
    
  5. Actualizar RELEASE_NOTES.md y la tabla de releases del README. (El análisis opcional lane consistency se corre aparte: make <fecha>-lane-consistency-sql.)

Es un proceso manual y local (corre el make quien publica el release). No hay Cloud Scheduler ni CI que construya las tablas; el CI del repo solo hace lint con ruff.

Tablas que produce (por release)

Tabla Generada por Grano
terminal_v3.approved_lanes_<fecha> 00_release STEP 1 fila por uuid (cartas porte clasificadas)
terminal_v3.datarelease_<fecha> 00_release STEP 2, re-escrita por 02_…_volume_maturation (departure_month, hub_origin, destination)
terminal_v3.datarelease_<fecha>_customers 01_…_customers variante clientes
terminal_v3.datarelease_<fecha>_volume_weekly / _monthly 02_…_volume_maturation volumen por ISO-week / mes

Columnas clave de datarelease: avg_price (AVG subtotal_mxn, vigintiles 4–17), price_km (ppk), low/high_interval, avg_distance, volume_observed/estimated. Ver docs/data_dictionary.md y docs/lane_taxonomy.md en el repo.

Consumo

La vía viva es Metabase. El front de Terminal pide los datos a solvento-api, que ejecuta cards de Metabase (POST /api/card/{id}/query/json) y devuelve el JSON. Las cards leen terminal_v3.datarelease_<fecha>.

Los IDs efectivos son por ambiente y viven en flux/app-config, no en los defaults del repo solvento-api:

flux/app-config → apps/<env>/solvento-api-values.yaml → envVariables.DATA_ACCESS_QUESTION_ID_*

Estado al 2026-07-25 (verificado contra el repo y ejecutando las cards):

Ambiente ORIGINS / DESTINATIONS / DATA / BORDER_DOMESTIC / ROUTES Modelo de card Release que lee
demo / prod / staging 6266 / 6265 / 6264 / 6267 / 6268 modelo 7 datarelease_20260624 ✅
development 6006 / 6007 / 6008 / 6009 / 6010 modelo 6 datarelease_20260515 ⚠️ desalineado
  • Editar el SQL de una card NO requiere re-deploy del backend: el ID se resuelve en cada request. Lo que sí rompe sin deploy de por medio es cambiar nombres de columnas de salida o template-tags — el processResult y el schema de solvento-api dependen de ellos. Cambiar el ID sí exige tocar app-config (rollout de pods).
  • ⚠️ ORIGINS / DESTINATIONS / QUOTING (5494-5496) siguen sobre Terminal v2 (model_analytics_terminal_v2.v_quoting_*) en los cuatro ambientes. El quoting del front nunca migró a v3.
  • ⚠️ Los defaults del repo solvento-api (5692-5694, 5904, 5909) están colgados de releases viejos: #5901 → release 20260421 y #5696 → tmp.chr_20260302. Hoy todos los ambientes los sobreescriben por env, pero un ambiente nuevo sin override serviría datos de marzo.
  • Los releases/*/metabase/ son documentación legado del modelo. Eso no contradice lo anterior: el consumo por Metabase está vivo, lo que ya no se hace es ese despliegue documental.

Detalle completo en Consumo desde la app (Metabase).

Si falla / está desactualizado 🛠️

Impacto: los datos de Terminal quedan en la última versión publicada hasta que alguien re-corra el pipeline. No hay automatización que lo refresque.

"El front muestra un mes viejo" — diagnóstico en orden

No asumas que hay que lanzar un release. Recorré esto de arriba hacia abajo:

  1. ¿Qué mes tiene la tabla?
    SELECT MAX(departure_month) FROM `solvento-risk-analytics-prod.terminal_v3.datarelease_<fecha>`
    
    Recordá el rezago por diseño: un mes M recién entra el día 15 de M+1. Si hoy es 10 de agosto, que el último mes sea junio es correcto, no un bug.
  2. ¿Está vieja la tabla? → re-corré el release en sitio (sección ⭐ de arriba). No hace falta release nuevo si no cambia la lógica.
  3. ¿La tabla está al día pero el front no? → mirá el question ID del ambiente en flux/app-config (apps/<env>/solvento-api-values.yaml) y ejecutá esa card en Metabase. Si la card devuelve el mes correcto y el front no, es caché de navegador/CDN o caché de resultado de Metabase.
  4. ¿Recién corriste el pipeline? → confirmá la hora de escritura antes de pedirle a alguien que recargue:
    SELECT table_id, TIMESTAMP_MILLIS(last_modified_time)
    FROM `solvento-risk-analytics-prod.terminal_v3.__TABLES__`
    
Síntoma Dónde mirar Acción
Tabla con datos viejos último datarelease_<fecha> y su MAX(departure_month) Re-correr el release en sitio (⭐). Release nuevo solo si cambia la lógica
Tabla al día, front viejo question ID del ambiente en flux/app-config Alinear el ID al modelo del release vigente
Un ambiente distinto a los demás comparar los DATA_ACCESS_QUESTION_ID_* entre apps/*/ Igualar (hoy development está atrasado)
Falla make <fecha> Salida de scripts/run_sql.py; ¿.env con GCP_PROJECT? Probar primero en DATASET=tmp; revisar el SQL del step que falló
Faltan datos aguas arriba Modelos dbt-analytics (int_carta_porte, fact_syntage_invoices*) Ver la capa dbt y el run de dbt Cloud
Confusión con terminal_client (dbt-etls) Camino apagado el 2026-09-11 Ya no existe; el cálculo vivo es este repo — ver Apagado Terminal → Supabase

Escalación: lógica del modelo / releases → Angel Gutierrez (autor) · datos aguas arriba (dbt) → Diego · question IDs / deploy por ambiente → equipo de backend vía flux/app-config · resto → Martin.

Enlaces


Última actualización: 2026-07-25 · Corrección importante: hasta esta fecha la ficha decía que Metabase estaba deprecado y que el consumo iba por Supabase (Terminal_Uploader). Es al revés: el backend pega directo a Metabase y los question IDs por ambiente viven en flux/app-config. También se documentó que refrescar datos NO requiere un release nuevo: el pipeline es idempotente y el cutoff es dinámico.