SAP PM Connector¶
The SapPmConnector integrates Machina with SAP Plant Maintenance (SAP PM)
on SAP S/4HANA, reading and creating maintenance data via OData REST APIs.
Prerequisites¶
- SAP S/4HANA system with OData services enabled
- API access to at least:
API_EQUIPMENT,API_MAINTENANCEORDER,API_MAINTENANCEPLAN - OAuth 2.0 Client Credentials (recommended) or HTTP Basic authentication
Installation¶
Configuration¶
from machina.connectors import SapPM
from machina.connectors.cmms import OAuth2ClientCredentials
connector = SapPM(
url="https://sap.example.com/sap/opu/odata/sap",
auth=OAuth2ClientCredentials(
token_url="https://sap.example.com/oauth/token",
client_id="my-client",
client_secret="my-secret",
),
sap_client="100",
)
await connector.connect()
Capabilities¶
| Capability | Description |
|---|---|
read_assets |
Read equipment master records (API_EQUIPMENT/Equipment) |
read_work_orders |
Read maintenance orders — filter by asset_id and/or status (accepts WorkOrderStatus enum or raw SAP code) |
create_work_order |
Create maintenance orders (CSRF token handled automatically) |
update_work_order |
Update status, assignee, or description via PATCH (CSRF-safe) |
read_spare_parts |
Read BOM / material data (configurable endpoint, default API_BILL_OF_MATERIAL_SRV/BillOfMaterialItem) |
read_maintenance_plans |
Read preventive-maintenance plans (API_MAINTENANCEPLAN/MaintenancePlan) |
Convenience methods¶
These methods are available but are not declared as agent-discoverable capabilities:
| Method | Description |
|---|---|
get_work_order(id) |
Fetch a single maintenance order by number |
close_work_order(id) |
Transition to CLOSED (SAP TECO) via update_work_order |
cancel_work_order(id) |
Transition to CANCELLED (SAP DLFL) via update_work_order |
Usage Examples¶
Read assets¶
assets = await connector.read_assets()
for asset in assets:
print(f"{asset.id}: {asset.name} ({asset.criticality})")
Read work orders with Machina enum filter¶
from machina.domain.work_order import WorkOrderStatus
wos = await connector.read_work_orders(
asset_id="10000001",
status=WorkOrderStatus.IN_PROGRESS, # auto-mapped to SAP "PCNF"
)
Get a single work order¶
wo = await connector.get_work_order("4000001")
if wo:
print(f"{wo.id}: {wo.status} — {wo.failure_mode}")
Create a work order¶
from datetime import datetime, timezone
from machina.domain import WorkOrder, WorkOrderType, Priority
wo = WorkOrder(
id="",
type=WorkOrderType.CORRECTIVE,
priority=Priority.HIGH,
asset_id="10000001",
description="Replace bearing on drive end",
created_at=datetime.now(tz=timezone.utc),
updated_at=datetime.now(tz=timezone.utc),
)
created = await connector.create_work_order(wo)
print(f"Created: {created.id}")
Update / close a work order¶
from machina.domain.work_order import WorkOrderStatus
# Update specific fields
updated = await connector.update_work_order(
"4000001",
status=WorkOrderStatus.COMPLETED,
assigned_to="TECH_SMITH",
)
# Convenience wrappers
await connector.close_work_order("4000001")
await connector.cancel_work_order("4000002")
Configurable BOM Endpoint¶
The spare-parts endpoint varies across SAP versions. The default targets
API_BILL_OF_MATERIAL_SRV/BillOfMaterialItem (S/4HANA Cloud). Override
for on-premise or legacy systems:
connector = SapPM(
url="https://sap.example.com/sap/opu/odata/sap",
auth=auth,
bom_service="API_EQUIPMENT", # legacy service
bom_entity_set="EquipmentBOM", # legacy entity set
bom_material_field="Material", # field name for SKU filter
bom_equipment_field="Equipment", # field name for asset filter
)
Entity Mapping¶
| SAP Field | Machina Field |
|---|---|
Equipment |
Asset.id |
EquipmentName |
Asset.name |
EquipmentCategory |
Asset.type (M→Rotating, E→Electrical, I→Instrument, …) |
FunctionalLocation |
Asset.location |
ABCIndicator |
Asset.criticality (A/B/C) |
MaintenanceOrder |
WorkOrder.id |
MaintenanceOrderType |
WorkOrder.type (PM01→Corrective, PM02→Preventive, PM03→Predictive, PM04→Improvement) |
MaintPriority |
WorkOrder.priority (1→Emergency, 2→High, 3→Medium, 4→Low) |
MaintenanceOrderSystemStatus |
WorkOrder.status (CRTD, REL, PCNF, CNF, TECO, CLSD, DLFL) |
MaintenanceActivityType |
WorkOrder.failure_mode |
MaintenanceCause / MaintNotifCause |
WorkOrder.failure_cause |
Resilience¶
All HTTP calls route through a shared retry helper with exponential backoff. Retries are triggered on:
- 429 Too Many Requests — honours the
Retry-Afterheader - 503 Service Unavailable — transient upstream failures
- Network errors —
TimeoutException,ConnectError,ReadError
Default: 3 retries, 0.5 s → 8 s backoff cap.
Known Limitations¶
- OData v2 vs v4: The connector handles both response formats (
d.resultsandvalue). Your SAP system may use either depending on the service version. - Custom fields: SAP Z-fields are stored in
metadatadict; access them viaasset.metadata["ZZ_CUSTOM_FIELD"]. - CSRF tokens: Write operations (create, update) automatically fetch a CSRF token within the same HTTP session to ensure cookie-based session affinity.
- Functional locations: Currently read as part of the
Asset.locationfield. A dedicated functional-location hierarchy is planned for a future release.
API Reference¶
SapPmConnector ¶
SapPmConnector(*, url: str, auth: _AuthUnion, sap_client: str = '', bom_service: str = 'API_BILL_OF_MATERIAL_SRV', bom_entity_set: str = 'BillOfMaterialItem', bom_material_field: str = 'BillOfMaterialComponent', bom_equipment_field: str = '')
Connector for SAP Plant Maintenance (S/4HANA).
Provides integration with SAP's OData APIs for reading and creating maintenance data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
Base URL of the SAP OData gateway
(e.g. |
required |
auth
|
_AuthUnion
|
Authentication strategy — :class: |
required |
sap_client
|
str
|
SAP client number (sent as |
''
|
bom_service
|
str
|
OData service group used by :meth: |
'API_BILL_OF_MATERIAL_SRV'
|
bom_entity_set
|
str
|
Entity set inside |
'BillOfMaterialItem'
|
bom_material_field
|
str
|
Name of the material / component field on
|
'BillOfMaterialComponent'
|
bom_equipment_field
|
str
|
Name of the equipment field on
|
''
|
Example
from machina.connectors import SapPM
from machina.connectors.cmms import OAuth2ClientCredentials
connector = SapPM(
url="https://sap.example.com/sap/opu/odata/sap",
auth=OAuth2ClientCredentials(
token_url="https://sap.example.com/oauth/token",
client_id="my-client",
client_secret="my-secret",
),
sap_client="100",
)
await connector.connect()
assets = await connector.read_assets()
connect
async
¶
Verify connection to SAP OData gateway.
For :class:OAuth2ClientCredentials, fetches the access token
first. Then issues a $metadata request to verify reachability.
Raises:
| Type | Description |
|---|---|
ConnectorAuthError
|
If authentication fails. |
ConnectorError
|
If the OData gateway is unreachable. |
get_asset
async
¶
Look up a single equipment record by number.
read_work_orders
async
¶
Read maintenance orders from SAP PM.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
asset_id
|
str
|
Filter by equipment number. |
''
|
status
|
WorkOrderStatus | str
|
Filter by status — accepts a :class: |
''
|
get_work_order
async
¶
Look up a single maintenance order by number.
create_work_order
async
¶
update_work_order
async
¶
update_work_order(work_order_id: str, *, status: WorkOrderStatus | None = None, assigned_to: str | None = None, description: str | None = None) -> WorkOrder
Update an existing maintenance order in SAP PM.
Only non-None fields are included in the PATCH payload.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
work_order_id
|
str
|
SAP maintenance order number. |
required |
status
|
WorkOrderStatus | None
|
New :class: |
None
|
assigned_to
|
str | None
|
New responsible person. |
None
|
description
|
str | None
|
New order description. |
None
|
Returns:
| Type | Description |
|---|---|
WorkOrder
|
The updated work order. |
close_work_order
async
¶
Transition a work order to CLOSED status.
cancel_work_order
async
¶
Transition a work order to CANCELLED status.
read_spare_parts
async
¶
Read material / spare-part data from SAP's BOM service.
The service group, entity set, and filter field names are
configured on the connector (see constructor args
bom_service, bom_entity_set, bom_material_field,
bom_equipment_field). Defaults target the standard S/4HANA
API_BILL_OF_MATERIAL_SRV/BillOfMaterialItem entity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
asset_id
|
str
|
Optional Equipment identifier. Applied as a
server-side OData |
''
|
sku
|
str
|
Optional material number, translated to a server-side
|
''
|
read_maintenance_plans
async
¶
Read preventive-maintenance plans from SAP PM.
read_maintenance_history
async
¶
Return completed/closed maintenance orders for an asset.