# SAP S/4HANA and ECC

Gateway service roots, OData V2 and V4 paths, the sap-client parameter and the service catalog.

S/4HANA and ECC publish their APIs through SAP Gateway - classic services use OData V2 and newer S/4HANA ones use V4. Both kinds work with outgoing SAP connections, the only difference being the address and the version selected in the connection's form.

## Connection settings {#connection-settings}

- Address: `https://host:port/sap/opu/odata/sap/API_BUSINESS_PARTNER` (V2) or `https://host:port/sap/opu/odata4/sap/...` (V4)
- OData version: match the service - `2.0` for `/sap/opu/odata/`, `4.0` for `/sap/opu/odata4/`
- Auth type: Basic with a technical user, or OAuth2 in BTP-fronted setups
- Needs CSRF token: keep it enabled - Gateway requires X-CSRF-Token for every write

## Finding services {#finding-services}

Every Gateway system holds a catalog of the services it activates. Reading it through an outgoing SAP connection pointed at the catalog's root lists what is available:

- Catalog address: `https://host:port/sap/opu/odata/IWFND/CATALOGSERVICE;v=2`
- Entity set: `ServiceCollection`

```python
conn = self.sap['SAP.Catalog']

for service in conn.iter('ServiceCollection', top=50):
    self.logger.info('Service -> %s', service['TechnicalServiceName'])
```

In S/4HANA, the services themselves are documented in the SAP API Business Hub - the entity sets used in these chapters, such as `A_BusinessPartner` in `API_BUSINESS_PARTNER`, come from the standard whitelisted APIs.

## The sap-client parameter {#the-sap-client-parameter}

Systems with more than one client expect the `sap-client` query parameter with each request. It is passed through the custom query option, alongside any other options:

```python
partners = conn.read('A_BusinessPartner',
    top=10,
    custom={'sap-client': '100'},
)
```

## Reading related entities {#reading-related-entities}

Gateway services model relations as navigation properties - addresses of a business partner, items of a sales order. The expand option brings them inline with the parent:

```python
orders = conn.read('A_SalesOrder',
    filter="SalesOrganization eq '1010'",
    expand='to_Item',
    top=10,
)

for order in orders:
    items = order['to_Item']
    self.logger.info('Order %s has %d items', order['SalesOrder'], len(items))
```

## Learn more {#learn-more}

- [SAP integrations](https://zato.io/docs/dev/sap/) - Business entities as Python dicts, over OData V2 and V4
- [Querying SAP](https://zato.io/docs/dev/sap/query/) - Filters, paging, counting and related entities
- [SuccessFactors](https://zato.io/docs/dev/sap/success-factors/) - The OData V2 API, its authentication and its quirks
- [SAP security](https://zato.io/docs/dev/sap/security/) - Basic Auth, API keys, bearer tokens and OAuth2 client credentials
- [SAP in enmasse](https://zato.io/docs/dev/sap/enmasse/) - Connections as YAML, with secrets read from the environment
