For the past few months, I have been working on an S/4 HANA new implementation leveraging the SAP integration suite – Cloud Integration, Event Mesh, API Hub, Integration advisor, etc. In this blog, I wanted to capture sending out business transaction events from S/4 HANA to the Event Mesh and having CPI read from the Event Mesh and then process the event, read additional data from S/4 HANA using the OData API to enrich the message, and then publish to an outbound queue for downstream systems.
So we set this up and we were sending sales order creations and changes to the event mesh:
Maintain the channel topics for publishing the business events
Then we create the queue in Event Mesh and the queue subscription to subscribe to the sales order create and change events:
For our testing, we used the SAP transaction code SWUE to trigger the sales order event to send to the queue. Once this is all set up, the events will be triggered automatically when creating or changing a sales order:
Here are the sales order in the queue:
So once we receive the sales order business transaction events in the queue, we can then read the queue and process the message to enhance the details through the OData call to S/4 HANA and publish on an outbound queue.
Here is the IFlow:
Here is the Groovy script to extract the sales order number:
// Extract the SALESORDER from the Business Transaction Event
import com.sap.gateway.ip.core.customdev.util.Message;
import groovy.json.*
def Message processData(Message message) {
def json = new JsonSlurper().parseText(message.getBody(String));
message.setHeader('SALESORDER',json.data.KEY[0].SALESORDER);
message.setHeader('eventType',json.eventType);
return message;
}
Here we log the business transaction event with the sales order number we extracted:
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
def Message processData(Message message) {
def map = message.getHeaders();
def salesorder = map.get("SALESORDER");
def eventType = map.get("eventType");
def body = message.getBody(java.lang.String) as String;
def messageLog = messageLogFactory.getMessageLog(message);
//Properties
def properties = message.getProperties();
String sBody = "SalesOrder " + salesorder + " Event " + eventType + " from S4 HANA";
if(messageLog != null) {
messageLog.setStringProperty(sBody, body);
messageLog.addAttachmentAsString(sBody, body, 'application/json');
}
return message;
}
Here is the Request-Reply to call the OData sales order API to get the order details:
Here is the Log SAP OData XML Response:
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
def Message processData(Message message) {
def map = message.getHeaders();
def salesorder = map.get("SALESORDER");
def body = message.getBody(java.lang.String) as String;
def messageLog = messageLogFactory.getMessageLog(message);
//Properties
def properties = message.getProperties();
String sBody = "OData SalesOrder " + salesorder + " Details XML from S4 HANA";
if(messageLog != null) {
messageLog.setStringProperty(sBody, body);
messageLog.addAttachmentAsString(sBody, body, 'text/xml');
}
return message;
}
Here is the Log SAP OData JSON Response:
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
def Message processData(Message message) {
def map = message.getHeaders();
def salesorder = map.get("SALESORDER");
def body = message.getBody(java.lang.String) as String;
def messageLog = messageLogFactory.getMessageLog(message);
//Properties
def properties = message.getProperties();
String sBody = "OData SalesOrder " + salesorder + " Details JSON from S4 HANA";
if(messageLog != null) {
messageLog.setStringProperty(sBody, body);
messageLog.addAttachmentAsString(sBody, body, 'application/json');
}
return message;
}
And here is the final publishing to the outbound queue:
So once the event is written to the queue and the Flow is triggered, then you can see the following messages in the integration message monitoring:
Here is how the event json looks like:
SalesOrder 0000000494 Event BO.SalesOrder.Created from S4 HANA
{
"eventType": "BO.SalesOrder.Created",
"cloudEventsVersion": "0.1",
"source": https://sap.corp,
"eventID": "AvI8lTL3HuyLryFqiGugrw==",
"eventTime": "2021-10-15T05:30:44Z",
"schemaURL": https://sap.corp/sap/opu/odata/IWXBE/BROWSER_SRV/,
"contentType": "application/json",
"data": {
"KEY": [
{
"SALESORDER": "0000000494"
}
]
}
}
OData SalesOrder 0000000494 Details XML from S4 HANA
<A_SalesOrder>
<A_SalesOrderType>
<CreationDate>2021-10-21T00:00:00.000</CreationDate>
<CreatedByUser>YTHAM</CreatedByUser>
<OrganizationDivision>00</OrganizationDivision>
<PurchaseOrderByCustomer>YT_ODN_AU</PurchaseOrderByCustomer>
<DistributionChannel>10</DistributionChannel>
<SalesOrganization>3010</SalesOrganization>
<SoldToParty>30100001</SoldToParty>
<TotalNetAmount>120.00</TotalNetAmount>
<to_Item>
<A_SalesOrderItemType>
<SalesOrderItemText>Service Material 01</SalesOrderItemText>
<ProductionPlant>3010</ProductionPlant>
<PurchaseOrderByCustomer>YT_ODN_AU</PurchaseOrderByCustomer>
<Material>SM0001</Material>
<MaterialByCustomer>
</MaterialByCustomer>
<RequestedQuantityUnit>H</RequestedQuantityUnit>
<TransactionCurrency>AUD</TransactionCurrency>
<PricingDate>2021-10-21T00:00:00.000</PricingDate>
<NetAmount>120.00</NetAmount>
<SalesOrderItemCategory>TAD</SalesOrderItemCategory>
<SalesOrderItem>10</SalesOrderItem>
<HigherLevelItem>0</HigherLevelItem>
<RequestedQuantity>1.000</RequestedQuantity>
<SalesOrder>494</SalesOrder>
</A_SalesOrderItemType>
</to_Item>
<TransactionCurrency>AUD</TransactionCurrency>
<SalesOrderType>OR</SalesOrderType>
<SalesOrderDate>2021-10-21T00:00:00.000</SalesOrderDate>
<to_Partner>
<A_SalesOrderHeaderPartnerType>
<PartnerFunction>SP</PartnerFunction>
<Customer>30100001</Customer>
<Supplier>
</Supplier>
<Personnel>0</Personnel>
<ContactPerson>0</ContactPerson>
<SalesOrder>494</SalesOrder>
</A_SalesOrderHeaderPartnerType>
<A_SalesOrderHeaderPartnerType>
<PartnerFunction>BP</PartnerFunction>
<Customer>30100001</Customer>
<Supplier>
</Supplier>
<Personnel>0</Personnel>
<ContactPerson>0</ContactPerson>
<SalesOrder>494</SalesOrder>
</A_SalesOrderHeaderPartnerType>
<A_SalesOrderHeaderPartnerType>
<PartnerFunction>PY</PartnerFunction>
<Customer>30100001</Customer>
<Supplier>
LanguageHTML/XML
</Supplier>
<Personnel>0</Personnel>
<ContactPerson>0</ContactPerson>
<SalesOrder>494</SalesOrder>
</A_SalesOrderHeaderPartnerType>
<A_SalesOrderHeaderPartnerType>
<PartnerFunction>SH</PartnerFunction>
<Customer>30100001</Customer>
<Supplier>
</Supplier>
<Personnel>0</Personnel>
<ContactPerson>0</ContactPerson>
<SalesOrder>494</SalesOrder>
</A_SalesOrderHeaderPartnerType>
</to_Partner>
<SalesOrder>494</SalesOrder>
</A_SalesOrderType>
</A_SalesOrder>
OData SalesOrder 0000000494 Details JSON from S4 HANA
{
"A_SalesOrder": {
"A_SalesOrderType": {
"CreationDate": "2021-10-21T00:00:00.000",
"CreatedByUser": "YTHAM",
"OrganizationDivision": "00",
"PurchaseOrderByCustomer": "YT_ODN_AU",
"DistributionChannel": "10",
"SalesOrganization": "3010",
"SoldToParty": "30100001",
"TotalNetAmount": "120.00",
"to_Item": {
"A_SalesOrderItemType": {
"SalesOrderItemText": "Service Material 01",
"ProductionPlant": "3010",
"PurchaseOrderByCustomer": "YT_ODN_AU",
"Material": "SM0001",
"MaterialByCustomer": "",
"RequestedQuantityUnit": "H",
"TransactionCurrency": "AUD",
"PricingDate": "2021-10-21T00:00:00.000",
"NetAmount": "120.00",
"SalesOrderItemCategory": "TAD",
"SalesOrderItem": "10",
"HigherLevelItem": "0",
"RequestedQuantity": "1.000",
"SalesOrder": "494"
}
},
"TransactionCurrency": "AUD",
"SalesOrderType": "OR",
"SalesOrderDate": "2021-10-21T00:00:00.000",
"to_Partner": {
"A_SalesOrderHeaderPartnerType": [
{
"PartnerFunction": "SP",
"Customer": "30100001",
"Supplier": "",
"Personnel": "0",
"ContactPerson": "0",
"SalesOrder": "494"
},
{
"PartnerFunction": "BP",
"Customer": "30100001",
"Supplier": "",
"Personnel": "0",
"ContactPerson": "0",
"SalesOrder": "494"
},
{
"PartnerFunction": "PY",
"Customer": "30100001",
"Supplier": "",
"Personnel": "0",
"ContactPerson": "0",
"SalesOrder": "494"
},
{
"PartnerFunction": "SH",
"Customer": "30100001",
"Supplier": "",
"Personnel": "0",
"ContactPerson": "0",
"SalesOrder": "494"
}
]
},
"SalesOrder": "494"
}
}
}
Lastly, I wanted to add some troubleshooting tips. When we first configured the outbound topic, the events were not being sent out to the message queue. So we had to create a ticket for SAP and we were told that the entries:
The issue is because of 2 wrong entries maintained in the table SBO_I_BODEF for Sales Order. Please find the attached screenshot.
Wrong Entries:
SALES ORDER BO BUS2032
SALES ORDER SOA 114
Expected Entries:
SalesOrder BO BUS2032
SalesOrder CL CL_SD_SALESORDER_WORKFLOW
SalesOrder SOA 114
Expected entries are there in the table. Since there are wrong entries also present in the table for the same Business Object, events are not getting passed. Please remove those entries and retest the issue.
Maintain Customizing settings under
SAP NetWeaver->Application Server->Business Management->SAP Object Type Repository->SAP Object Types->Maintain Object Representation
Make sure the following entries are there for BUS2032. There should not be another set of entries there for BUS2032:
Hopefully, this will help if you are embarking on the journey of publishing events from S/4 HANA to downstream subscribers. Though this is the evolution of SAP integration with events and OData, the IDoc and SOAP async approaches still are widely used and maybe the better choice given the scenarios. After having set up IDoc integration and SOAP integration through PI/PO for many projects, the new event mesh approach is a nice option to have also in the wide array of SAP integration methodolgies.