Andesmar BI · Documentación interna

Asientos contables SAP: de la venta al libro

Cómo las ventas de las agencias se convierten en liquidaciones y asientos que viajan a SAP — explicado para negocio, con el detalle técnico debajo.

Septiembre 2026 Schema: sap Jobs: sap-liquidacion-pipeline · sap-asientos-sender-qa
◆
La idea en una línea: cada día, lo que vendió cada agencia se agrupa en una liquidación, la liquidación se traduce a un asiento contable con las cuentas de SAP, y el asiento se envía por la interfaz de SAP. Todo queda registrado en el schema sap de la base GCP, así se puede auditar qué se liquidó, qué se armó y qué respondió SAP.
1

Para qué sirve

Reemplazar el proceso del servidor on-premise que hoy liquida agencias contra SAP

Origen
Ventas
public.sales · tickets
Pasos 1 y 2
agrupar por agencia
Liquidación
sap.liquidacion_sap
número + detalle por boleto
Pasos 3 y 4
cuentas + envío
Destino
SAP
asiento validado o rechazado

Para negocio

Contaduría necesita que la plata que cobran las agencias aparezca en SAP con la cuenta correcta y un número de liquidación que se pueda rastrear. Hoy lo hace un servidor on-premise; GCP hace lo mismo desde la base en la nube y corre en paralelo contra SAP QA hasta que se decida el pase a producción.

Para técnica

Todo vive en el schema sap de andesmar_sales. Los procedimientos se crean por migraciones Alembic: la fuente de verdad del procedimiento es la migración, no el .sql suelto del repo, que puede estar desactualizado. Runbook: scripts/sap/RUNBOOK_LIQUIDACION_SAP.md.

2

Los cuatro pasos

Tres corren solos a la madrugada; el cuarto es el envío

PasoQué hace (negocio)Dónde queda (técnico)Quién lo corre
1 Liquidar boletos Agrupa los boletos vendidos por agencia y día y les asigna un número de liquidación sap.liquidacion_sap, sap.liquidacion_sap_ticket Job sap-liquidacion-pipeline, 05:00 ART
2 Liquidar ítems Lo mismo para los ítems vendidos aparte del boleto (seguros, percepciones) Tablas de liquidación de ítem en sap
3 Armar asientos Traduce cada liquidación a líneas contables con la cuenta SAP y el CUIT de la agencia Función sap.armar_asientos()
4 Enviar Manda cada asiento a SAP y guarda la respuesta ("Documento validado" o el error) send_asientos.py, marca en sap.asiento_envio por ambiente Job sap-asientos-sender-qa, 06:00 ART (solo QA)
ⓘ
El pipeline de las 05:00 no envía nada: deja los asientos armados. El envío es un job aparte a las 06:00 que hoy apunta únicamente a SAP QA. Los schedulers corren en southamerica-east1 con cron en UTC (0 8 * * * y 0 9 * * *).
3

Tipo de servicio N / P / I

La clasificación que decide la cuenta contable

TipoCuándoCuenta SAP
P ProvincialTramo dentro de Mendoza o dentro de Córdoba411100108
I InternacionalBoleto que no lleva IVA411100103
N NacionalTodo lo demás411100104
⚠
No es lo mismo que alcance_tramo. SAP marca P solo dentro de Mendoza o Córdoba e I cuando el boleto no lleva IVA; alcance_tramo del BI es geografía pura. Coinciden en ~96% de los boletos. Por eso el BI lleva una columna aparte, tipo_liquidacion, copiada de sap.liquidacion_sap_ticket. Para cuadrar contra SAP, tipo_liquidacion; para analizar viajes, alcance_tramo. No hay que unificarlas.
4

Numeración de liquidaciones

Un contador por agencia; la referencia viaja a SAP

Cómo se numera hoy

  • El contador vive en sap.agency_liquidacion_counter y es por código de agencia.
  • La referencia tiene la forma AGENCIA:NUMERO + tipo (por ejemplo C80:44N).
  • GCP hoy numera por tipo de servicio; el legacy on-premise no.
  • SAP trunca la referencia (XBLNR) en 16 caracteres: la conciliación de respuestas trunca los dos lados.

Lo acordado el 25/09 (pendiente de aplicar)

  • Un número por agencia y día, igual para SAP y para el SRT.
  • El día D-1 se cierra a las 03:00.
  • Lo que llega tarde va a una liquidación nueva, no reabre la cerrada.
  • El cambio espera la respuesta del lado SRT. Detalle: docs/integracion/SRT_LIQUIDACION_UNIFICADA.md.
⚠
Cuidado al cambiar el código de una agencia. Como el contador es por código, una agencia que Betterez recodifica arrancaría en 1 y repetiría números ya usados. Antes de dar de alta un código nuevo, buscar también por CUIT y por nombre, y continuar la serie.
5

Quién entra y quién no

Agencias excluidas y el CUIT como única validación

Agencia que Contaduría decidió no pasar a SAP
sap.ag_excluidas
La filtra el Paso 1. El sender no lee esta tabla.
Agencia sin CUIT cargado
sap.agency_cuit
Liquida, pero no arma asiento: dispara la alerta de liquidaciones sin asiento.
CUIT genérico (10000000010)
Válido en SAP
Es deliberado para esa casuística; no es un error.
Código con distinta mayúscula
Igualdad exacta
armar_asientos matchea el código tal cual: no arma y no da error.
⚠
armar_asientos() solo valida el CUIT. No puede saber que una agencia es de prueba o de cuenta corriente: eso lo decide Contaduría y se carga en sap.ag_excluidas. Los códigos de agencia y los CUIT de la lista vienen de Contaduría y del on-premise, no de Betterez, que no expone el CUIT de la agencia.
6

Envío a SAP y corte

Qué sale y qué queda en la cola

  • Corte SAP_ENVIO_DESDE=2026-09-02: solo se envían asientos con fecha de documento desde el inicio del paralelo. Es obligatorio en producción para no duplicar lo que ya liquidó el on-premise.
  • Prefijos excluidos: Busbud se excluye por prefijo en el sender hasta habilitarlo con una tanda chica.
  • Marca por ambiente: sap.asiento_envio registra el envío separado para QA y para producción; enviar a QA no marca el asiento como enviado a producción.
  • Producción no está configurada: solo existen credenciales de QA. El pase a producción es una decisión de Andesmar.
  • "Pendiente en la base" no es lo mismo que "se envía solo": el corte y los prefijos recortan la cola.
7

Alertas

Sentry (proyecto betterez-api-gcp) y políticas de Cloud Monitoring

#AlertaQué significa para negocio
1Asientos con errorSAP rechazó o no dio veredicto sobre asientos enviados (un solo aviso por corrida, con el detalle)
2TardanzaLa corrida tardó más de lo esperado (el job avisa a los 15 min)
3Sin movimientoEl pipeline no generó nada en el día, o el envío desatendido encontró la cola vacía
4DescuadreUn asiento cuya partida doble no da cero (control propio de GCP)
5Liquidaciones sin asientoPlata liquidada que no arma asiento: falta un CUIT o una exclusión, lo define Andesmar
6Código fuera del catálogoUna liquidación nueva con un código de agencia que no está en dim_agency (típico: Betterez renombró la agencia)
ⓘ
Además hay dos políticas de GCP sobre el job: ejecución fallida y sin corrida exitosa en 26 h. Ojo: la alerta de liquidaciones sin asiento solo ve lo que falta; una agencia que arma asientos pero no debería (por ejemplo, de prueba) no la dispara.
8

Estado del paralelo

Envío diario a SAP QA desde el 02/09/2026

6.776
envíos a QA
al 22/09
6.762
"Documento validado"
al 22/09
0
rechazos de SAP
al 22/09
  • 29/09: 3 asientos sin respuesta de SAP QA (QA tarda ~68 s por asiento).
  • 29/09: 12 liquidaciones sin asiento por $2,65 M, de agencias sin CUIT cargado.
  • Producción: 0 asientos enviados. El pase depende de Andesmar.
9

Checklist rápido

Antes de responder una pregunta sobre SAP

  • Para cuadrar contra SAP: tipo_liquidacion, no alcance_tramo
  • Número de liquidación: la clave real es agency_code + número
  • Conciliar con el legacy por fecha e importe, no por referencia
  • Agencia que no debe pasar: sap.ag_excluidas, decidido por Contaduría
  • Procedimiento vigente: leer la migración, no el .sql suelto
  • Envío: revisar corte SAP_ENVIO_DESDE y prefijos excluidos antes de hablar de "pendientes"