← Blog Arquitectura

SAP Business One con una sola puerta: cómo lo integré en 17 aplicaciones sin que ninguna le hable directo

10 de octubre de 2026 · 6 min de lectura

Cuando una empresa usa SAP Business One y empieza a construir sus propias aplicaciones, lo primero que pasa es que cada una se conecta a SAP por su cuenta. La de ventas consulta clientes, la de compras consulta proveedores, la de recursos humanos consulta empleados. Cada una con sus credenciales, sus consultas y su forma de manejar los errores.

Con dos aplicaciones se puede vivir así. Con diecisiete, no. En la plataforma que lidero ninguna aplicación habla con SAP directamente, y cuento cómo está armado y qué fui aprendiendo al integrarlo.

Una sola puerta hacia SAP

Toda la comunicación con SAP pasa por un gateway: un servicio en .NET que es el único que tiene las credenciales de SAP. Por dentro usa dos caminos:

  • El Service Layer, la API oficial de SAP Business One, para crear documentos y para las lecturas estándar.
  • Lectura directa a HANA, con procedimientos almacenados, para las consultas pesadas que por Service Layer tomarían muchas llamadas.

Y tampoco las aplicaciones llaman al gateway. Solo lo llama el backend del portal, y cada módulo le pide los datos al portal:

 módulo (ventas, compras, rrhh…)
        │  token del usuario
        ▼
 backend del portal ──── tablas espejo (maestros)
        │  mismo token
        ▼
 gateway SAP  (único con credenciales)
        │
        ▼
 SAP Business One  (Service Layer + HANA)

El token del usuario viaja en cada salto. Así el gateway sabe quién pidió cada cosa, y ningún módulo necesita una cuenta de servicio de SAP.

Los maestros se copian, no se consultan

La decisión que más ordenó todo fue esta: los maestros de SAP (clientes, empleados, artículos, proveedores, cuentas contables, departamentos) se sincronizan en la base de datos del portal como tablas espejo. Los módulos leen esas tablas a través de la API del portal. Nunca consultan SAP en vivo para un maestro.

La sincronización tiene un solo punto de entrada por maestro, un POST /sync que es lo único que llama al gateway. Se dispara de dos formas:

  • Con un botón "Sincronizar con SAP" en la configuración del portal, solo para administradores.
  • Con una tarea programada todas las madrugadas, que corre todos los maestros en secuencia. Si uno falla, los demás siguen.

Por dentro es un upsert por lotes, y lo que desaparece de SAP se marca como inactivo en vez de borrarse, porque puede haber registros en el portal que todavía apuntan a él. La corrida completa, unos 35 mil registros, toma menos de un minuto.

¿Por qué copiar en lugar de consultar?

  • Velocidad. Un buscador de clientes no puede esperar a SAP en cada tecla.
  • Disponibilidad. Si SAP está en mantenimiento, el portal sigue funcionando con los datos de la noche anterior.
  • Un solo lugar para corregir. Si cambia cómo se lee un maestro, se cambia en la sincronización y todos los módulos lo reciben igual.

En vivo solo va lo que tiene que estar al segundo: el stock, el costo de un artículo, el tipo de cambio y la creación de documentos.

Para que esto se mantenga, quedó escrito como regla, con el patrón paso a paso para agregar un maestro nuevo: el endpoint en el gateway, la tabla espejo y su sincronización en el portal, la pantalla con el botón, y el módulo leyendo del portal. Cualquiera del equipo puede agregar uno siguiendo esos pasos.

Lo que aprendí integrando

El Service Layer corta en 20 sin avisar

Una consulta pedía 100 órdenes y llegaban 20. El Service Layer pagina de a 20 por defecto y deja el resto detrás de un @odata.nextLink. No da error ni advertencia, y el count de la respuesta sí trae el total real. Lo descubrimos porque un usuario decía que sus órdenes no estaban en SAP, cuando en realidad estaban en la página dos.

Hay que pedir el tamaño de página explícitamente (la cabecera Prefer: odata.maxpagesize) o seguir el nextLink. Y del lado de quien pagina, la condición de corte tiene que ser el count, no "el lote vino más chico de lo que pedí", porque con este comportamiento eso corta en la primera vuelta.

DocTotal no está en la moneda del documento

En los documentos de SAP Business One, DocTotal es el total en moneda local. DocCurrency dice en qué moneda se emitió. Mostrar DocTotal con el símbolo de DocCurrency hacía que una orden en dólares apareciera con el monto en soles, o sea más del triple.

El importe en la moneda del documento es DocTotalFc, pero cuando el documento está en moneda local SAP lo deja en 0, y un 0 se puede leer como "cero dólares". Por eso el gateway lo publica como null en ese caso, y quien lo consume usa DocTotal cuando no hay otro. Lo llamamos "moneda extranjera" y no "dólares", porque una parte de las compras están en euros.

Hubo un segundo tropiezo con esto mismo: el Service Layer solo devuelve los campos que pides en el $select. Agregamos DocTotalFc al modelo pero no a la lista de campos de la consulta, así que llegaba vacío siempre. Eso no lo detecta el compilador, solo se ve llamando a SAP.

Un error de validación disfrazado de caída

Una alerta de errores 5xx sonó un día por varios 502 seguidos. No era SAP caído: alguien había pedido un costeo sin elegir cliente, el gateway respondió 400 diciendo que faltaba el cliente, y la función que hacía las lecturas convertía cualquier respuesta no exitosa en 502. Las escrituras sí propagaban bien el 400, las lecturas no.

Ese error tenía dos costos: el usuario veía "SAP no responde" cuando el problema era un campo vacío, y la métrica que dispara las alertas se llenaba de falsos 5xx. Ahora las lecturas propagan los 400, 409 y 422 tal cual, con el mensaje del backend.

El SQL de SAP también es código

Los procedimientos almacenados que lee el gateway se crearon directamente en HANA y no estaban en ningún repositorio. Y el mapeo de sus resultados es tolerante a propósito: busca las columnas por nombre, y si una columna cambia de nombre, el campo llega null o 0 sin ningún error.

O sea, alguien podía cambiar un procedimiento y una pantalla empezaba a mostrar ceros sin que nada fallara. La regla que acordamos: el SQL vive con el código que se rompe si cambia. Cada procedimiento que llama el gateway se versiona en su repositorio, en el mismo cambio que el código que lo lee.

Qué haría distinto si empezara hoy

  • Versionar el SQL de SAP desde el primer procedimiento. Recuperarlo después, cuando ya hay decenas en uso, es arqueología.
  • Pruebas contra una copia de la compañía. Los dos tropiezos del DocTotal se ven recién llamando a SAP. Una prueba que corra contra una base de respaldo los habría atrapado antes de producción.
  • Una sola función de paginación en el gateway. El corte en 20 apareció en varias consultas a la vez porque cada una paginaba a su manera.
  • La moneda explícita en cada monto. Ningún campo de dinero sin su moneda al lado, aunque parezca obvio.

Si estás por integrar con SAP Business One

Antes de escribir la primera consulta, decide quién es el único que habla con SAP. Si cada aplicación se conecta por su cuenta, después cuesta mucho juntar todo en un solo lugar. Y separa desde el inicio lo que se copia (los maestros) de lo que va en vivo: esa decisión define cuánto depende tu plataforma de que SAP esté disponible.

Si estás integrando con SAP Business One, escríbeme por LinkedIn. Me interesa saber cómo lo resolvieron otros equipos, sobre todo con el Service Layer.

SAP Business OneService LayerHANAIntegraciónArquitectura.NET