SAP BAPI Transactions Walkthrough

This post has been republished via RSS; it originally appeared at: New blog articles in Microsoft Tech Community.

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 HKLM\SOFTWARE\Microsoft\BizTalk Server\3.0\Configuration\EnableBizTalkSapSessionProvider 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:

 

Article1ConsumeAdapterServiceRedux.png

 

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.

 

Article1OrchReduxS1.png

Article1OrchReduxS21.png

Article1OrchReduxS22.png

Article1OrchReduxS3.png

Article1OrchReduxS4.png

 

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.

 

Article1CorrelationIdRedux.png

 

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.

 

Article1OrchReduxS21Annotated.png

Article1OrchReduxS22Annotated.png

 

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";

 

 

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:

 

Article1OrchReduxS3Annotated.png

 

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.

 

Article1OrchReduxS4Annotated.png

 

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

<GETSTATUSResponse> <RETURN>...</RETURN> <STATUSINFO> <BAPISDSTAT> <DOC_NUMBER> 0000002466 </DOC_NUMBER> ... </GETSTATUSResponse> <GETSTATUSResponse> <RETURN> <MESSAGE> Data was not found for the document </MESSAGE> ... </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

REMEMBER: these articles are REPUBLISHED. Your best bet to get a reply is to follow the link at the top of the post to the ORIGINAL post! BUT you're more than welcome to start discussions here:

This site uses Akismet to reduce spam. Learn how your comment data is processed.