SAP Business One es el ERP de miles de empresas medianas en México y Latinoamérica. Tarde o temprano hay que conectarlo con algo: la tienda en línea, un CRM, una app para vendedores o un portal de clientes. La forma recomendada de hacerlo es Service Layer, la API web oficial de SAP Business One.
Qué es Service Layer
Es un servicio que expone los objetos de negocio de SAP B1 (artículos, socios de negocio, pedidos, facturas…) mediante una API OData sobre HTTPS. Normalmente vive en:
https://<servidor>:50000/b1s/v1/
Respeta las reglas de negocio de SAP B1, así que es mucho más seguro que escribir directo en la base de datos (algo que nunca debes hacer).
1. Iniciar sesión
const base = 'https://sap-servidor:50000/b1s/v1';
const r = await fetch(`${base}/Login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ CompanyDB: 'SBO_EMPRESA', UserName: 'integracion', Password: process.env.SAP_PASS }),
});
const cookies = r.headers.getSetCookie().map((c) => c.split(';')[0]).join('; ');
// Envía estas cookies (B1SESSION, ROUTEID) en las siguientes llamadas
La sesión expira después de un tiempo sin uso (por defecto, unos 30 minutos). Reutilízala y vuelve a iniciar sesión solo cuando recibas un 401. Usa un usuario dedicado para la integración, con los permisos mínimos.
2. Consultar con OData
GET /b1s/v1/Items?$select=ItemCode,ItemName,QuantityOnStock&$filter=Valid eq 'tYES'&$orderby=ItemCode
$select: solo los campos que necesitas.$filter: filtra en el servidor (eq,gt,startswith(ItemName,'Tor'),and,or).$orderby,$top,$skip: orden y paginación.
Por defecto Service Layer regresa páginas pequeñas (alrededor de 20 registros) y un odata.nextLink para la siguiente. Puedes pedir páginas más grandes con un encabezado:
async function traerTodo(ruta, cookies) {
const todos = [];
let url = `${base}/${ruta}`;
while (url) {
const r = await fetch(url, { headers: { Cookie: cookies, Prefer: 'odata.maxpagesize=200' } });
const data = await r.json();
todos.push(...data.value);
url = data['odata.nextLink'] ? `${base}/${data['odata.nextLink']}` : null;
}
return todos;
}
3. Crear un pedido
await fetch(`${base}/Orders`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Cookie: cookies },
body: JSON.stringify({
CardCode: 'C0001',
DocDueDate: '2026-10-05',
Comments: 'Pedido web #10492',
DocumentLines: [
{ ItemCode: 'A001', Quantity: 2, UnitPrice: 350 },
{ ItemCode: 'B014', Quantity: 1, UnitPrice: 1200 },
],
}),
});
Guarda el folio externo (por ejemplo, el número de pedido de tu tienda) en un campo del documento (NumAtCard o un campo de usuario) para poder evitar duplicados si la llamada se reintenta.
Buenas prácticas
- Nunca escribas directo en la base de datos de SAP B1. Rompe la integridad y el soporte.
- Idempotencia: antes de crear, busca si ya existe un documento con tu folio externo.
- Colas y reintentos: si SAP no responde, la operación espera y se reintenta; no se pierde.
- Bitácora de cada llamada con su respuesta, para diagnosticar.
- Certificados: muchos servidores usan certificados propios; configúralos bien en lugar de desactivar la validación.
- Cierra sesión (
POST /Logout) en procesos por lotes para no acumular sesiones.
Casos típicos
- Tienda en línea ↔ SAP B1: pedidos, existencias y precios.
- App de vendedores: clientes, precios especiales y pedidos desde el celular.
- Portal de clientes: estados de cuenta, facturas y estatus de pedidos.
- CRM ↔ SAP B1: clientes y cotizaciones sin captura doble.
¿Necesitas conectar tu SAP Business One con otro sistema? Platiquemos.