This article is contributed. See the original author and article here.
Introduction
One of the ways that BizTalk can communicate with SAP servers is by using Business Application Programming Interfaces (BAPIs), as documented in Operations on BAPIs in SAP. SAP introduced BAPIs as an object-oriented way to structure business data and processes. It is assumed that the reader has some familiarity with how to use this functionality in BizTalk.
We extend the sample presented in Run BAPI Transactions in SAP using BizTalk Server by considering a more general scenario where the same BizTalk orchestration processes multiple BAPI transactions received separately but as part of the same Logical Unit of Work (LUW) on the SAP server. The last stage of the scenario is to verify the status of the transactions. All sample code can be downloaded.
Prerequisites
Starting with BizTalk Server 2016 CU6 and BizTalk Server 2020 CU1, BAPI transactions need to be enabled explicitly, by creating the registry value HKLMSOFTWAREMicrosoftBizTalk Server3.0ConfigurationEnableBizTalkSapSessionProvider of type DWORD and setting it to ‘1’, as documented in the fix page.
The BAPI object is the Sales Order (BUS2032), as in Message Schemas for BAPI Operations. The schemas and metadata are downloaded by using the “Consume Adapter Service” in Visual Studio, as explained in this section of the SAP Adapter documentation. The process is summarized below:
Note: Published SAP enhancement packs can add an auto-commit feature to specific object types (e.g., purchase orders). It is not the case here for sales order on the SAP server that was used for this tutorial. It is important since the point of the demo is to accumulate BAPI transactions before committing them.
Orchestrated Scenario
In the context of a tutorial, the scenario’s orchestration was designed with the intent of clearly delineating different stages of BAPI transactions in the same way as a functional test would do, i.e., with each stage being verifiable. So in the scenario, a specified number of BAPI transactions requests is received before being committed, and the status of these transactions is verified afterwards by using another BAPI transaction, this time sent as regular SAP Remote Function Call (RFC).
The orchestration stages are, in chronological order:
- Get the transaction count from a file location.
- Send BAPI transactions to the SAP server based on the transaction details received from another file location (BAPI transaction requests).
- After the expected number of transactions has been processed, perform the expected outcome (COMMIT/ROLLBACK) specified in the last received transaction request. The decision for using the last request was for simplification and to show how to create the COMMIT/ROLLBACK messages on the fly rather than receiving them.
- Get the transaction status (i.e., committed or not committed) from the SAP server for each transaction of Step 2.
For instance, if the transaction count in Step 1 is 2, the orchestration will wait until it has received the two BAPI transaction requests from a file location, and then use the second (last) request’s “IsCommit” Boolean element to determine whether both transactions should be committed. |
Note: In a full-fledged application, the rollback path would be well-suited in the catch exception block of a scope shape. For the sake of simplicity, the current scenario lets the input determine the rollback outcome, as in orchestration code path verification.
The diagram below shows the four stages, which are presented in detail in the next sections. The send-receive port is shown to the left (LOBPort). The “SaveResponse” port to the right-hand side of the diagram is a FILE-transport send port for saving the received messages along the way.
At each stage of the orchestration, the messages received at the receive locations are saved for verification purposes. These interleaved send shapes are also useful to address the compile time error “in a sequential convoy the ports must be identical” which would otherwise happen given that the orchestration implements a sequential convoy with different receive ports.
Note: In the remainder of this article, inbound messages containing the orders to be processed by the orchestration are referred to as “BAPI transaction requests“. On the other hand, BAPI transactions refer to outbound messages sent to the SAP server after the BAPI transaction request is transformed to one of the schemas downloaded from SAP in visual studio (CREATEFROMDAT2, GETSTATUS, BAPI_TRANSACTION_COMMIT, BAPI_TRANSACTION_ROLLBACK). |
Stage 1: Scenario Parameters
The orchestration is activated by a message that contains the number of transactions to expect. A correlation set ensures that related incoming messages will be processed by the same orchestration sequentially. Correlations are documented in correlations walkthrough. In our scenario, the correlation set is based on a field called “CommonId“, defined in a property schema and used as a field for all incoming schemas. The figure below illustrates how the CommonId field is used in the orchestration.
Stage 2: Receive and process BAPI transaction requests
Once the transaction count is known, the orchestration starts receiving the BAPI transaction requests. A request looks like:
<ns0:Orders xmlns:ns0="http://BAPISend.MultipleOrders">
<Order>
<ORDER_HEADER_IN>
<DOC_TYPE>TA</DOC_TYPE>
<SALES_ORG>1000</SALES_ORG>
<DISTR_CHAN>12</DISTR_CHAN>
</ORDER_HEADER_IN>
<ORDER_ITEMS_IN>
<MATERIAL>DPC1020</MATERIAL>
</ORDER_ITEMS_IN>
<ORDER_PARTNERS>
<PARTN_ROLE>AG</PARTN_ROLE>
<PARTN_NUMB>0000001012</PARTN_NUMB>
</ORDER_PARTNERS>
</Order>
<isCommit>true</isCommit>
<CommonId>CommonId_0</CommonId>
</ns0:Orders>
The value of isCommit is saved in a variable for use in the next stage. Then, the Orders document is mapped to a BUS2032.CREATEFROMDAT2 document representing the BAPI transaction to execute on the SAP server.
A variable BAPIConnectionState keeps track of the connection state property to use when sending the CREATEFROMDAT2. The first message should have the value “OPEN”, and subsequent ones “REUSE”, as explained in Run BAPI Transactions in SAP using BizTalk Server.
The SAP server responds to a BAPI transaction request with a document id (the CREATEFROMDAT2Response.SALESDOCUMENT field), which is saved locally in a list variable SalesDocumentIDs of type defined in a helper library, and for use in Stage 4.
The SALESDOCUMENT value is extracted from the SAP response message BAPIResponse, and saved with the following expression shape:
SalesDocumentIDs.Add(((System.String)xpath(BAPIResponse,
"string(/*[local-name()='CREATEFROMDAT2Response' and namespace-uri()='http://Microsoft.LobServices.Sap/2007/03/Bapi/BUS2032/']/*[local-name()='SALESDOCUMENT' and namespace-uri()='http://Microsoft.LobServices.Sap/2007/03/Bapi/BUS2032/']/text())")));
The steps are illustrated numbered below.
Stage 3: Commit or Rollback
After the expected number of requests has been reached, the isCommit field of the last request is used to determine whether the series of BAPI transactions (LUW on the SAP server side) should be committed or rolled back. The messages sent to the SAP server are constructed with the following expressions:
CommitXML = new System.Xml.XmlDocument();
CommitXML.LoadXml(@"<ns0:BAPI_TRANSACTION_COMMIT xmlns:ns0=""http://Microsoft.LobServices.Sap/2007/03/Bapi/BUS2032/""><ns0:WAIT>X</ns0:WAIT></ns0:BAPI_TRANSACTION_COMMIT>");
BAPICommitRequest = CommitXML;
BAPICommitRequest(Microsoft.Adapters.SAP.BiztalkPropertySchema.ConnectionState) = "CLOSE";
and
RollbackXML = new System.Xml.XmlDocument();
RollbackXML.LoadXml(@"<ns0:BAPI_TRANSACTION_ROLLBACK xmlns:ns0=""http://Microsoft.LobServices.Sap/2007/03/Bapi/BUS2032/""></ns0:BAPI_TRANSACTION_ROLLBACK>");
BAPIRollbackRequest = RollbackXML;
BAPIRollbackRequest(Microsoft.Adapters.SAP.BiztalkPropertySchema.ConnectionState) = "ABORT";
Stage 3 is shown below:
Stage 4: Check status for each transaction
GETSTATUS messages are sent to the SAP server for each document id saved in Stage 2, but without the connection state context property as there is no need to bind the messages to a transaction context. The messages can be created on the fly, from an XML document, as in the expression:
GETSTATUSxml = new System.Xml.XmlDocument();
GETSTATUSxml.LoadXml(@"<ns0:GETSTATUS xmlns:ns0=""http://Microsoft.LobServices.Sap/2007/03/Bapi/BUS2032/""><ns0:SALESDOCUMENT>"+ SalesDocumentIDs.Get(currentIndex)"</ns0:SALESDOCUMENT><ns0:STATUSINFO><ns1:BAPISDSTAT xmlns:ns1="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"><ns1:DOC_NUMBER></ns1:DOC_NUMBER><ns1:DOC_DATE></ns1:DOC_DATE><ns1:PURCH_NO></ns1:PURCH_NO><ns1:PRC_STAT_H></ns1:PRC_STAT_H><ns1:DLV_STAT_H></ns1:DLV_STAT_H><ns1:REQ_DATE_H></ns1:REQ_DATE_H><ns1:DLV_BLOCK></ns1:DLV_BLOCK><ns1:ITM_NUMBER></ns1:ITM_NUMBER><ns1:MATERIAL></ns1:MATERIAL><ns1:SHORT_TEXT></ns1:SHORT_TEXT><ns1:REQ_DATE></ns1:REQ_DATE><ns1:REQ_QTY></ns1:REQ_QTY><ns1:CUM_CF_QTY></ns1:CUM_CF_QTY><ns1:SALES_UNIT></ns1:SALES_UNIT><ns1:NET_VALUE></ns1:NET_VALUE><ns1:CURRENCY></ns1:CURRENCY><ns1:NET_PRICE></ns1:NET_PRICE><ns1:COND_P_UNT></ns1:COND_P_UNT><ns1:COND_UNIT></ns1:COND_UNIT><ns1:DLV_STAT_I></ns1:DLV_STAT_I><ns1:DELIV_NUMB></ns1:DELIV_NUMB><ns1:DELIV_ITEM></ns1:DELIV_ITEM><ns1:DELIV_DATE></ns1:DELIV_DATE><ns1:DLV_QTY></ns1:DLV_QTY><ns1:REF_QTY></ns1:REF_QTY><ns1:S_UNIT_ISO></ns1:S_UNIT_ISO><ns1:CD_UNT_ISO></ns1:CD_UNT_ISO><ns1:CURR_ISO></ns1:CURR_ISO><ns1:MATERIAL_EXTERNAL></ns1:MATERIAL_EXTERNAL><ns1:MATERIAL_GUID></ns1:MATERIAL_GUID><ns1:MATERIAL_VERSION></ns1:MATERIAL_VERSION><ns1:PO_ITM_NO></ns1:PO_ITM_NO><ns1:CREATION_DATE></ns1:CREATION_DATE><ns1:CREATION_TIME></ns1:CREATION_TIME><ns1:S_UNIT_DLV></ns1:S_UNIT_DLV><ns1:DLV_UNIT_ISO></ns1:DLV_UNIT_ISO><ns1:REA_FOR_RE></ns1:REA_FOR_RE><ns1:PURCH_NO_C></ns1:PURCH_NO_C></ns1:BAPISDSTAT></ns0:STATUSINFO></ns0:GETSTATUS>");
BAPIGetStatus = GETSTATUSxml;
Instead, a more concise way is to use the utility Xsd.exe to generate a C# class representing the BUS2032 object schema, and add the generated code to a helper library. So the helper code will look like:
public partial class GETSTATUS
{
private string sALESDOCUMENTField;
private BAPISDSTAT[] sTATUSINFOField;
…
A variable called GETSTATUSObject has the GETSTATUS type:
The BAPIGetStatus message is created with the following expression, which is much simpler than the LoadXml one above:
GETSTATUSObject.SALESDOCUMENT = SalesDocumentIDs.Get(currentIndex); // The ids that were saved in Stage 2 in SalesDocumentIDs
GETSTATUSObject.STATUSINFO = new SapBAPITxClient.BAPISDSTAT[1]; // Must be allocated for receiving the response data from SAP
BAPIGetStatus = GETSTATUSObject;
Stage 4 is outlined below.
For each BAPI transaction, the server response depends on whether the BAPI transaction was committed. The following table shows the GETSTATUSResponses received from the server, which are saved locally by using the SaveStatusSendPort port with FILE transport type.
Transaction was committed successfully | Transaction was not committed |
|
|
Concluding Remarks
The tutorial presented here expands upon the previous documentation by showing step-by-step the processing of multiple BAPI transactions that are part of the same context on the SAP server. Each received message corresponds to exactly one BAPI transaction request. The next logical step is to consider the case of multiple BAPI transactions requests per message, and this will be presented in another blog post about BAPIs.
Sample Code
All code used in this article is attached.
When building the BizTalk solution, the following warnings can be safely ignored:
…BAPIOrchestration.odx(892,27): warning X4014: convoy processing will not occur — check your protocol if you were expecting it
…BAPIOrchestration.odx(874,22): convoy found at ‘activate receive(TransactionInfoIn.Operation_1, RequestsInfo, initialize Correlation_1)’
…BAPIOrchestration.odx(892,27): and ‘receive(FileIn.Transaction, SendToAdapter, Correlation_1)’
References
Run BAPI Transactions in SAP using BizTalk Server
Message Schemas for BAPI Operations
Using Correlations in Orchestrations
Registry setting to enable BAPI transactions
Get Metadata for SAP Operations in Visual Studio
Walkthrough: Correlations in BizTalk Orchestration
Browse, search, and retrieve metadata from SAP for BAPI operations
Brought to you by Dr. Ware, Microsoft Office 365 Silver Partner, Charleston SC.
Recent Comments