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:


 


Article1ConsumeAdapterService.jpg


 


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:


 



  1. Get the transaction count from a file location.

  2. Send BAPI transactions to the SAP server based on the transaction details received from another file location (BAPI transaction requests).

  3. 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.

  4. 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.


 


BVTOrchStages2.jpg


 


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.


 


Article1CorrelationId.jpg


 


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.


 


OrderToBAPISalesOrder.JPG


 


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.


 


Article1OrderIdsVariable.jpg


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.


 


Stage2.jpg


 


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:


 


Stage3.jpg


 


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:


GETSTATUSObject.png


 


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.


Stage4.jpg


 


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


<?xml version="1.0" encoding="utf-8"?>
<GETSTATUSResponse xmlns="http://Microsoft.LobServices.Sap/2007/03/Bapi/BUS2032/">
    <RETURN>
        <TYPE xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"></TYPE>
        <CODE xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"></CODE>
        <MESSAGE xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"></MESSAGE>
        <LOG_NO xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"></LOG_NO>
        <LOG_MSG_NO xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/">0</LOG_MSG_NO>
        <MESSAGE_V1 xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"></MESSAGE_V1>
        <MESSAGE_V2 xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"></MESSAGE_V2>
        <MESSAGE_V3 xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"></MESSAGE_V3>
        <MESSAGE_V4 xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"></MESSAGE_V4>
    </RETURN>
    <STATUSINFO>
        <BAPISDSTAT xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/">
            <DOC_NUMBER>0000002466</DOC_NUMBER>...</GETSTATUSResponse>

<?xml version="1.0" encoding="utf-8"?>
<GETSTATUSResponse xmlns="http://Microsoft.LobServices.Sap/2007/03/Bapi/BUS2032/">
    <RETURN>
        <TYPE xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/">E</TYPE>
        <CODE xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/">V4211</CODE>
        <MESSAGE xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/">
            Data was not found for the document
        </MESSAGE>
        <LOG_NO xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"></LOG_NO>
        <LOG_MSG_NO xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/">0</LOG_MSG_NO>
        <MESSAGE_V1 xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"></MESSAGE_V1>
        <MESSAGE_V2 xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"></MESSAGE_V2>
        <MESSAGE_V3 xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"></MESSAGE_V3>
        <MESSAGE_V4 xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"></MESSAGE_V4>
    </RETURN>
    <STATUSINFO></STATUSINFO>
</GETSTATUSResponse>


 


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


 


Operations on BAPIs in SAP


Run BAPI Transactions in SAP using BizTalk Server


Message Schemas for BAPI Operations


SAP Adapter documentation


Using Correlations in Orchestrations


Working with Convoy Scenarios


Xsd.exe utility


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.