bi_rebuild (Planificación, Comercial, Contaduría, administración de MSTR).Qué resuelve: para cada atributo y métrica que se puede llevar a Strategy dice
qué es, de dónde sale, qué valores trae y dónde se equivoca la gente.
Versión: 2026-09-29. Columnas verificadas contra el catálogo real de la base
(
information_schema.columns + COMMENT ON COLUMN) ese mismo día; valores de ejemplomedidos sobre datos de producción.
Tarea: ClickUp 86e3geh0w (pedido de Mariela Ramos: ordenar y renombrar carpetas).
Este documento no reemplaza a las guías temáticas: las resume en un solo lugar y enlaza a cada una para el detalle. Si algo de acá contradice a una guía, la sección Inconsistencias encontradas dice cuál está bien.
Cómo usarlo
Cada sección es un tema de negocio y tiene una tabla por objeto de la base. Las columnas de esas tablas son siempre las mismas:
| Columna del diccionario | Qué dice |
|---|---|
| Columna | Nombre técnico exacto, tabla.columna (la tabla va en el título de la sección). Es lo que se mapea en Architect. |
| Nombre en Strategy | Nombre sugerido en castellano para el atributo o la métrica. Es una propuesta; lo aplica quien administra MSTR. |
| Qué significa | En lenguaje de negocio. |
| De dónde sale | Cadena de origen: dato de Betterez → tabla public.* (copia fiel de Betterez) → paso del ETL incremental que lo escribe en bi_rebuild (el número de paso es el encabezado -- N) de scripts/sql/bi_rebuild/operativo/bi_rebuild_incremental.sql). |
| Tipo / valores | Tipo en Postgres y valores posibles. smallint 1/0 = bandera (1 sí, 0 no). |
| Cuidado con | La trampa conocida. Vacío = no hay trampa conocida. |
Rol en Strategy: cuando dice atributo va a una carpeta de atributos; métrica se arma con una agregación (Sum, Count, Max) sobre esa columna; clave es un ID de join que no se muestra al usuario; técnica no se publica en tableros.
Refresco: bi_rebuild se recalcula dos veces por día (Cloud Run Job bi-rebuild-incremental, 00:00 y 12:00 ART). Los cubos de Strategy deben refrescarse después de esas corridas.
Reglas de oro
Si se respetan estas diez, el 90% de los números "que no cierran" desaparece.
- La plata es
fact_sale.net_sale. Nunca sumartotal_amount.public.salesmezcla transacciones de Betterez con filas sintéticasR-<boleto>(devolución) yPI-<ítem>(seguro, percepción) que desglosan plata ya incluida en otra transacción.net_salelas pone en 0 cuando tienen contenedor;Sum(total_amount)las cuenta dos veces.Sum(net_sale)= pagos con signo (ADR 2026-09-29). - Boletos se cuentan en
fact_ticket_mstr(ofact_ticket) conCount(ticket_id), nunca contando ventas. Una venta trae 1,59 boletos en promedio: contar ventas subestima ~40%. - Importes por medio de pago van en
fact_payment/fact_sale, nunca repartidos por boleto.fact_ticket_mstr.primary_payment_typees sólo un atributo (con qué se pagó), no un monto. - No se suman monedas distintas. Toda métrica de dinero lleva filtro de
currency(ARS,CLP,BOB,USD). No hay conversión de tipo de cambio enbi_rebuild. - Banderas
is_*/has_*sonsmallint1/0, no verdadero/falso. Filtrar con= 1/= 0. Única excepción de texto:viaja_con_menor_3vale'Si'/'No'. sale_dateya está en hora argentina (ART). Una venta a las 22:00 del 30/09 es del 30/09, no del 01/10. No recalcular desde UTC.- Boleto pagado =
is_paid = 1(neto de cancelaciones y cambios). Para cuadrar contra el reporte web de Betterez por agencia,is_paid_including_partial_cancel = 1. dim_manifest.origin/destinationdescriben el RECORRIDO del bus (primera y última parada), no dónde subió el pasajero. Para el tramo del pasajero:fact_ticket.origin/destination.- Tres "alcances" distintos que no se unifican:
alcance_ruta(etiqueta del manifiesto),alcance_tramo(geografía del viaje del pasajero) ytipo_liquidacion(N/P/I con que liquidó SAP). Para cuadrar con SAP,tipo_liquidacion; para analizar viajes,alcance_tramo. - Itinerario =
schedule_id, no el texto. Hay variosschedule_idcon el mismo nombre. El km del servicio se agrega conMax(schedule_distance_km), nuncaSumpor boleto.
Reglas complementarias: PI- no significa seguro (seguro = is_insurance = 1); el schema bi (plural: fact_tickets, fact_sales) es legacy y no se usa; los tableros leen bi_rebuild, nunca public.
Mapa: qué tabla para qué pregunta
| Pregunta | Tabla | Grano (1 fila =) | Métrica base |
|---|---|---|---|
| ¿Cuánta plata entró? (venta neta) | fact_sale | 1 fila de public.sales (transacción o desglose) | Sum(net_sale) + filtro currency |
| ¿Cuánto se vendió en bruto (sin devoluciones)? | v_fact_sale_parent_only | 1 venta padre | Sum(gross_sale) |
| ¿Cuántos boletos? | fact_ticket_mstr / fact_ticket | 1 boleto | Count(ticket_id) con is_paid = 1 |
| ¿Con qué medio se pagó y cuánto? | fact_payment (+ fact_sale) | 1 pago | Sum(amount) con signo (ver §4) |
| ¿Cuántos boletos por medio de pago? | fact_ticket_mstr | 1 boleto | Count(ticket_id) por primary_payment_type |
| ¿Seguros y menores de 3 vendidos? | fact_sold_item | 1 ítem | Count(sold_item_id), Sum(item_amount) |
| ¿Qué tan lleno fue el bus? | dim_manifest | 1 manifiesto (servicio en un día) | peak_load, peak_load_factor |
| ¿Cuántos suben / bajan en cada parada? ¿Carga media? | fact_manifest_leg | 1 tramo entre dos paradas de un manifiesto | Sum(pax_on), carga media (§12) |
| ¿Qué agencia? | dim_agency | 1 agencia | atributo |
| ¿Qué servicio / recorrido? | dim_schedule, dim_route | 1 itinerario / 1 ruta | atributo |
Diagrama de relaciones (claves de join):
dim_date.date_id ─────────── fact_*.sale_date (y manifest_date en fact_manifest_leg / dim_manifest)
dim_mes_anio.id_mes_anio ─── dim_date.id_mes_anio ─── fact_ticket.mes_anio
dim_agency.agency_id ─────── fact_sale / fact_ticket / fact_payment / fact_sold_item .agency_id
dim_channel.channel ──────── fact_*.channel
fact_sale.sale_id ────────── fact_ticket.sale_id, fact_payment.sale_id, fact_sold_item.sale_id
fact_ticket.ticket_id ────── fact_ticket_mstr.ticket_id (1:1), fact_sold_item.ticket_id
dim_manifest.manifest_id ─── fact_ticket(.mstr).manifest_id, fact_manifest_leg.manifest_id, bridge_manifest_tag.manifest_id
dim_schedule.schedule_id ─── fact_ticket(.mstr).schedule_id, dim_manifest.schedule_id, fact_manifest_leg.schedule_id, dim_schedule_leg.schedule_id
dim_route.route_id ───────── fact_ticket(.mstr).route_id, dim_schedule.route_id, dim_manifest.route_id
dim_payment_method.payment_type ── fact_payment.payment_type, fact_sale.primary_payment_type, fact_ticket_mstr.primary_payment_type
dim_sold_item_product.product_name ── fact_sold_item.product_name
dim_manifest_tag.tag_name_norm ── bridge_manifest_tag.tag_name_norm
Ventas y plata
3.1 bi_rebuild.fact_sale — 31 columnas
Grano: 1 fila de public.sales. Incluye transacciones de Betterez y las filas sintéticas R- y PI-. ETL: paso 8 (y 8c para las columnas de pago). Guías: GUIA_TABLERO_AGENCIAS §5.3, GUIA_FORMA_DE_PAGO, ADR docs/decisions/2026-09-29_devoluciones_contenedor_y_grano_transaccion.md.
| Columna | Nombre en Strategy | Qué significa | De dónde sale | Tipo / valores | Cuidado con |
|---|---|---|---|---|---|
sale_id | Venta (ID) | Identificador interno de la fila de venta | public.sales.id → paso 8 | uuid | Clave de join; mapear como texto en MSTR. No es el código que ve Contaduría (ese es transaction_id, en fact_ticket) |
net_sale | Venta neta | Plata de la transacción con signo: una cancelación resta, un cambio suma su neto | sales.total con regla de grano → paso 8 | numeric | La métrica de plata oficial. Vale 0 en filas R- con source_transaction_id y PI- con venta madre (su plata ya está en otra fila). Filtrar currency |
gross_sale | Venta bruta | Total de la fila si es positivo; 0 si es negativo o es un desglose con contenedor | sales.total → paso 8 | numeric | No resta devoluciones. Para "venta bruta" de tablero usar v_fact_sale_parent_only |
total_amount | Total informado (técnico) | Total crudo de la fila tal como viene de Betterez | sales.total → paso 8 | numeric | Nunca sumar para plata: duplica devoluciones y seguros. Sólo auditoría |
cancellation_amount | Importe cancelado | Total de la fila cuando status = cancelled | sales.total + sales.status → paso 8 | numeric | Informativo; ya está dentro de net_sale |
refounds_amount | Importe devuelto | ABS(total) de las filas refunded (siempre positivo) | sales.total → paso 8 | numeric | Informativo. No restarlo de net_sale (ya está descontado). Nombre con typo histórico ("refound") |
is_refund | Es devolución | 1 si la fila es un reembolso | sales.is_refund → paso 8 | smallint 1/0 | — |
is_refound | (no publicar) | Copia exacta de is_refund | sales.is_refund → paso 8 | smallint 1/0 | Duplicado legacy con typo; iguales en el 100% de las filas. Usar is_refund |
sale_type | Tipo de transacción | sale = venta; refund = fila R-; cancellation = transacción que sólo devuelve | sales.sale_type → paso 8 | text: sale, refund, cancellation | Sirve para abrir la venta neta por tipo |
source_transaction_id | Transacción contenedora | En filas R-: la transacción de Betterez que contiene esa devolución | sales.source_transaction_id (payload crudo) → paso 8 | text | Si no es NULL, net_sale = 0 en esa fila |
status | Estado de la venta | Estado de la transacción en Betterez | sales.status → paso 8 | text: confirmed, refunded, cancelled, pending | No confundir con el estado del boleto |
channel | Canal de venta | Canal del boleto de la venta | tickets.channel → paso 8 | text: agency-backoffice, websales, callcenter, unknown | unknown (~27%) son filas sin boleto propio (R-, PI-, cancelaciones), no un error |
sale_date | Fecha de venta | Día de la venta en hora argentina | sales.sale_date (timestamptz) → AT TIME ZONE 'America/Argentina/Buenos_Aires' → paso 8 | date | Ya en ART (regla 6) |
sale_hour | Hora de venta | Hora 0–23 ART | idem | integer | — |
fecha_liquidacion | Fecha de liquidación | Fecha contable de la transacción: la primera liquidación SAP que la incluye | La menor entre sus boletos, sus devoluciones (R-), las devoluciones que contiene (cancelación o cambio) y sus ítems (PI-) → paso 11b | date | Casi siempre sale_date + 1. NULL = no liquidada por SAP (call center, Chile/Bolivia/Perú, agencias excluidas, antes de junio 2026): ~6% del net_sale de sep-2026 |
num_tickets | Boletos en la venta | Cantidad de boletos no borrados de la venta | COUNT(tickets) → paso 8 | integer | Para contar boletos usar fact_ticket_mstr (regla 2) |
pax_qty | Pasajeros en la venta | Cantidad de pasajeros según Betterez | sales.pax_qty → paso 8 | integer | — |
currency | Moneda | ISO 4217 de la venta | sales.currency (default ARS) → paso 8 | varchar(3): ARS, CLP, BOB, USD | Regla 4 |
agency_id | Agencia (ID) | Agencia resuelta de la venta | sales.agency_id; si falta y es devolución, la de la transacción original → paso 8 | text; sin_agency_id = sin agencia | Join a dim_agency. sin_agency_id se muestra como Backoffice |
agency_code | Código de agencia | Código de negocio (ej. 128, M42, PTF) | dim_agency.agency_code → paso 8 | text | Un código puede tener varios agency_id |
agency_label | Agencia | "{código} - {nombre}" (ej. 128 - REALICO) | dim_agency.agency_label → paso 8 | text | Usar esta y no public.sales.agency_name |
agency_country | País de la agencia | País del punto de venta | Catálogo Betterez si es ≠ AR; si no, sales.shift_location_country → paso 8 | text: AR, CL, BO, PE, NULL | Betterez informa AR en agencias chilenas; el catálogo gana (29/09) |
agency_id_raw | (técnica) | agency_id tal como vino | sales.agency_id → paso 8 | text | Auditoría |
agency_id_resolved | (técnica) | Igual a agency_id | paso 8 | text | Redundante con agency_id |
agency_resolution_method | (técnica) | Cómo se resolvió la agencia | paso 8 | text: raw, from_original_transaction, from_associated_trx, fallback_unknown | Auditoría |
agency_resolution_confidence | (técnica) | Confianza de esa resolución | paso 8 | text: high, medium, low | Auditoría |
primary_payment_type | Medio de pago principal | El pago success de mayor importe de la venta | public.payments → paso 8c | text (FK dim_payment_method) | NULL si la venta no tiene pagos |
primary_payment_category | Categoría de pago principal | Categoría de negocio del medio principal | dim_payment_method.payment_category → paso 8c | text: ver §4.2 | — |
payment_method_count | Cantidad de medios de pago | Medios distintos con pago success | paso 8c | integer | — |
is_multi_tender | Pago combinado | 1 si se pagó con más de un medio | paso 8c | smallint 1/0 | — |
paid_amount | Importe cobrado (sin signo) | Suma de pagos success de la venta | payments.amount → paso 8c | numeric | Sin signo: en devoluciones es la plata que sale y viene positiva. Para neto: CASE WHEN total_amount < 0 THEN -paid_amount ELSE paid_amount END |
Vistas derivadas:
v_fact_sale_parent_only(28 columnas, las mismas defact_salesinsale_typeni
source_transaction_id): sólo ventas padre, sin R-, PI- ni cancelaciones. Es venta bruta, no neta. Nombre sugerido: "Ventas padre (bruto)".
v_fact_sale_agency_canonica(15 columnas):fact_saleconagency_id_reporting(agencia
canónica). Uso técnico; no publicar sin revisar con Ingeniería.
Métricas recomendadas:
| Métrica (Strategy) | Definición | Filtros obligatorios |
|---|---|---|
| Venta neta | Sum(fact_sale.net_sale) | currency fija |
| Venta bruta | Sum(v_fact_sale_parent_only.gross_sale) | currency fija |
| Cantidad de transacciones | Count(v_fact_sale_parent_only.sale_id) | — |
| Cobrado neto | Sum(CASE WHEN total_amount < 0 THEN -paid_amount ELSE paid_amount END) | currency fija |
Pagos
4.1 bi_rebuild.fact_payment — 15 columnas
Grano: 1 pago de una venta (una venta puede tener varios). ETL: paso 8c, desde public.payments (arreglo data.payments[] del webhook). Guía: GUIA_FORMA_DE_PAGO_BI_REBUILD.md.
| Columna | Nombre en Strategy | Qué significa | De dónde sale | Tipo / valores | Cuidado con |
|---|---|---|---|---|---|
payment_id | Pago (ID) | Identificador del pago | payments.id → 8c | uuid | Clave |
sale_id | Venta (ID) | Venta a la que pertenece | payments.sale_id → 8c | uuid | Join a fact_sale |
payment_type | Medio de pago (código) | Código Betterez del medio (cash, visa, plataforma10, …) | payments.type → 8c | text | Join a dim_payment_method |
display_name | Medio de pago | Nombre legible del medio | dim_payment_method.display_name → 8c | text | — |
provider | Proveedor | Procesador del pago (Prisma, Mercado Pago, …) | payments.provider → 8c | text | — |
amount | Importe pagado | Importe del pago, en unidades de la moneda | payments.amount normalizado (escala ×100.000 corregida) → 8c | numeric | Siempre positivo: en devoluciones/cancelaciones es plata que sale. Neto: invertir signo cuando fact_sale.total_amount < 0 |
currency | Moneda | ISO de la venta | sales.currency → 8c | varchar(3) | Regla 4 |
status | Estado del pago | Resultado del cobro | payments.status → 8c | text: success, error, pending | — |
is_success | Pago aprobado | 1 si status = success | 8c | smallint 1/0 | Filtrar = 1 para importes |
sale_date | Fecha de venta | Fecha ART de la venta | sales.sale_date → 8c | date | Es la fecha de la venta, no del pago |
fecha_liquidacion | Fecha de liquidación | Fecha contable de la venta del pago | fact_sale.fecha_liquidacion por sale_id → 11b | date | El pago es de la transacción: en una cancelación lo lleva la fila contenedora |
sale_hour | Hora de venta | 0–23 ART | idem | integer | — |
channel | Canal de venta | Canal de la venta | fact_sale.channel → 8c | text | — |
agency_id | Agencia (ID) | Agencia de la venta | fact_sale.agency_id → 8c | text | — |
updated_at | (técnica) | Última escritura ETL | 8c | timestamptz | — |
Métrica: Importe por medio de pago (neto) = Sum(CASE WHEN fact_sale.total_amount < 0 THEN -amount ELSE amount END) con is_success = 1 y currency fija. Desde el 28/09/2026 no hay pagos duplicados en la tabla.
4.2 bi_rebuild.dim_payment_method — 11 columnas
Grano: 1 código de medio de pago (32 medios). Semilla manual en bi_rebuild_schema_patch_once.sql.
| Columna | Nombre en Strategy | Qué significa | Tipo / valores | Cuidado con |
|---|---|---|---|---|
payment_type | Medio de pago (código) | Clave | text | — |
display_name | Medio de pago | Nombre legible (incluye tarjeta / banco) | text | — |
provider | Proveedor | Procesador | text | — |
payment_category | Categoría de pago | Nivel grueso | text: cash, card_terminal, card_credit, card_debit, online, external_channel, voucher, no_payment, other | no_payment es legítimo (cortesías, movimientos IROP). other = código nuevo sin catalogar |
payment_group | Grupo de pago | Agrupación para gráficos | text: physical, online, external_channel, voucher, system | — |
is_card / is_cash / is_online / is_external_channel / is_no_payment | Es tarjeta / efectivo / online / canal externo / sin cobro | Banderas de categoría | smallint 1/0 | — |
is_active | Medio activo | Vigente en el catálogo | smallint 1/0 | — |
Boletos
5.1 bi_rebuild.fact_ticket — 85 columnas
Grano: 1 boleto (1 pasajero en 1 tramo). ETL: paso 8b (base), 9–10 (etiquetas y alcance de ruta), 10-sync (nombre de itinerario), 10-geo (provincias y alcance_tramo), 10b (transportista, alícuota), 10d (liquidación SAP), 10e (transaction_id), 10f (conexiones), 11 (viaja_con_menor_3). Guías: SCHEMA_FACT_TICKET.md, GUIA_ESTADOS_TICKET_BI_REBUILD.md.
Identificación y venta
| Columna | Nombre en Strategy | Qué significa | De dónde sale | Tipo / valores | Cuidado con |
|---|---|---|---|---|---|
ticket_id | Boleto (ID) | ID interno del boleto | public.tickets.id → 8b | uuid | Clave; mapear como texto. Count(ticket_id) = boletos |
ticket_number | Número de boleto | Código visible del pasaje (ej. BRHKTV) | Betterez ticketNumber → tickets.ticket_number → 8b | varchar | Betterez reutiliza números para boletos distintos (~15/día): no usarlo como clave |
sale_id | Venta (ID) | Venta a la que pertenece | tickets.sale_id → 8b | uuid | Join a fact_sale / fact_payment |
transaction_id | Número de transacción | Código Betterez de la venta (el que ve Contaduría) | sales.transaction_id → 10e | text | Para joins usar sale_id |
channel | Canal de venta | Canal del boleto | Betterez channel → tickets.channel → 8b | text: agency-backoffice, websales, callcenter, unknown, manual-backfill | — |
channel_group | Grupo de canal | Agrupación del canal | dim_channel.channel_group → 8b | text: physical, web, callcenter | — |
agency_id | Agencia (ID) | Agencia de la venta | sales.agency_id → 8b | text; sin_agency_id = Backoffice | fact_ticket_mstr no tiene agencia: para boletos por agencia usar este hecho o unir por ticket_id |
agency_code | Código de agencia | dim_agency → 8b | text | — | |
agency_label | Agencia | "{código} - {nombre}" | dim_agency → 8b | text | — |
sale_date | Fecha de venta | Día de venta ART | sales.sale_date → 8b | date | Regla 6 |
sale_hour | Hora de venta | 0–23 ART | idem | integer | — |
mes_anio | Mes de venta | YYYYMM de sale_date | 8b | integer (ej. 202609) | Join a dim_mes_anio. Año anterior = mes_anio - 100 |
Importes, tarifa e impuestos
| Columna | Nombre en Strategy | Qué significa | De dónde sale | Tipo / valores | Cuidado con |
|---|---|---|---|---|---|
ticket_amount | Importe del pasaje | Precio del boleto (con IVA) | Betterez total → tickets.total (normalizado) → 8b | numeric | Es el precio del boleto, no la caja: para plata cobrada usar net_sale (regla 1). Filtrar currency; ~85 pasajes ARS legítimos > 500.000 (outliers) |
ticket_taxes | IVA del pasaje | IVA incluido en el precio | Betterez displayTotalTaxes → tickets.display_total_taxes → 8b | numeric | No mezclar con la percepción RG3819 (fact_sold_item) |
vat_rate | Alícuota de IVA | Alícuota declarada por Betterez | tickets.taxes_detail.value (milésimas: 10500) → 10b, guardada en porcentaje | numeric: 10.50 o NULL | NULL en tramos internacionales (no tributan IVA argentino) |
currency | Moneda | ISO de la venta | sales.currency → 8b | varchar(3) | Regla 4 |
fare | Tarifa | Nombre de la tarifa en Betterez | tickets.fare → 8b | varchar (ej. Adulto, Cortesia 20%, NIÑO BUS MENDOZA) | — |
pax_segment | Segmento de pasajero | Clasificación de la tarifa | Regla sobre fare → 8b | varchar: adult, child_fare, courtesy, other | courtesy no es "gratis": el 98% son tarifas "Cortesia 10/20/30%" que se pagan con descuento (medido sep-2026). other = estudiante, jubilado, promociones |
is_child_fare | Tarifa niño | 1 si pax_segment = child_fare | 8b | smallint 1/0 | No es menor de 3 (eso es viaja_con_menor_3 / fact_sold_item) |
Estado del boleto
Detalle y árbol de decisión: GUIA_ESTADOS_TICKET_BI_REBUILD.md.
| Columna | Nombre en Strategy | Qué significa | De dónde sale | Tipo / valores | Cuidado con |
|---|---|---|---|---|---|
is_paid | Boleto pagado | Pagado y sin cancelación, reembolso ni cambio | tickets.status + public.refund_details → 8b (1b) | smallint 1/0 | Filtro por defecto para contar boletos |
is_paid_including_partial_cancel | Pagado (criterio Betterez web) | is_paid o cancelación parcial con penalidad retenida | idem → 8b | smallint 1/0 | Sólo para cuadrar con el reporte web de Betterez por agencia |
effective_ticket_status | Estado del boleto | Estado curado (prioriza refund_details) | 8b | text: paid, cancelled, changed, payment expired, waiting for payment, movedbyirop, created | Para contar pagados usar is_paid, no = 'paid' (ver Inconsistencias) |
is_cancelled | Cancelado | Status cancelled o hay cancelación en refund_details | 8b / 1e | smallint 1/0 | — |
is_partial_cancel | Cancelación parcial | Cancelado con reembolso menor al precio | refund_details → 8b | smallint 1/0 | Betterez lo sigue contando como vendido |
is_full_cancel | Cancelación total | Reembolso ≥ precio | idem | smallint 1/0 | — |
is_refunded | Reembolsado | Status refunded o reembolso en refund_details | idem | smallint 1/0 | — |
has_refund_record | Tiene devolución registrada | Existe fila en refund_details | idem | smallint 1/0 | — |
refund_type | Tipo de devolución | Tipos presentes en refund_details | idem | text: cancel, change, NULL | — |
refund_amount | Importe reembolsado | Suma devuelta por el boleto | refund_details.refunded → 8b | numeric | Informativo; la plata neta está en fact_sale |
cancel_penalty_amount | Penalidad retenida | Precio − importe cancelado (cancelación parcial) | 8b | numeric | — |
ticket_status | (técnica) Estado crudo | Status tal como lo manda Betterez | tickets.status → 8b | text | Sólo auditoría |
current_status | (técnica) | Espejo del estado OLTP | 8b | text | Evitar en KPI |
is_final_paid | (técnica) | Espejo OLTP | 8b | smallint 1/0 | Evitar en KPI |
status_synced_at | (técnica) | Última sincronización del estado | 8b | timestamp | — |
successor_ticket_number | Boleto de reemplazo | Número del boleto que reemplazó a este en un cambio | Heurística + API changedFromTicketId → job semanal successor | text | NULL si no hubo cambio o no se pudo enlazar |
successor_transaction_id | Transacción del reemplazo | Transacción Betterez del boleto nuevo | idem | text | — |
successor_match_method | Método de enlace del reemplazo | Cómo se encontró el reemplazo | idem | text: heuristic_unique, api, same_webhook, heuristic_ambiguous, same_transaction, heuristic_disambiguated | heuristic_ambiguous = baja confianza |
Viaje, ruta e itinerario
| Columna | Nombre en Strategy | Qué significa | De dónde sale | Tipo / valores | Cuidado con |
|---|---|---|---|---|---|
origin | Origen del pasajero | Parada donde sube | Betterez origin → tickets.origin → 8b | text | Es el tramo del pasajero, no del bus |
destination | Destino del pasajero | Parada donde baja | tickets.destination → 8b | text | idem |
origin_province / destination_province | Provincia origen / destino | Provincia de la parada | dim_station_geo + dim_province → 10-geo | text | — |
origin_country / destination_country | País origen / destino | País de la parada | idem | text (Argentina, Chile, …) | — |
alcance_tramo | Alcance del tramo | Geografía del viaje del pasajero | Provincias/países de origen y destino (respaldo: etiquetas) → 10-geo | text: Nacional, Provincial, Internacional, Sin clasificar | Internacional = alguna punta fuera de AR. No es alcance_ruta ni tipo_liquidacion (§10) |
alcance_ruta | Alcance de la ruta (etiqueta) | Clasificación del servicio por etiqueta del manifiesto | manifest_entries.manifest_tags → bridge → paso 10 | text: Internacional, Nacional, Provincial, Sin clasificacion | Una etiqueta nueva en Betterez queda "Sin clasificacion" sin error |
tipo_servicio | Tipo de servicio | Línea o refuerzo, por etiqueta | idem → paso 10 | text: Linea, Refuerzo, Sin clasificacion, NULL | Trae NULL además de "Sin clasificacion" (ver Inconsistencias) |
has_nacional / has_internacional / has_linea_provincial | Etiqueta Nacional / Internacional / Línea provincial | El manifiesto tiene esa etiqueta | bridge → paso 10 | smallint 1/0 | INTERNACIONAL CHILE y NACIONAL CHILE cuentan como internacional. has_linea_provincial mira el manifiesto actual: un boleto devuelto sale del manifiesto y queda en 0. Para venta provincial usar es_linea_provincial |
es_linea_provincial | Línea provincial (incluye devueltos) | 1 si el boleto es de línea provincial aunque se haya cancelado o cambiado | Manifiesto actual → manifiesto que tuvo (manifest_entries) → mayoría de los manifiestos etiquetados del itinerario → paso 10g | smallint 1/0 | Usar esta, no has_linea_provincial, para venta provincial con devoluciones |
origen_linea_provincial | (técnica) Origen de la marca provincial | De dónde salió es_linea_provincial | 10g | text: manifiesto, manifiesto_historico, itinerario, sin_dato | Auditoría |
fecha_devolucion | Fecha de devolución | Día (ART) de la devolución del boleto | Fila R- (refund_details → sales.sale_date) → 10g | date | NULL = sin devolución. Con refund_amount, para restar devoluciones por el mes en que ocurrieron |
has_linea / has_refuerzo | Etiqueta Línea / Refuerzo | idem | idem | smallint 1/0 | — |
manifest_tags | (técnica) | Etiquetas crudas del manifiesto | manifest_entries.manifest_tags → 8b | jsonb | No usar en MSTR: usar has_* |
route_id | Ruta (ID) | Ruta de Inventory de Betterez | dim_schedule.route_id / schedules.route_id / dim_manifest.route_id → 8b | text (ObjectId) | ~59.000 boletos viejos traen un route_id con formato ORIGEN_DESTINO que no existe en dim_route |
itinerary | Itinerario (nombre) | Nombre del servicio (ej. 13:00 Mendoza-Uspallata) | dim_schedule.display_name → 8b / 10-sync | text | No usar como ID (regla 10) |
schedule_id | Itinerario (ID) | Servicio de Betterez | tickets.schedule_id o del manifiesto → 8b | text | ID del atributo Itinerario |
schedule_distance_km | Km del itinerario | Km del recorrido completo del servicio | dim_schedule.distance_total_km → 8b | numeric | Max, nunca Sum por boleto |
travel_distance | Km del tramo | Km del tramo que compró el pasajero | Betterez travelDistance; si falta, suma de tramos de schedule_legs (paso 1d) → 8b | integer | Cobertura ~56%. No es km del servicio |
distance | Km de la ruta (mediana) | Mediana de km de los itinerarios de la ruta | dim_route.distance → 8b | numeric | Sólo confiable si dim_route.distance_confiable = 1 |
operating_company_name | Transportista | Razón social o UTE que opera el servicio | dim_schedule.operating_company_name → 10b | text | No es la marca (brand_name). Clave del Tablero Contable |
product_family | Familia de producto | Familia Betterez | tickets.product_family → 8b | text: reservation, … | Tours comparten reservation |
product_type | Tipo de producto | Tipo concreto | tickets.product_type → 8b | text: reservation, city_tour_mendoza, citytour_cordoba, nueva_cuyanita, boleto_caliente, ac80_-_la_salada, combo_millonario, … | Pasajeros interurbanos = product_type = 'reservation' |
trip_type | Tipo de tramo | full_route si el tramo coincide con el recorrido del manifiesto | 8b | text: full_route, partial_segment | partial_segment no es cancelación parcial |
route_match_flag | (técnica) | 1 si el tramo = recorrido del manifiesto | 8b | integer 1/0 | — |
Fechas del viaje y manifiesto
| Columna | Nombre en Strategy | Qué significa | De dónde sale | Tipo / valores | Cuidado con |
|---|---|---|---|---|---|
travel_date | Fecha de viaje del tramo | Día en que el pasajero viaja su tramo | tickets.travel_date → 8b | date | Difiere de la salida del servicio en ~25% (nocturnos, largos) |
departure_datetime_utc | Salida (UTC) | Fecha y hora de salida del tramo | tickets.departure_datetime → 8b | timestamptz | — |
departure_date_ar | Fecha de salida del tramo (ART) | Fecha argentina de la salida | idem en ART → 8b | date | — |
manifest_date | Fecha del servicio | Fecha de salida del bus (manifiesto) | dim_manifest.manifest_date → 10-geo | date | La que usa Planificación. NULL sin manifiesto |
manifest_id | Manifiesto (ID) | Servicio en un día concreto | Match por ticket_number o por itinerario + fecha → 8b | text | NULL si no matcheó |
manifest_match_method | Cómo se asignó el manifiesto | Método o motivo de no-match | 8b | text: ticket_number, path_schedule, chain_reissue_heuristic, non_reservation_product, non_paid_status, cancelled_no_manifest, refunded_no_manifest, no_match | Sólo no_match es un huérfano a investigar |
date_match_flag | (técnica) | 1 si travel_date = manifest_date | 8b | integer 1/0 | — |
has_manifest_entry_oltp | (técnica) | Existe en manifest_entries | 8b | smallint 1/0 | — |
Conexiones (viaje con trasbordo)
Betterez vende un viaje con trasbordo como un boleto por tramo, en la misma venta. Guía: GUIA_CONEXIONES_BI_REBUILD.md. Regla: un boleto es un tramo; para contar viajes, filtrar tramo_orden = 1.
| Columna | Nombre en Strategy | Qué significa | De dónde sale | Tipo / valores | Cuidado con |
|---|---|---|---|---|---|
journey_id | Viaje (ID) | Viaje de Betterez; todos sus tramos lo comparten | tickets.journey_id → 10f | text | NULL antes de marzo 2026: para contar viajes usar tramo_orden = 1, no Count Distinct journey_id |
tramo_orden | N.º de tramo | Orden del tramo dentro del viaje | journey_order + 1 → 10f | integer: 1, 2, 3 | Un viaje directo es un solo tramo con 1 |
cantidad_tramos | Cantidad de tramos | Tramos vendidos del viaje | 10f | integer: 1 directo, 2 o 3 con conexión | No cambia si se cancela un tramo |
is_conexion | Es conexión | 1 si el boleto es tramo de un viaje con trasbordo | 10f | smallint 1/0 | Contar filas con is_conexion = 1 da tramos, no viajes |
parada_conexion | Parada de trasbordo | Dónde trasborda (con dos: MENDOZA > SAN LUIS) | 10f | text; NULL si es directo | Nombre de parada tal como lo manda Betterez |
origen_viaje / destino_viaje | Origen / destino del viaje | Punta a punta del viaje completo que compró el pasajero | 10f | text | origin / destination son los del tramo |
Liquidación SAP y ancillaries
| Columna | Nombre en Strategy | Qué significa | De dónde sale | Tipo / valores | Cuidado con |
|---|---|---|---|---|---|
numero_liquidacion | N.º de liquidación SAP | Liquidación de agencia que incluyó el boleto | sap.liquidacion_sap_ticket → 10d | integer | Se numera por agencia: la clave es agency_code + número. Ver §10 |
fecha_liquidacion | Fecha de liquidación | Fecha contable del boleto | idem → 10d | date | Casi siempre sale_date + 1 |
tipo_liquidacion | Tipo de liquidación SAP | N / P / I con que SAP liquidó | sap.liquidacion_sap_ticket.tipo_servicio → 10d | text: N, P, I, NULL | Ver §10 |
viaja_con_menor_3 | Viaja con menor de 3 | El adulto lleva un ítem BOLETO SEGURO | fact_sold_item → paso 11 | varchar: 'Si' / 'No' | Única bandera de texto (regla 5) |
5.2 bi_rebuild.fact_ticket_mstr — 68 columnas
Grano: 1 boleto (misma cantidad de filas que fact_ticket, 1:1 por ticket_id). Es la tabla "ancha" pensada para el cubo de Strategy. ETL: paso 10 (+ 10b, 10c, 10d, 10e, 10f, 11). Guía: SCHEMA_FACT_TICKET_MSTR.md.
53 columnas compartidas con fact_ticket, con el mismo significado (ver §5.1): ticket_id, sale_date, departure_date_ar, travel_date, ticket_amount, manifest_id, channel, channel_group, origin, destination, route_id, itinerary, is_paid, effective_ticket_status, product_type, product_family, has_nacional, has_internacional, has_linea_provincial, has_linea, has_refuerzo, currency, ticket_taxes, fare, pax_segment, is_child_fare, schedule_id, schedule_distance_km, successor_ticket_number, successor_transaction_id, successor_match_method, viaja_con_menor_3, origin_province, destination_province, origin_country, destination_country, alcance_tramo, manifest_date, operating_company_name, vat_rate, numero_liquidacion, fecha_liquidacion, tipo_liquidacion, transaction_id, journey_id, tramo_orden, cantidad_tramos, is_conexion, parada_conexion, origen_viaje, destino_viaje, es_linea_provincial, origen_linea_provincial.
15 columnas propias:
| Columna | Nombre en Strategy | Qué significa | De dónde sale | Tipo / valores | Cuidado con |
|---|---|---|---|---|---|
has_viajes_especiales | Etiqueta Viajes especiales | Manifiesto con esa etiqueta | bridge → paso 10 | smallint 1/0 | Sin manifiesto: todos los has_* = 0 |
has_auxilio | Etiqueta Auxilio | idem | idem | smallint 1/0 | — |
has_bus_turistico | Etiqueta Bus turístico | idem | idem | smallint 1/0 | — |
has_combo_millonario | Etiqueta Combo millonario | idem | idem | smallint 1/0 | — |
has_la_cuyanita | Etiqueta La Cuyanita | idem | idem | smallint 1/0 | — |
has_encomiendas | Etiqueta Encomiendas | idem | idem | smallint 1/0 | — |
has_ip | Etiqueta IP | idem | idem | smallint 1/0 | — |
has_ficticio | Etiqueta Ficticio | Manifiesto ficticio (no operó como tal) | idem | smallint 1/0 | Conviene excluirlos (= 0) en ocupación |
has_viaje_protegido | Tiene seguro | El boleto tiene VIAJE PROTEGIDO asociado | fact_sold_item → 11 | smallint 1/0 | Subestima los ítems sin ticket_number |
viaje_protegido_amount | Importe del seguro del boleto | Suma de seguros del boleto | idem | numeric | Para recaudación de seguros usar fact_sold_item |
has_boleto_seguro | Tiene boleto de menor de 3 | El boleto tiene BOLETO SEGURO asociado | idem | smallint 1/0 | Misma señal que viaja_con_menor_3 |
boleto_seguro_amount | Importe boleto menor de 3 | Suma de esos ítems | idem | numeric | idem |
primary_payment_type | Medio de pago principal | Medio principal de la venta del boleto | fact_sale.primary_payment_type → 10c | text | Atributo, no importe (regla 3) |
primary_payment_category | Categoría de pago principal | idem, nivel grueso | fact_sale → 10c | text (ver §4.2) | idem |
updated_at | (técnica) | Última escritura ETL | paso 10 | timestamptz | — |
Lo que NO tiene (y hay que ir a fact_ticket por ticket_id): agency_id / agency_code / agency_label, ticket_number, sale_id, sale_hour, mes_anio, alcance_ruta, tipo_servicio, is_partial_cancel, is_paid_including_partial_cancel, is_cancelled, is_refunded, refund_amount, manifest_match_method, trip_type, travel_distance.
5.3 bi_rebuild.v_conexion_trasbordo — 35 columnas (vista)
Desde 02/10/2026 (migración v_conexion_trasbordo). Una fila por trasbordo: une el tramo que llega a la parada de conexión (llega_*: ticket, itinerario, schedule_id, manifiesto, transportista, salida y llegada en UTC) con el que parte de ella (parte_*), dentro del mismo viaje y pasajero. Un viaje de 3 tramos da 2 filas (trasbordo_orden 1 y 2). Especificación del tablero: ESPEC_TABLERO_CONEXIONES_MSTR.md.
viaje_ides eljourney_id; en el histórico sinjourney_idesv:+ un hash de venta, viaje
y pasajero (no expone el documento). 12 tramos históricos ambiguos quedan afuera.
espera_min: minutos entre la llegada programada del tramo que llega y la salida del
siguiente. Promediar solo con espera_valida = 1 (entre 0 y 24 h).
is_paid_trasbordo = 1cuando los dos tramos están vigentes. No filtra: un trasbordo con un
tramo cancelado también es dato (llega_is_paid, parte_is_paid).
- Métrica "Pasajeros que trasbordan" =
Count(llega_ticket_id)conis_paid_trasbordo = 1. - Septiembre 2026: 1.771 trasbordos, 1.573 vigentes, espera mediana 70 min. Pares más frecuentes:
PPCO → GJ01 en Córdoba (205) y JG03 → COPB en Córdoba (173).
- No duplica boletos: cada tramo aparece a lo sumo una vez como "llega" y una como "parte". Se
calcula al consultarla (~0,2 s el reporte por par de servicios).
Manifiestos y ocupación
Un manifiesto es un servicio (itinerario) en un día concreto: el bus que salió el 15/03 a las 13:00. Guías: GUIA_FACT_MANIFEST_LEG_BI_REBUILD.md, GUIA_TABLERO_ETIQUETAS_MANIFIESTO.md.
6.1 bi_rebuild.dim_manifest — 32 columnas
Grano: 1 manifiesto. ETL: paso 7, desde public.manifest_legs y public.manifest_entries (Betterez GET /manifests/{id}, webhooks manifests.* y job andesmar-manifests-sync).
| Columna | Nombre en Strategy | Qué significa | De dónde sale | Tipo / valores | Cuidado con |
|---|---|---|---|---|---|
manifest_id | Manifiesto (ID) | ID Betterez del manifiesto | manifest_legs.manifest_id → 7 | text | Clave |
manifest_date | Fecha del servicio | Día de salida del servicio | manifest_legs.manifest_date → 7 | date | Filtro temporal de ocupación (no sale_date) |
schedule_id | Itinerario (ID) | Servicio | manifest_legs.schedule → 7 | text | — |
schedule_display_name | Itinerario | Nombre del servicio | manifest_legs.schedule_display_name → 7 | text | Puede repetirse entre schedule_id |
route_id | Ruta (ID) | Ruta Inventory | → 7 | text | — |
origin | Origen del recorrido | Primera parada del bus | Primera station de manifest_legs por leg_ord → 7 (CTE ruta_od) | text; Sin datos si no hay | Regla 8: no es donde subió el pasajero |
destination | Destino del recorrido | Última parada del bus | Última to_station → 7 | text | idem |
departure_time_ref | Hora de salida | Hora de salida de la primera parada | manifest_legs.departure_time → 7 | text HH:MM | idem |
departure_hour | Hora de salida (hora) | Hora entera de la salida | derivada → 7 | integer | — |
manifest_status | Estado del manifiesto | Estado en Betterez | manifest_entries.manifest_status → 7 | text: published, paused, emptied, canceled, Sin datos | Para ocupación filtrar published; Sin datos = manifiesto sin boletos |
peak_load | Pasajeros en el pico | Máxima cantidad de pasajeros a bordo en algún tramo | MAX(manifest_legs.load) → 7 | integer | Es el pico, no la carga media (§12) |
peak_load_factor | Ocupación pico (%) | Máximo % de ocupación en algún tramo | MAX(manifest_legs.load_factor) → 7 | numeric (0–100) | idem |
bus_capacity | Capacidad del bus | Asientos del manifiesto | Betterez manifest.capacity o vehicle.capacity → 7 | numeric | — |
inferred_capacity | Capacidad (estimada) | bus_capacity, o peak_load / peak_load_factor si falta | → 7 | numeric | — |
available_seats_est | Asientos libres en el pico (est.) | Capacidad inferida − pico | → 7 | numeric | Estimación |
trip_scope | (técnica) Alcance | Internacional / nacional por etiqueta | etiquetas → 7 | text: internacional, nacional, NULL | Minúsculas y sin "provincial": usar fact_manifest_leg.alcance_ruta |
manifest_tags | (técnica) | Etiquetas crudas | manifest_entries.manifest_tags → 7 | jsonb | Usar bridge_manifest_tag |
vehicle_id / vehicle_name / vehicle_plate / vehicle_number / vehicle_capacity / bus_id | Vehículo / Patente / Interno / Capacidad del vehículo | Unidad asignada | Betterez vehicle.* → manifest_legs → 7 | text / numeric | Cobertura parcial (ver has_vehicle_data) |
vehicle_json | (técnica) | Objeto vehículo completo | → 7 | jsonb | — |
driver_primary_first_name / driver_primary_last_name / driver_primary_external_id / driver_primary_order | Chofer principal | Primer chofer asignado | Betterez drivers[] → 7 | text / integer | Cobertura parcial |
drivers_json | (técnica) | Todos los choferes | → 7 | jsonb | — |
driver_source | (técnica) | Origen del dato de recursos | → 7 | text | — |
has_driver_data / has_vehicle_data | Tiene chofer / Tiene vehículo | Hay datos de chofer / vehículo | → 7 | smallint 1/0 | — |
6.2 bi_rebuild.fact_manifest_leg — 21 columnas
Grano: 1 tramo entre dos paradas consecutivas de un manifiesto (manifest_id + leg_ord). ETL: paso 7b (y un UPDATE posterior al paso 10 para alcance_ruta / product_type), desde public.manifest_legs (Betterez manifest.legs[]). Cubre desde abril de 2025.
| Columna | Nombre en Strategy | Qué significa | De dónde sale | Tipo / valores | Cuidado con |
|---|---|---|---|---|---|
manifest_id | Manifiesto (ID) | manifest_legs → 7b | text | Join a dim_manifest | |
leg_ord | Orden de la parada | Posición del tramo en el recorrido (0 = primera) | Betterez legs[] → 7b | integer | — |
manifest_date | Fecha del servicio | → 7b | date | Filtro temporal | |
route_id, schedule_id, schedule_display_name | Ruta / Itinerario | Igual que en dim_manifest | → 7b | text | — |
station_id / station | Parada (ID) / Parada | Parada donde empieza el tramo | Betterez stationId / station → 7b | text | Nombres con variantes: agrupar por station_id |
to_station_id / to_station | Parada siguiente | Parada donde termina el tramo | → 7b | text | — |
arrival_time / departure_time | Hora de llegada / salida | Horario en esa parada | Betterez arrivalTime / departureTime → 7b | text HH:MM | Texto, no timestamp |
duration_minutes | Duración del tramo (min) | Minutos hasta la parada siguiente | Betterez leg → 7b | integer | ~14% en 0 o NULL |
pax_on | Suben | Pasajeros que suben en station | Betterez paxOn → 7b | integer | — |
pax_off | Bajan | Pasajeros que bajan en station | Betterez paxOff → 7b | integer | — |
load | Pasajeros a bordo | Pasajeros a bordo al salir de station hacia to_station (= carga anterior + suben − bajan) | Betterez load → 7b | integer | Es un stock, no un flujo: no sumar entre paradas (ver §12) |
load_factor | Ocupación del tramo (%) | load / capacidad × 100, redondeado por Betterez | Betterez loadFactor → 7b | integer 0–100 | Entero redondeado: para promedios, recalcular con load y bus_capacity |
distance_km | Km del tramo | Km entre station y to_station | dim_schedule_leg.distance_km por estaciones → 7b (desde 29/09/2026) | numeric | NULL si Betterez informa 0 o no se encuentra (~22% en sep-2026): dato faltante. Para carga media usar v_manifest_carga_media (§12) |
alcance_ruta | Alcance de la ruta | Igual que fact_ticket.alcance_ruta | etiquetas → post-paso 10 | text | — |
product_type | Tipo de producto | Producto dominante del manifiesto | moda de fact_ticket.product_type → post-paso 10 | text | — |
updated_at | (técnica) | → 7b | timestamptz | — |
6.3 Etiquetas: dim_manifest_tag (9) y bridge_manifest_tag (2)
| Columna | Nombre en Strategy | Qué significa | Tipo / valores | Cuidado con |
|---|---|---|---|---|
dim_manifest_tag.tag_name_norm | Etiqueta (ID) | Etiqueta normalizada (minúsculas, sin acento) | text | Clave |
dim_manifest_tag.tag_name | Etiqueta | Nombre como en Betterez | text: NACIONAL, INTERNACIONAL, INTERNACIONAL CHILE, NACIONAL CHILE, LINEA PROVINCIAL, LINEA, REFUERZO, VIAJES ESPECIALES, AUXILIO, Bus Turistico, COMBO MILLONARIO, La Cuyanita, ENCOMIENDAS, IP, FICTICIO | — |
dim_manifest_tag.facet | Familia de etiqueta | Agrupador | text: route_scope, service_type, function, status | — |
dim_manifest_tag.description / color / sort_order | Descripción / color / orden | Presentación | text / smallint | — |
dim_manifest_tag.is_active | Etiqueta activa | smallint 1/0 | — | |
dim_manifest_tag.first_seen_at / last_seen_at | (técnica) | Primera y última vez vista | timestamptz | — |
bridge_manifest_tag.manifest_id / tag_name_norm | (clave) | Relación N:M manifiesto ↔ etiqueta (paso 9) | text | En Strategy preferir los has_* de los hechos; el bridge es para auditoría |
Agencias y canales
7.1 bi_rebuild.dim_agency — 11 columnas
Grano: 1 agencia Betterez (319). ETL: paso 4 + backfill_dim_agency_catalog.sql (export de agencias de Betterez). Guía: GUIA_TABLERO_AGENCIAS_BI_REBUILD.md.
| Columna | Nombre en Strategy | Qué significa | Tipo / valores | Cuidado con |
|---|---|---|---|---|
agency_id | Agencia (ID) | ID Mongo de Betterez | text | Clave; sin_agency_id = Backoffice (call center sin agencia) |
agency_code | Código de agencia | Código de negocio | text | Un código puede tener varios IDs |
agency_display_name | Nombre de agencia | Nombre sin código | text | — |
agency_label | Agencia | "{código} - {nombre}" | text | La DESC a mostrar |
agency_name | (no publicar) | Alias de agency_label | text | Compatibilidad con cubos viejos |
agency_country | País de la agencia | text: AR, CL, BO, PE, NULL (46) | El país explícito del catálogo ≠ AR gana | |
is_active | Agencia activa | Vigente hoy en Betterez | smallint 1/0 | No usar como filtro por defecto: una inactiva puede tener ventas |
is_blacklisted | Agencia duplicada | Duplicado del catálogo, no ofrecer en el buscador | smallint 1/0 | El filtro del buscador es is_blacklisted = 0 |
catalog_source | (técnica) | Origen de la fila | text: betterez_export, system_sentinel | — |
first_seen_sale_date / last_seen_sale_date | Primera / última venta | Rango de actividad | date | — |
agency_blacklist (5 columnas: agency_id, agency_code, agency_label, motivo, created_at) es la lista decidida por Andesmar de la que se deriva is_blacklisted. No se publica.
7.2 bi_rebuild.dim_channel — 2 columnas
| Columna | Nombre en Strategy | Tipo / valores |
|---|---|---|
channel | Canal de venta | agency-backoffice, websales, callcenter, manual-backfill, unknown |
channel_group | Grupo de canal | physical, web, callcenter |
Plataforma 10 no es un canal: es una agencia (agency_code = 'PTF') dentro del grupo web.
Rutas, itinerarios y geografía
Tres ideas que se mezclan: itinerario (el servicio con horario: dim_schedule), ruta (agrupador comercial de Betterez: dim_route) y tramo (lo que compró el pasajero: fact_ticket.origin/destination). Detalle: CONVENCIONES §4.1.
8.1 bi_rebuild.dim_schedule — 20 columnas
Grano: 1 itinerario. ETL: paso 6c, desde public.schedules (webhooks schedule.* y backfill_schedules.py).
| Columna | Nombre en Strategy | Qué significa | Tipo / valores | Cuidado con |
|---|---|---|---|---|
schedule_id | Itinerario (ID) | text | Clave | |
display_name | Itinerario | Nombre legible | text | Hay nombres repetidos (hasta 90 versiones de un recorrido): agrupar por route_id para "el mismo servicio" |
route_id / route_name | Ruta (ID) / Ruta | Ruta de Inventory | text | — |
travel_route_id / travel_route_name | Recorrido comercial | Travel route de Betterez | text | — |
from_station / to_station / from_id / to_id | Cabecera / Destino final | Extremos del itinerario | text | — |
departure_time | Hora de salida | text | — | |
from_date_ymd / to_date_ymd | Vigencia desde / hasta | text YYYY-MM-DD | Texto, no fecha | |
distance_total_km | Km del itinerario | Km del recorrido completo | numeric | — |
duration_total_minutes | Duración (min) | integer | — | |
capacity | Capacidad declarada | integer | — | |
brand_name | Marca comercial | Ej. ANDESMAR | text | Agrupa varias razones sociales |
operating_company_name | Transportista | Razón social / UTE | text | La del Tablero Contable |
is_deleted | Itinerario borrado | smallint 1/0 | — | |
updated_at | (técnica) | timestamptz | — |
8.2 bi_rebuild.dim_schedule_leg — 8 columnas
Grano: 1 tramo del itinerario (schedule_id, leg_ord). Columnas: from_id, from_name, to_id, to_name, distance_km (km del tramo), duration_minutes. Es la única fuente de km por tramo (la usa la carga media, §12). Cuidado: el leg_ord del itinerario no siempre coincide con el del manifiesto; unir por schedule_id + from_id + to_id.
8.3 bi_rebuild.dim_route — 7 columnas
Grano: 1 ruta (181). ETL: paso 6.
| Columna | Nombre en Strategy | Qué significa | Tipo / valores | Cuidado con |
|---|---|---|---|---|
route_id | Ruta (ID) | ObjectId Betterez | text | — |
ruta | Ruta | origen - destino legible | text | — |
origin / destination | Origen / destino de la ruta | Del recorrido principal | text | Si recorridos > 1 la ruta agrupa otros pares que no se ven acá |
distance | Km de la ruta (mediana) | Mediana de km de sus itinerarios | numeric | Sólo sirve si distance_confiable = 1 |
recorridos | Cantidad de recorridos | Pares origen-destino que agrupa | smallint | — |
distance_confiable | Km confiable | 1 si recorridos = 1 | smallint 1/0 | Para km por boleto usar schedule_distance_km |
8.4 Estaciones: dim_station (4), dim_station_geo (5), dim_province (3)
| Columna | Nombre en Strategy | Qué significa |
|---|---|---|
dim_station.station_id / station_name | Parada (ID) / Parada | Catálogo de paradas vistas en manifiestos (paso 6b, 430 paradas) |
dim_station.name_norm / aliases | (técnica) | Nombre normalizado y variantes de escritura |
dim_station_geo.name_key / sample_name | (técnica) | Clave por nombre normalizado (paso 6b-geo) |
dim_station_geo.province / country | Provincia / País de la parada | Geografía de la parada |
dim_station_geo.geo_source | (técnica) | betterez_inventory, manual_curado, manual_limite (parada en límite provincial: país sí, provincia no) |
dim_province.province_norm / province_label / country | Provincia | Catálogo de provincias con etiqueta legible |
Seguros, menores de 3 y otros ítems
Guía: GUIA_TABLERO_SEGUROS_Y_MENORES.md, SCHEMA_FACT_SOLD_ITEM.md.
9.1 bi_rebuild.fact_sold_item — 27 columnas
Grano: 1 ítem vendido aparte del pasaje (fila PI-). ETL: paso 11, desde public.sold_item_details.
| Columna | Nombre en Strategy | Qué significa | De dónde sale | Tipo / valores | Cuidado con |
|---|---|---|---|---|---|
sold_item_id | Ítem (ID) | sold_item_details.id → 11 | uuid | Count = ítems | |
sold_item_number | Número de ítem | Ej. PI-ABC123 | → 11 | text | — |
sale_id / parent_sale_id / parent_transaction_id | Venta del ítem / Venta madre | Fila PI- y la venta del pasaje que la contiene | → 11 | uuid / text | — |
ticket_number / ticket_id | Boleto asociado | Boleto al que se agregó el ítem | → 11 | text / uuid | Puede ser NULL (~20% de menores) |
product_name | Producto | Nombre Betterez (VIAJE PROTEGIDO, BOLETO SEGURO NACIONAL, …) | → 11 | text | — |
product_category | Categoría de producto | dim_sold_item_product → 11 | text: insurance_passenger, minor_under_3, tax_cross_border, other_paid_in | other_paid_in = producto nuevo sin catalogar | |
product_type / product_family | (técnica) | Suelen ser paid_in / item | → 11 | text | — |
is_insurance | Es seguro | 1 = VIAJE PROTEGIDO | → 11 | smallint 1/0 | PI- ≠ seguro |
is_minor_boleto_seguro | Es boleto menor de 3 | 1 = BOLETO SEGURO% | → 11 | smallint 1/0 | Cuenta ítems, no boletos. No es tarifa niño |
item_amount | Importe del ítem | Con IVA | sold_item_details.total → 11 | numeric | Filtrar currency y status = 'paid' |
item_net | Neto del ítem | Sin IVA | sold_item_details.price → 11 | numeric | — |
item_taxes | IVA del ítem | Calculado total − price | → 11 | numeric | sold_item_details.total_taxes viene contaminado en origen y no se usa |
vat_rate | Alícuota del ítem | item_taxes / item_net × 100 | → 11 | numeric | 0,00 en seguros y boleto seguro internacional |
quantity | Cantidad | → 11 | integer | — | |
status | Estado del ítem | → 11 | text: paid, waiting for payment, cancelled, changed, payment expired, created | Un ítem devuelto puede seguir en paid: cruzar con la transacción de reembolso | |
sale_date / sale_hour | Fecha / hora de venta | ART | sales.sale_date (o created_at) → 11 | date / integer | — |
fecha_liquidacion | Fecha de liquidación | Fecha de la liquidación SAP que incluye el ítem | sap.liquidacion_sap_item_detalle (fila de venta) → 11b | date | Casi siempre sale_date + 1 |
channel / channel_group | Canal / Grupo de canal | Del boleto padre | → 11 | text | — |
agency_id | Agencia (ID) | → 11 | text | — | |
currency | Moneda | → 11 | varchar(3) | Regla 4 | |
updated_at | (técnica) | → 11 | timestamptz | — |
9.2 Catálogo y vistas
dim_sold_item_product(4):product_name(clave),product_category,betterez_catalog_name,
is_active. 7 productos: VIAJE PROTEGIDO, 5 variantes de BOLETO SEGURO, IMPUESTO NACIONAL ARGENTINO - RG3819.
v_insurance_kpi(7) yv_minor_kpi(7): agregados porsale_date,channel,channel_group,
agency_id, currency, con insurance_items_sold / insurance_revenue y minor_items_sold / minor_revenue. Ya filtran status = paid. Nombres sugeridos: "Seguros vendidos", "Recaudación seguros", "Boletos menor de 3 vendidos", "Recaudación boletos menor de 3".
Liquidación SAP y contable
Viven como columnas de fact_ticket y fact_ticket_mstr (§5.1): numero_liquidacion, fecha_liquidacion, tipo_liquidacion, más operating_company_name y vat_rate del Tablero Contable. Origen: sap.liquidacion_sap_ticket (pipeline sap-liquidacion-pipeline, 05:00 ART). fecha_liquidacion también está en fact_sale, fact_payment y fact_sold_item (desde 02/10/2026, paso 11b), para filtrar plata, pagos e ítems por fecha contable.
- Venta provincial:
es_linea_provincial = 1(nohas_linea_provincial, que pierde los boletos
devueltos). Neto por cohorte = Sum(ticket_amount) − Sum(refund_amount) por sale_date. Neto por movimientos del mes = Sum(ticket_amount) por sale_date − Sum(refund_amount) por fecha_devolucion. Septiembre 2026 (ARS): bruta $272,2 M, neto A $259,3 M, neto B $259,0 M. Guía: GUIA_VENTA_PROVINCIAL_TABLERO_CONTABLE.md.
- NULL es legítimo: call center, ventas en Chile / Bolivia / Perú, agencias excluidas de SAP
(sap.ag_excluidas) y todo mayo de 2026 (la tabla SAP arranca en junio).
- El número es por agencia: la liquidación 799 existe en muchas agencias. Clave =
agency_code + numero_liquidacion.
- Fecha contable: Andesmar fecha el boleto en sus planillas por
fecha_liquidacion, no por
sale_date.
Los tres alcances (no son intercambiables):
| Columna | Qué clasifica | Criterio | Valores | Usar para |
|---|---|---|---|---|
alcance_ruta | El servicio | Etiqueta que Andesmar pone al manifiesto en Betterez | Internacional, Nacional, Provincial, Sin clasificacion | Filtrar por tipo de servicio |
alcance_tramo | El viaje del pasajero | Geografía de origen y destino (internacional si alguna punta está fuera de AR) | Internacional, Nacional, Provincial, Sin clasificar | Analizar viajes |
tipo_liquidacion | Cómo liquidó SAP | Réplica del on-premise: P sólo dentro de Mendoza o de Córdoba; I = boleto sin IVA | N, P, I, NULL | Cuadrar contra SAP |
alcance_tramo y tipo_liquidacion coinciden ~96%. La diferencia es de definición, no un error.
Fechas
11.1 bi_rebuild.dim_date — 9 columnas y dim_mes_anio — 2 columnas
ETL: pasos 2, 2b y 3.
| Columna | Nombre en Strategy | Qué significa | Tipo / valores | Cuidado con |
|---|---|---|---|---|
dim_date.date_id | Día | Clave de fecha | date (desde 2025-06-02) | Llega hasta 2144 (ver Inconsistencias): acotar el rango en los filtros |
year / month / day | Año / Mes (número) / Día del mes | integer | — | |
month_name | (no publicar) | Nombre del mes | text en inglés (September) | Para el nombre en castellano usar dim_mes_anio.descripcion_mes_anio |
week | Semana ISO | integer | Semana ISO: la del 1 de enero puede ser la 52/53 del año anterior | |
quarter | Trimestre | integer 1–4 | — | |
day_of_week | Día de la semana | 1 = lunes … 7 = domingo | integer | — |
id_mes_anio | Mes (ID) | YYYYMM | integer | Join a dim_mes_anio |
dim_mes_anio.id_mes_anio | Mes (ID) | integer | — | |
dim_mes_anio.descripcion_mes_anio | Mes | Ej. Septiembre 2026 | text | Ordenar por id_mes_anio, no por el texto |
11.2 ¿Qué fecha uso?
| Pregunta | Fecha | Dónde |
|---|---|---|
| ¿Cuánto se vendió en septiembre? | sale_date (ART) | todos los hechos |
| ¿Cuántos viajan en septiembre (ventas futuras)? | travel_date o departure_date_ar | fact_ticket(.mstr) |
| ¿Cuántos pasajeros llevó el servicio del día X? (Planificación) | manifest_date | fact_ticket(.mstr), dim_manifest, fact_manifest_leg |
| ¿Qué entró en la liquidación contable del día X? | fecha_liquidacion | fact_ticket(.mstr), fact_sale, fact_payment, fact_sold_item |
Carga media de un servicio ("CM")
Pregunta abierta con Mariela Ramos: si "CM de un servicio" es carga media. Mientras se confirma, esto es lo que se puede calcular hoy con los datos existentes.
12.1 Qué hay
fact_manifest_leg tiene, para cada tramo entre dos paradas de un manifiesto, los pasajeros a bordo (load) y el % de ocupación (load_factor), además de suben / bajan. dim_manifest tiene la capacidad (bus_capacity) y el pico (peak_load). El km de cada tramo no está en fact_manifest_leg: hay que traerlo de dim_schedule_leg.distance_km. Ejemplo real (servicio Caleta Olivia → Resistencia, 20/09/2026, 58 asientos): arranca vacío, llega a 56 pasajeros entre Sierra Grande y Las Grutas, y baja a 16 después de Rosario.
12.2 Tres definiciones posibles
| Definición | Fórmula por manifiesto | Qué responde | Ejemplo (servicio de arriba) |
|---|---|---|---|
| Pico (ya existe) | dim_manifest.peak_load / peak_load_factor | ¿Cuánto se llenó en el peor tramo? | 56 pax (97%) |
| Carga media ponderada por km (recomendada) | Σ(load × km_tramo) / Σ(km_tramo) = pasajeros-km / km recorridos | ¿Cuántos pasajeros llevó en promedio a lo largo del recorrido? | 31,0 pax |
| Factor de ocupación medio | Σ(load × km_tramo) / (Σ km_tramo × bus_capacity) | Mismo, en % de la capacidad | 53,4% |
| Carga media simple | Avg(load) sobre los tramos | Promedio por parada (sesgado: una parada cada 5 km pesa lo mismo que un tramo de 180 km) | 30,3 pax |
| Pasajeros transportados | Sum(pax_on) | ¿Cuántos pasajeros distintos subieron? | 122 pax |
La carga media ponderada por km es la definición habitual en transporte de pasajeros (pasajeros-km sobre kilómetros recorridos). Si Planificación entiende "CM" como pasajeros transportados por servicio, la fórmula es Sum(pax_on), que ya está disponible sin nada extra.
Para un servicio en un período (ej. "el 13:00 Mendoza-Uspallata en septiembre"), agregar pasajeros-km y km de todos sus manifiestos y dividir al final (no promediar los promedios).
12.3 En Strategy: v_manifest_carga_media (desde 29/09/2026)
Implementado (migración carga_media_manifiesto): fact_manifest_leg.distance_km trae el km de cada tramo (paso 7b del incremental, unido por estaciones) y la vista bi_rebuild.v_manifest_carga_media da una fila por manifiesto, lista para Strategy sin joins:
| Columna | Nombre en Strategy | Qué es | Aditiva |
|---|---|---|---|
pax_km | Pasajeros-km | Σ pasajeros a bordo × km de cada tramo con km | Sí |
km_con_dato | Km con dato | Σ km de los tramos con distancia conocida | Sí |
carga_media_km | Carga media | pax_km / km_con_dato | No |
ocupacion_media_pct | Ocupación media (%) | carga media / capacidad × 100 | No |
carga_media_simple | Carga media simple | Promedio de pasajeros a bordo por tramo | No |
pico_pasajeros / ocupacion_pico_pct | Pico / Ocupación pico (%) | Tramo más lleno | No |
pasajeros_subidos | Pasajeros transportados | Σ pax_on | Sí |
capacidad | Capacidad | dim_manifest.bus_capacity | — |
tramos / tramos_con_km / km_completo | — | Calidad del dato: km_completo = 0 si falta el km de algún tramo | — |
manifest_status, schedule_id, schedule_display_name, route_id, origin, destination, alcance_ruta, manifest_date | — | Atributos para filtrar y agrupar | — |
Métricas en Strategy para un servicio o un período (sumar primero, dividir al final; nunca promediar carga_media_km):
- Carga media =
Sum(pax_km) / Sum(km_con_dato) - Ocupación media % =
100 × Sum(pax_km) / Sum(km_con_dato × capacidad)(conviene exponer
km_con_dato × capacidad como hecho, o calcularlo en la vista del reporte)
- Filtro por defecto:
manifest_status = 'published'.
Validación: scripts/sql/bi_rebuild/operativo/validate_manifest_carga_media.sql. Backfill del histórico: scripts/sql/bi_rebuild/backfill/backfill_fact_manifest_leg_km.sql (aplicado 29/09).
12.4 Limitaciones medidas (29/09/2026)
- Km faltante.
distance_kmes NULL cuando Betterez informa el tramo en 0 (~17% en septiembre, ej. Rawson → Trelew) o el itinerario no está endim_schedule_leg. Cobertura de tramos con km: ~78% en septiembre 2026, 58–65% antes de abril 2026 (itinerarios viejos menos cubiertos). En septiembre, de 7.839 manifiestos publicados: 4.462 con km completo, 3.037 parcial, 340 sin km. La carga media se pondera solo con los tramos que tienen km; para comparaciones finas, filtrarkm_completo = 1. - No unir por
leg_ord: el orden del itinerario no coincide con el del manifiesto en ~12% de los tramos (la vista ya une por estaciones). load_factorviene redondeado a entero por Betterez: no promediarlo; usar la vista.- Manifiestos no operativos:
emptied,canceled,pausedySin datostienen carga 0 o parcial: filtrarpublished. - Si Planificación entiende "CM" como pasajeros transportados, es
Sum(pasajeros_subidos).
Objetos que no van a Strategy
| Objeto | Por qué |
|---|---|
Schema bi (plural: fact_tickets, fact_sales, v_tickets_detalle, …) | Legacy, sin refresco desde 2026-04-15 |
etl_control | Marca de agua del ETL (last_run, lookback_interval) |
stg_*, tmp_* | Staging y tablas temporales de backfill |
v_agency_catalog_drift | Control de calidad del catálogo de agencias |
aux_combustible, aux_consumo_axion, aux_flota_andesmar, aux_ypf_ruta, f_combustibles | Tema combustible, con su propio runbook (scripts/combustible_sync/RUNBOOK_AUX_COMBUSTIBLE_SYNC.md); fuera del alcance de este diccionario |
public.* | Copia operativa de Betterez; los tableros no la leen |
Inconsistencias encontradas
Relevadas el 29/09/2026 comparando las guías contra la base. La base manda sobre qué columnas y valores existen; las guías, sobre el significado.
| # | Dónde | Qué dice la guía | Qué dice la base / el ETL | Estado |
|---|---|---|---|---|
| 1 | SCHEMA_FACT_TICKET_MSTR.md, COMMENT de successor_match_method | 3 valores (guía) / 4 valores (comentario) | 6 valores: heuristic_unique, api, same_webhook, heuristic_ambiguous, same_transaction, heuristic_disambiguated | Corregido en la guía; el COMMENT de la base queda desactualizado (se corrige por migración, no en esta tarea) |
| 2 | INFORME_MAESTRO_BI_REBUILD.md §2.6 | sale_type incluye change | Sólo sale, refund, cancellation: el enum de la API prevé change pero la ingesta no lo escribe | Corregido |
| 3 | GUIA_TABLERO_AGENCIAS_BI_REBUILD.md §3, INFORME_MAESTRO §2.5 | catalog_source = betterez_export | historical_only | betterez_export y system_sentinel (fila de sin_agency_id) | Corregido |
| 4 | GUIA_TABLERO_AGENCIAS_BI_REBUILD.md §5.4 vs §2.1 | §5.4: "en tableros operativos, filtrar activas"; §2.1: "is_active no es filtro por defecto" | — | Contradicción interna: vale §2.1. Corregido §5.4 |
| 5 | SCHEMA_FACT_SOLD_ITEM.md | channel_group = physical / online | physical, web, callcenter | Corregido |
| 6 | CONVENCIONES §4, SCHEMA_FACT_TICKET_MSTR.md | currency: ARS, CLP, USD | También BOB (4.055 ventas) | Corregido en CONVENCIONES |
| 7 | CONVENCIONES §4.1 (ejemplo) | route_id = MENDOZA_Uspallata Terminal | route_id es ObjectId de Betterez; el formato ORIGEN_DESTINO quedó en ~59.000 boletos viejos que no unen a dim_route | Anotado; ejemplo no corregido (es ilustrativo). Pendiente: remediar esos route_id |
| 8 | CONVENCIONES §4 | dim_route.distance = "mediana histórica por par OD" | Mediana de los itinerarios de la ruta; confiable sólo con distance_confiable = 1 (COMMENT de la base) | Anotado |
| 9 | SCHEMA_FACT_TICKET.md | distance = "km de la ruta (o del tramo si no hay ruta)" | El ETL copia sólo dim_route.distance | Corregido |
| 10 | TICKET_AMOUNT_OUTLIERS_REGLAS_MSTR.md, GUIA_TABLERO_METRICAS §3 | Revenue / IVA con effective_ticket_status = 'paid' | GUIA_TABLERO_METRICAS §5 dice "no usar effective_ticket_status = 'paid' para conteos ejecutivos; usar is_paid" | Contradicción entre guías: vale is_paid = 1. No corregido (afecta ejemplos SQL) |
| 11 | GUIA_TABLERO_METRICAS §1, SCHEMA_FACT_TICKET_MSTR.md | "Revenue pasajes" = Sum(ticket_amount) | La plata oficial es Sum(fact_sale.net_sale) (ADR 29/09). ticket_amount es precio del boleto | Anotado: renombrar la métrica a "Importe de pasajes" en Strategy |
| 12 | SCHEMA_FACT_TICKET.md, CONVENCIONES §4 | tipo_servicio cae a Sin clasificacion sin flags | 125.750 boletos con tipo_servicio NULL además de 474.717 Sin clasificacion | Anotado (revisar paso 10) |
| 13 | alcance_ruta vs alcance_tramo | — | Uno dice Sin clasificacion, el otro Sin clasificar | Anotado: unificar en Strategy con un alias |
| 14 | dim_date | "horizonte futuro" | Llega a 2144-12-13 (43.659 días); month_name en inglés | Anotado (bug conocido de extensión de dim_date) |
| 15 | dim_manifest.trip_scope | nacional / internacional | Minúsculas, sin provincial, NULL en 2.111 manifiestos de septiembre | Anotado: usar alcance_ruta |
| 16 | COMMENT de fact_ticket.is_child_fare | "true cuando…" | Es smallint 1/0 | Anotado |
| 17 | GUIA_FORMA_DE_PAGO §4, INFORME_MAESTRO §2.6b | fact_ticket_mstr "no tiene sale_id a propósito", no se puede llegar a la venta | Sigue sin sale_id, pero desde el 28/09 tiene transaction_id, que permite ese join | Anotado |
| 18 | CONVENCIONES §4 | pax_segment = 'courtesy' sin aclaración | 30% de los boletos de septiembre; casi todos "Cortesia 20%/30%", pagos con descuento | Anotado en este diccionario |
| 19 | GUIA_FACT_MANIFEST_LEG_BI_REBUILD.md | Sin mención de km por tramo | dim_schedule_leg se une por estaciones, no por leg_ord | Anotado en §12 |
Propuesta de carpetas para Strategy
Propuesta para quien administra MSTR. Criterio: una carpeta por tema de negocio, adentro Atributos y Métricas separados, y el grano en el nombre de la métrica cuando puede confundirse.
Andesmar BI (bi_rebuild)
├── 01 Ventas y plata
│ ├── Atributos: Tipo de transacción, Estado de la venta, Canal de venta, Moneda
│ └── Métricas: Venta neta, Venta bruta, Cantidad de transacciones, Cobrado neto
├── 02 Boletos
│ ├── Atributos: Estado del boleto, Tarifa, Segmento de pasajero, Tipo de producto,
│ │ Alcance del tramo, Origen / Destino del pasajero, Provincia / País
│ └── Métricas: Boletos pagados, Boletos pagados (criterio Betterez web),
│ Boletos cancelados, Importe de pasajes, IVA de pasajes, Km vendidos
├── 03 Forma de pago
│ ├── Atributos: Medio de pago, Categoría de pago, Grupo de pago, Pago combinado
│ └── Métricas: Importe por medio de pago, Boletos por medio de pago, % pago combinado
├── 04 Seguros y menores de 3
│ ├── Atributos: Producto, Categoría de producto
│ └── Métricas: Seguros vendidos, Recaudación seguros, Boletos menor de 3, Recaudación menor de 3,
│ Percepción RG3819
├── 05 Servicios, manifiestos y ocupación
│ ├── Atributos: Manifiesto, Fecha del servicio, Hora de salida, Estado del manifiesto,
│ │ Origen / Destino del recorrido, Parada, Orden de la parada, Alcance de la ruta,
│ │ Tipo de servicio, Etiquetas (Línea, Refuerzo, La Cuyanita, ...), Vehículo, Chofer
│ └── Métricas: Suben, Bajan, Pasajeros a bordo, Pico de pasajeros, Ocupación pico (%),
│ Carga media, Ocupación media (%), Pasajeros transportados, Capacidad
├── 06 Agencias y canales
│ ├── Atributos: Agencia, Código de agencia, País de la agencia, Agencia activa,
│ │ Canal de venta, Grupo de canal
│ └── Métricas: (las de 01 y 02, filtradas por agencia; no se duplican)
├── 07 Rutas e itinerarios
│ ├── Atributos: Itinerario, Ruta, Recorrido comercial, Transportista, Marca comercial,
│ │ Cabecera / Destino final
│ └── Métricas: Km del itinerario (Max), Duración del itinerario
├── 08 Contable y liquidación SAP
│ ├── Atributos: N.º de liquidación SAP, Fecha de liquidación, Tipo de liquidación SAP,
│ │ Alícuota de IVA, Transportista
│ └── Métricas: Importe liquidado, IVA liquidado
├── 09 Fechas
│ └── Atributos: Día, Semana, Mes, Trimestre, Año, Día de la semana
│ (una jerarquía por fecha: de venta, de viaje, del servicio, de liquidación)
├── 90 Auditoría (no usar en tableros)
│ └── Total informado, Estado crudo, Método de match de manifiesto, Resolución de agencia, ...
└── 99 Obsoleto — schema bi (a borrar)
Convenciones de nombres sugeridas:
- En castellano, sin nombres técnicos visibles: "Venta neta", no
net_sale. - Métricas de dinero con la moneda en el filtro de la métrica y, si hay varias, en el nombre: "Venta neta (ARS)", "Venta neta (CLP)".
- Métricas de conteo con la unidad: "Boletos pagados", "Transacciones", "Ítems de seguro". Nunca "Ventas" a secas (no se sabe si son boletos o transacciones).
- Atributos de fecha con el calificativo: "Fecha de venta", "Fecha del servicio", "Fecha de viaje", "Fecha de liquidación". Nunca "Fecha" sola.
- Las banderas se muestran como Sí / No en el atributo (el dato es 1/0).
- Un solo nombre por concepto en todo el proyecto: si "Agencia" es
agency_label, no hay otra "Agencia" armada conagency_name.
Mantenimiento
- Al agregar una columna a
bi_rebuild(siempre por migración Alembic), agregar su fila acá y en
la guía temática, con COMMENT ON COLUMN en la migración.
- Verificación de columnas contra la base:
SELECT table_name, column_name, data_type,
col_description(format('%I.%I', table_schema, table_name)::regclass, ordinal_position) AS comentario
FROM information_schema.columns
WHERE table_schema = 'bi_rebuild'
ORDER BY table_name, ordinal_position;
- Guías relacionadas: CONVENCIONES_Y_GLOSARIO_TABLERO_MSTR.md
(glosario), GUIA_TABLERO_METRICAS_BI_REBUILD.md (índice de métricas), INFORME_MAESTRO_BI_REBUILD.md (capa técnica). Versión HTML: docs/presentaciones/diccionario-datos.html.