Service Layer de SAP Business One: login, $filter, $select y paginación, con ejemplos que funcionan

Por Zain Abbas, Kodenova · Actualizado el

Respuesta cortaEl Service Layer es la API REST (OData) de SAP Business One. Usted envía con POST la base de datos de la empresa, el nombre de usuario y la contraseña a /b1s/v1/Login o /b1s/v2/Login, y luego manda las cookies B1SESSION y ROUTEID en cada llamada. Lee datos con GET y opciones OData como $select, $filter y $orderby, sigue el nextLink para obtener más páginas, crea con POST, actualiza con PATCH y termina la sesión con Logout.

Qué es el Service Layer

El Service Layer es la API web de SAP Business One. Expone objetos de negocio como socios de negocios, artículos, pedidos y facturas a través de HTTPS, usando el protocolo OData. Cualquier lenguaje que pueda enviar una solicitud HTTP puede usarlo: JavaScript, Python, C#, PHP o una herramienta low-code.

Nació en la versión de SAP Business One para HANA. Los feature packs recientes de la 10.0 también lo ofrecen para SQL Server. Por defecto escucha en el puerto 50000 sobre HTTPS:

https://your-b1-server:50000/b1s/v1/
https://your-b1-server:50000/b1s/v2/

¿v1 o v2?

Ambas rutas llegan a los mismos objetos de negocio.

  • /b1s/v1 usa OData versión 3. Todavía funciona y la mayor parte del código antiguo la usa.
  • /b1s/v2 usa OData versión 4, que SAP ahora considera el protocolo principal.

La diferencia que se nota primero está en las respuestas. v1 devuelve odata.nextLink, v2 devuelve @odata.nextLink. Para proyectos nuevos, elija v2 y manténgase en ella. Los ejemplos de abajo usan rutas v1 porque siguen siendo las más comunes en la práctica. Cambie v1 por v2 y funcionan de la misma manera.

Paso 1: Iniciar sesión

Envíe un POST con la base de datos de su empresa, el usuario y la contraseña:

POST https://your-b1-server:50000/b1s/v1/Login
Content-Type: application/json
{
  "CompanyDB": "SBODEMOUS",
  "UserName": "manager",
  "Password": "your-password"
}

Una respuesta exitosa contiene el ID de sesión, la versión y el tiempo de espera en minutos:

{
  "SessionId": "1c9e4b2a-....",
  "Version": "1000xxx",
  "SessionTimeout": 30
}

El servidor también establece dos cookies: B1SESSION y ROUTEID.

  • B1SESSION identifica su sesión.
  • ROUTEID le indica al balanceador de carga del Service Layer qué nodo tiene su sesión, para que cada solicitud vuelva al mismo nodo.

Un navegador guarda y envía estas cookies por usted. Fuera de un navegador, debe agregar ambas al encabezado Cookie de cada llamada posterior:

Cookie: B1SESSION=1c9e4b2a-....; ROUTEID=.node1

Por defecto, la sesión expira después de 30 minutos sin actividad. Cada llamada reinicia el temporizador. Construya su cliente de modo que una respuesta 401 dispare un único inicio de sesión nuevo y un reintento, no un ciclo infinito.

Paso 2: Leer datos con GET

Un GET simple devuelve una página de registros con todos los campos:

GET /b1s/v1/BusinessPartners

Eso es lento y pesado. Use las opciones de consulta OData para pedir solo lo que necesita.

OpciónQué haceEjemplo
$selectDevuelve solo estos campos$select=CardCode,CardName,Balance
$filterDevuelve solo los registros que coinciden$filter=CardType eq 'cCustomer'
$orderbyOrdena los resultados$orderby=DocDate desc
$topDevuelve como máximo n registros$top=50
$skipOmite los primeros n registros$skip=100

Todo junto:

GET /b1s/v1/Orders?$select=DocEntry,DocNum,CardCode,DocDate,DocTotal&$filter=DocDate ge '2026-10-01' and DocumentStatus eq 'bost_Open'&$orderby=DocDate desc&$top=50

Algunas cosas que suelen confundir:

  • Los nombres de campo son los del Service Layer, no los nombres de columna de SQL. Es DocumentStatus, no DocStatus. Los valores de enumeración son nombres como bost_Open y cCustomer, no O y C.
  • Las cadenas y las fechas van entre comillas simples. Los números no.
  • Los operadores de filtro son palabras: eq, ne, gt, ge, lt, le, unidos con and y or.
  • La búsqueda de texto usa funciones, por ejemplo startswith(CardName, 'Al').
  • Codifique en la URL los espacios y las comillas cuando su librería HTTP no lo haga por usted.

Para leer un solo registro, ponga su clave entre paréntesis: GET /b1s/v1/Orders(125) para el pedido con DocEntry 125, o GET /b1s/v1/BusinessPartners('C20000') para un socio de negocios.

Nuestro generador de solicitudes del Service Layer arma estas URL por usted, con las comillas correctas.

Paso 3: Manejar la paginación

El Service Layer nunca devuelve todo de una vez. El tamaño de página predeterminado es 20. Cuando hay más registros, la respuesta termina con un enlace a la página siguiente:

{
  "value": [ ... 20 records ... ],
  "odata.nextLink": "Orders?$select=DocEntry,DocNum&$skip=20"
}

Tiene dos formas de manejarlo.

  1. Seguir el nextLink. Llame a la URL que indica (es relativa a /b1s/v1/) hasta que no vuelva ningún nextLink. Es la forma más segura de leer una lista completa.
  2. Pedir páginas más grandes. Envíe un encabezado Prefer:
GET /b1s/v1/Items?$select=ItemCode,ItemName,QuantityOnStock
Prefer: odata.maxpagesize=200

El servidor confirma el tamaño que usó en el encabezado Preference-Applied. odata.maxpagesize=0 desactiva la paginación, pero evítelo en tablas grandes como facturas o asientos contables. Una respuesta enorme puede agotar el tiempo de espera o cargar el servidor.

Paginación controlada por el servidor: Prefer: odata.maxpagesize

El tamaño de página lo decide el servidor, a menos que la solicitud pida otra cosa. Pídalo por solicitud con el encabezado OData:

GET /b1s/v1/Orders?$select=DocEntry,DocNum
Prefer: odata.maxpagesize=100

La respuesta confirma lo que se aplicó:

HTTP/1.1 200 OK
Preference-Applied: odata.maxpagesize=100

El valor predeterminado de 20 proviene de la opción PageSize en conf/b1s.conf en el servidor del Service Layer. Cambiarlo allí cambia el valor predeterminado para todos los clientes que llaman a ese Service Layer, así que prefiera el encabezado en su propio código y deje la configuración del servidor como está. Cuando ambos están definidos, gana el encabezado odata.maxpagesize.

También puede paginar usted mismo con $top y $skip. Si lo hace, agregue siempre $orderby sobre un campo clave, o los registros pueden moverse entre páginas.

Paso 4: Crear un pedido de cliente con POST

POST /b1s/v1/Orders
Content-Type: application/json
Cookie: B1SESSION=...; ROUTEID=...
{
  "CardCode": "C20000",
  "DocDueDate": "2026-10-30",
  "Comments": "Created from web portal",
  "DocumentLines": [
    { "ItemCode": "A00001", "Quantity": 5, "WarehouseCode": "01" },
    { "ItemCode": "A00002", "Quantity": 2, "UnitPrice": 150.0 }
  ]
}

Un POST exitoso devuelve 201 Created con el documento nuevo completo, incluidos DocEntry y DocNum. Guarde el DocEntry. Lo necesita para cada actualización posterior.

Paso 5: Actualizar con PATCH

PATCH cambia solo los campos que usted envía. Todo lo demás queda como está:

PATCH /b1s/v1/Orders(125)
Content-Type: application/json
{
  "Comments": "Delivery moved to Friday",
  "DocDueDate": "2026-11-06"
}

Un PATCH exitoso devuelve 204 No Content. Tenga cuidado cuando haga PATCH sobre las líneas del documento. Pruebe primero en una copia de la empresa, y vuelva a leer el documento después para confirmar que las líneas quedaron como espera.

Unir entidades: $crossjoin y QueryService_PostQuery

Un $filter normal funciona sobre un solo conjunto de entidades, así que no puede unir, por ejemplo, pedidos con socios de negocios. Para eso, el manual de usuario del Service Layer describe el recurso $crossjoin (SAP Business One 9.2, versión para SAP HANA PL07 y posteriores):

GET /b1s/v1/$crossjoin(Orders,BusinessPartners)?$expand=Orders($select=DocEntry,DocNum),BusinessPartners($select=CardCode)&$filter=Orders/CardCode eq BusinessPartners/CardCode

Para filtrar sobre las líneas del documento (cabecera y líneas juntas), el manual describe un servicio de consulta, QueryService_PostQuery, que recibe la ruta y las opciones de consulta como dos campos separados en el cuerpo de un POST:

POST /b1s/v1/QueryService_PostQuery
Content-Type: application/json
{
  "QueryPath": "$crossjoin(Orders,Orders/DocumentLines)",
  "QueryOption": "$expand=Orders($select=DocEntry,DocNum),Orders/DocumentLines($select=ItemCode,LineNum)&$filter=Orders/DocEntry eq Orders/DocumentLines/DocEntry and Orders/DocumentLines/LineNum eq 0"
}

Revise las notas de versión del manual de usuario para su release antes de depender de esto. Si la unión se vuelve compleja, una consulta SQL guardada y llamada a través del Service Layer suele ser más simple; consulte errores de SQLQueries y sus soluciones.

Paso 6: Cerrar sesión

POST /b1s/v1/Logout

Cerrar sesión libera la sesión en el servidor. Para un proceso nocturno, inicie sesión una vez, haga el trabajo y cierre sesión. Para un portal web, mantenga una sesión por usuario o un pequeño pool, en lugar de iniciar sesión en cada solicitud.

Errores comunes y qué significan

ErrorCausa habitualSolución
401 UnauthorizedSesión expirada, o cookies no enviadasInicie sesión de nuevo; verifique que ambas cookies estén en la solicitud
Error CORS en el navegadorOrigen no permitidoActive CORS en la configuración del Service Layer (CorsEnable, CorsAllowedOrigins), o llame desde su backend
Error de SSL o de certificadoCertificado autofirmado predeterminadoInstale un certificado adecuado; confíe en el autofirmado solo en pruebas
El login falla con un mensaje de licenciaEl usuario no tiene una licencia adecuadaAsigne una licencia al usuario en Administración de licencias (License Administration)
400 con un mensaje de errorCampo faltante, valor incorrecto o regla de negocioLea error.message.value; indica el campo

Los códigos de error dentro de error.code son en su mayoría los mismos que los de la DI API; consulte equivalencias en la DI API de los códigos de error del Service Layer.

Uno más: si sus inicios de sesión funcionan pero las llamadas fallan al azar detrás de un balanceador de carga, verifique que se esté enviando ROUTEID. Sin ella, la solicitud puede llegar a un nodo que no conoce su sesión.

¿DI API o Service Layer?

AspectoDI APIService Layer
TipoLibrería COMREST / OData sobre HTTPS
PlataformaWindowsCualquier plataforma cliente
LenguajesPrincipalmente .NETCualquier lenguaje con HTTP
Ideal paraAdd-ons clásicos junto al cliente de B1Portales web, apps móviles, integraciones en la nube
ConcurrenciaUna conexión por procesoMuchas sesiones, con balanceo de carga

Para todo lo nuevo que se comunica con SAP Business One desde fuera del cliente, usamos el Service Layer. Es la base sobre la que construimos portales de negocio y apps móviles. La DI API todavía tiene su lugar en los add-ons de Windows existentes.

Para probar estas llamadas contra su propio sistema, empiece con el generador de solicitudes del Service Layer.

Si necesita un portal, una app o una integración construida sobre el Service Layer, contacte a Kodenova indicando su versión de SAP Business One y lo que desea conectar.

Fuentes

Servicios relacionados

Preguntas frecuentes

Preguntas frecuentes

¿Cuál es la diferencia entre /b1s/v1 y /b1s/v2?

La ruta v1 usa OData versión 3 y v2 usa OData versión 4. SAP ahora considera v4 como el protocolo principal. La diferencia más visible en las respuestas es el prefijo de las anotaciones: v2 devuelve @odata.nextLink donde v1 devuelve odata.nextLink.

¿Cuánto dura una sesión del Service Layer?

El tiempo de espera predeterminado es de 30 minutos de inactividad. La respuesta del Login devuelve el valor en SessionTimeout. Cada llamada reinicia el temporizador, así que una integración activa mantiene viva su sesión.

¿Cuál es el tamaño de página predeterminado en el Service Layer de SAP B1?

20. Por eso un GET devuelve solo 20 registros. Siga el nextLink de la respuesta, o envíe el encabezado Prefer: odata.maxpagesize=100 para obtener páginas más grandes en esa solicitud. El valor predeterminado de todo el servidor es la opción PageSize en conf/b1s.conf, y el encabezado Prefer tiene prioridad sobre ella.

¿Debo usar la DI API o el Service Layer para un proyecto nuevo?

Para integraciones nuevas web, móviles y en la nube, use el Service Layer. Funciona sobre HTTPS desde cualquier lenguaje y plataforma. La DI API todavía tiene sentido para add-ons .NET existentes que corren en Windows junto al cliente de SAP Business One.

¿Por qué mi aplicación en el navegador falla con un error CORS cuando en Postman funciona?

Los navegadores bloquean las llamadas entre orígenes distintos a menos que el servidor las permita. CORS está desactivado por defecto en el Service Layer. Actívelo en la configuración del Service Layer y agregue el origen de su aplicación, o llame al Service Layer desde su propio backend.

¿Listo para empezar?

Cuéntenos lo que necesita. Recibirá una respuesta técnica clara, un alcance definido y un precio fijo, normalmente en 24 horas.