Service Layer di SAP Business One: login, $filter, $select e paginazione, con esempi funzionanti
By Zain Abbas, Kodenova · Updated
Che cos’è il Service Layer
Il Service Layer è l’API web di SAP Business One. Espone oggetti di business come partner commerciali, articoli, ordini e fatture su HTTPS, tramite il protocollo OData. Qualsiasi linguaggio in grado di inviare una richiesta HTTP può usarlo: JavaScript, Python, C#, PHP o uno strumento low-code.
È nato sulla versione HANA di SAP Business One. I feature pack più recenti della 10.0 lo offrono anche per SQL Server. Per impostazione predefinita ascolta sulla porta 50000 in HTTPS:
https://your-b1-server:50000/b1s/v1/
https://your-b1-server:50000/b1s/v2/
v1 o v2
Entrambi i percorsi raggiungono gli stessi oggetti di business.
- /b1s/v1 usa OData versione 3. Funziona ancora e la maggior parte del codice meno recente lo usa.
- /b1s/v2 usa OData versione 4, che SAP considera oggi il protocollo principale.
La prima differenza che si nota è nelle risposte: v1 restituisce odata.nextLink, v2 restituisce @odata.nextLink. Per i nuovi progetti scelga v2 e non cambi più. Gli esempi qui sotto usano i percorsi v1 perché sono ancora i più diffusi sul campo. Sostituisca v1 con v2 e funzionano allo stesso modo.
Passo 1: login
Invii un POST con il database aziendale, l’utente e la password:
POST https://your-b1-server:50000/b1s/v1/Login
Content-Type: application/json
{
"CompanyDB": "SBODEMOUS",
"UserName": "manager",
"Password": "your-password"
}
Una risposta positiva contiene l’ID di sessione, la versione e il timeout in minuti:
{
"SessionId": "1c9e4b2a-....",
"Version": "1000xxx",
"SessionTimeout": 30
}
Il server imposta anche due cookie: B1SESSION e ROUTEID.
- B1SESSION identifica la sessione.
- ROUTEID indica al bilanciatore di carico del Service Layer quale nodo contiene la sessione, così ogni richiesta torna allo stesso nodo.
Un browser memorizza e invia questi cookie automaticamente. Fuori dal browser deve aggiungerli entrambi all’intestazione Cookie di ogni chiamata successiva:
Cookie: B1SESSION=1c9e4b2a-....; ROUTEID=.node1
Per impostazione predefinita la sessione scade dopo 30 minuti di inattività. Ogni chiamata azzera il timer. Imposti il client in modo che una risposta 401 provochi un solo nuovo login e un nuovo tentativo, non un ciclo infinito.
Passo 2: leggere i dati con GET
Un GET semplice restituisce una pagina di record con tutti i campi:
GET /b1s/v1/BusinessPartners
È lento e pesante. Usi le opzioni di query OData per chiedere solo ciò che le serve.
| Opzione | Cosa fa | Esempio |
|---|---|---|
$select | Restituisce solo questi campi | $select=CardCode,CardName,Balance |
$filter | Restituisce solo i record corrispondenti | $filter=CardType eq 'cCustomer' |
$orderby | Ordina i risultati | $orderby=DocDate desc |
$top | Restituisce al massimo n record | $top=50 |
$skip | Salta i primi n record | $skip=100 |
Tutto insieme:
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
Alcuni punti su cui si inciampa spesso:
- I nomi dei campi sono quelli del Service Layer, non i nomi delle colonne SQL. Si scrive
DocumentStatus, nonDocStatus. I valori enumerati sono nomi comebost_OpenecCustomer, nonOeC. - Stringhe e date vanno tra apici singoli. I numeri no.
- Gli operatori di filtro sono parole:
eq,ne,gt,ge,lt,le, combinati conandeor. - La ricerca testuale usa funzioni, ad esempio
startswith(CardName, 'Al'). - Codifichi nell’URL spazi e apici quando la sua libreria HTTP non lo fa da sola.
Per leggere un singolo record, metta la chiave tra parentesi: GET /b1s/v1/Orders(125) per l’ordine con DocEntry 125, oppure GET /b1s/v1/BusinessPartners('C20000') per un partner commerciale.
Il nostro creatore di richieste Service Layer compone questi URL per lei, con gli apici corretti.
Passo 3: gestire la paginazione
Il Service Layer non restituisce mai tutto in una volta. La dimensione di pagina predefinita è 20. Quando ci sono altri record, la risposta termina con un link alla pagina successiva:
{
"value": [ ... 20 records ... ],
"odata.nextLink": "Orders?$select=DocEntry,DocNum&$skip=20"
}
Ha due modi per gestirla.
- Seguire il nextLink. Richiami l’URL indicato (è relativo a
/b1s/v1/) finché non arriva più alcun nextLink. È il modo più sicuro per leggere un elenco completo. - Chiedere pagine più grandi. Invii un’intestazione
Prefer:
GET /b1s/v1/Items?$select=ItemCode,ItemName,QuantityOnStock
Prefer: odata.maxpagesize=200
Il server conferma la dimensione usata nell’intestazione Preference-Applied. odata.maxpagesize=0 disattiva la paginazione, ma eviti di farlo su tabelle grandi come fatture o registrazioni contabili: una risposta enorme può andare in timeout o caricare il server.
Paginazione gestita dal server: Prefer: odata.maxpagesize
La dimensione della pagina la decide il server, a meno che la richiesta non chieda altro. La chieda per singola richiesta con l’intestazione OData:
GET /b1s/v1/Orders?$select=DocEntry,DocNum
Prefer: odata.maxpagesize=100
La risposta conferma che cosa è stato applicato:
HTTP/1.1 200 OK
Preference-Applied: odata.maxpagesize=100
Il valore predefinito di 20 deriva dall’opzione PageSize in conf/b1s.conf sul server del Service Layer. Modificarlo lì cambia il valore predefinito per tutti i client che chiamano quel Service Layer, quindi nel suo codice preferisca l’intestazione e lasci invariata l’impostazione del server. Quando sono impostati entrambi, prevale l’intestazione odata.maxpagesize.
Può anche paginare da sé con $top e $skip. In quel caso aggiunga sempre $orderby su un campo chiave, altrimenti i record possono spostarsi da una pagina all’altra.
Passo 4: creare un ordine 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 riuscito restituisce 201 Created con il nuovo documento completo, compresi DocEntry e DocNum. Salvi il DocEntry: le servirà per ogni modifica successiva.
Passo 5: modificare con PATCH
PATCH modifica solo i campi inviati. Tutto il resto rimane invariato:
PATCH /b1s/v1/Orders(125)
Content-Type: application/json
{
"Comments": "Delivery moved to Friday",
"DocDueDate": "2026-11-06"
}
Un PATCH riuscito restituisce 204 No Content. Faccia attenzione quando modifica con PATCH le righe di un documento: provi prima su una copia della società e poi rilegga il documento per verificare che le righe siano come si aspetta.
Unire entità: $crossjoin e QueryService_PostQuery
Un normale $filter lavora su un solo entity set, quindi non può unire, ad esempio, ordini e partner commerciali. Per questo il manuale utente del Service Layer descrive la risorsa $crossjoin (SAP Business One 9.2 versione per SAP HANA PL07 e successive):
GET /b1s/v1/$crossjoin(Orders,BusinessPartners)?$expand=Orders($select=DocEntry,DocNum),BusinessPartners($select=CardCode)&$filter=Orders/CardCode eq BusinessPartners/CardCode
Per filtrare sulle righe dei documenti (testata e righe insieme), il manuale descrive un servizio di query, QueryService_PostQuery, che riceve il percorso e le opzioni di query come due campi separati nel corpo di 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"
}
Prima di farci affidamento, controlli le note di versione nel manuale utente per la sua release. Se l’unione diventa complessa, spesso è più semplice una query SQL salvata richiamata tramite il Service Layer; veda errori di SQLQueries e soluzioni.
Passo 6: logout
POST /b1s/v1/Logout
Il logout libera la sessione sul server. Per un job notturno, faccia login una volta, svolga il lavoro e poi il logout. Per un portale web mantenga una sessione per utente o un piccolo pool, invece di fare login a ogni richiesta.
Errori comuni e cosa significano
| Errore | Causa abituale | Soluzione |
|---|---|---|
| 401 Unauthorized | Sessione scaduta o cookie non inviati | Rifaccia il login; verifichi che entrambi i cookie siano nella richiesta |
| Errore CORS nel browser | Origine non consentita | Attivi CORS nella configurazione del Service Layer (CorsEnable, CorsAllowedOrigins) oppure chiami dal suo backend |
| Errore SSL o di certificato | Certificato autofirmato predefinito | Installi un certificato valido; consideri attendibile quello autofirmato solo in test |
| Il login fallisce con un messaggio di licenza | L’utente non ha una licenza adatta | Assegni una licenza all’utente in License Administration |
| 400 con un messaggio di errore | Campo mancante, valore errato o regola di business | Legga error.message.value: indica il campo |
I codici di errore in error.code sono per lo più gli stessi della DI API; veda gli equivalenti DI API dei codici di errore del Service Layer.
Un’ultima cosa: se il login funziona ma le chiamate falliscono a caso dietro un bilanciatore di carico, verifichi che il ROUTEID venga inviato. Senza di esso la richiesta può arrivare a un nodo che non conosce la sua sessione.
DI API o Service Layer?
| Aspetto | DI API | Service Layer |
|---|---|---|
| Tipo | Libreria COM | REST / OData su HTTPS |
| Piattaforma | Windows | Qualsiasi piattaforma client |
| Linguaggi | Soprattutto .NET | Qualsiasi linguaggio con HTTP |
| Ideale per | Add-on classici accanto al client B1 | Portali web, app mobile, integrazioni cloud |
| Concorrenza | Una connessione per processo | Molte sessioni, con bilanciamento del carico |
Per tutto ciò che di nuovo dialoga con SAP Business One dall’esterno del client usiamo il Service Layer. È la base su cui sviluppiamo portali aziendali e app mobile. La DI API ha ancora il suo posto negli add-on Windows esistenti. Le stesse chiamate al Service Layer possono alimentare l’IA: veda come collegare ChatGPT a SAP Business One con un server MCP in sola lettura.
Per provare queste chiamate sul suo sistema, parta dal creatore di richieste Service Layer.
Se le serve un portale, un’app o un’integrazione basata sul Service Layer, contatti Kodenova indicando la sua versione di SAP Business One e che cosa vuole collegare.
Sources
- SAP Learning: Exploring various data retrieval options using GET request (Service Layer)
- SAP Help Portal: Working with SAP Business One Service Layer (manuale utente, PDF)
- SAP Help Portal: Working with SAP Business One Service Layer, manuale utente per la versione 9.1–9.3 per SAP HANA (versione del documento 1.15, PDF)
- SAP Help Portal: Service Layer API Reference
- SAP Community: Is it possible to log into B1 Service Layer with cookies?
- SAP Community: Service Layer CORS header Access-Control
- Apideck: SAP Business One API integration guide
Related services
Frequently asked questions
Che differenza c'è tra /b1s/v1 e /b1s/v2?
Il percorso v1 usa OData versione 3, il v2 usa OData versione 4. SAP considera oggi la v4 il protocollo principale. La differenza più visibile nelle risposte è il prefisso delle annotazioni: v2 restituisce @odata.nextLink dove v1 restituisce odata.nextLink.
Quanto dura una sessione del Service Layer?
Il timeout predefinito è di 30 minuti di inattività. La risposta del Login riporta il valore in SessionTimeout. Ogni chiamata azzera il timer, quindi un'integrazione attiva mantiene viva la propria sessione.
Qual è la dimensione di pagina predefinita nel Service Layer di SAP B1?
20. Per questo un GET restituisce solo 20 record. Segua il nextLink nella risposta, oppure invii l'intestazione Prefer: odata.maxpagesize=100 per ottenere pagine più grandi in quella richiesta. Il valore predefinito per tutto il server è l'opzione PageSize in conf/b1s.conf, e l'intestazione Prefer ha la precedenza.
Per un nuovo progetto conviene la DI API o il Service Layer?
Per le nuove integrazioni web, mobile e cloud usi il Service Layer: funziona su HTTPS da qualsiasi linguaggio e piattaforma. La DI API ha ancora senso per gli add-on .NET esistenti che girano su Windows accanto al client SAP Business One.
Perché la mia app nel browser dà un errore CORS mentre in Postman funziona?
I browser bloccano le chiamate tra origini diverse, a meno che il server non le consenta. Nel Service Layer CORS è disattivato per impostazione predefinita. Lo attivi nella configurazione del Service Layer indicando l'origine della sua app, oppure chiami il Service Layer dal suo backend.
Iniziamo?
Ci descriva di cosa ha bisogno. Riceverà una risposta tecnica chiara, un perimetro definito e un prezzo, di norma entro 24 ore.
