Enterprise Resource Planning Blogs by SAP
Get insights and updates about cloud ERP and RISE with SAP, SAP S/4HANA and SAP S/4HANA Cloud, and more enterprise management capabilities with SAP blog posts.
Showing results for 
Search instead for 
Did you mean: 
Currently, our approach towards automating business processes in SAP S/4HANA mainly revolves around making use of the following implementations:

  • ODATA/SOAP API Calls – Primary Approach

  • Screen-Scraping – Secondary Approach (In case, the primary approach is not feasible)

However, there exists an alternate solution with relatively more feasibility than the Screen-Scraping approach, using BAPI Calls.

In simpler terms, BAPIs can be compared to ODATA APIs in terms of providing a service for performing operations such as Create, Read, Update, Delete pertaining to a business process.

Each BAPI function module contains detailed documentation that is accessible through the Function Module Documentation in SE37.

Note: While we have ODATA API Calls readily available for SAP S/4HANA On-Premise 1809 and 1909, for systems prior to the aforementioned versions like SAP S/4HANA On-Premise 1709, The current primary approach is the Screen-Scraping method which can prove to be quite cumbersome.
Therefore, The implementation of BAPI Calls reduces the performance as well as development overhead that is incurred in Screen-Scraping.

Most BAPIs are remote enabled, meaning they can be invoked via Remote Function Call (RFC), web services, or SAP's Java connector.

We will understand the implementation of each step taking the Create Simple Purchase Requisition from Excel bot as reference. Therefore, It is advisable to have the source code readily accessible to better understand the steps mentioned below.

Following are the steps to implement BAPI in SAP S/4HANA On-Premise systems:

  1. Establishing a connection with the SAP back-end system: The basic technology we use to connect to the SAP backend system is SAP ActiveX component. Here is a comprehensive list of ActiveX control that can be used. This bot will demonstrate the use of “Function Control” specifically.
    function getBAPI(sc) {
    try {
    var BAPI = new ActiveXObject("SAP.Functions");
    var Connection = BAPI.Connection;
    Connection.ApplicationServer = sc.localData.applicationServer;
    if (sc.localData.systemNumber) {
    Connection.SystemNumber = sc.localData.systemNumber;
    Connection.Client = sc.localData.clientNumber;
    Connection.User = sc.localData.credentials.username;
    Connection.Password = sc.localData.credentials.password;
    Connection.logon(0, true);
    return BAPI;
    }catch (ex) {
    sc.data.messages.error.push("description : " + ex.description);
    sc.localData.terminateProcessing = 1;

    The st_Fetch_Data step fetches the variables from the factory and stores the same variable values in the sc.localData object, then processing the input excel file. Once the variables are successfully fetched, The getBAPI() method is used to establish the connection with the SAP back-end system using the ActiveXObject() method. The connection requires several parameters like "ApplicationServer", "SystemNumber" (Optional), "Client", "User" and "Password" to successfully establish the connection.

  2. Adding the required BAPI Function module:Once the connection is successfully established, We need to add the required BAPI Function module.
    var prCreate = BAPI.Add(sc.localData.bapiCall.PR_CREATE);

    The value of sc.localData.bapiCall.PR_CREATE is "BAPI_PR_CREATE". 

  3. Reading or Writing data using imports, exports, and tables: Once we go through the Function Module Documentation, We will be able to understand the parameters that are to be exported, the tables that can be fetched and manipulated, and the parameters that can be imported.
    //Insert header row with values

    var docObj = sc.localData.prDocuments[docNumCounter];
    var header = prCreate.exports(sc.localData.bapiCall.PR_HEADER);
    header(sc.localData.mapping.header.PurchaseRequisitionType) = docObj[sc.localData.payloadValues.prType];
    //Insert headerX row with values
    var headerX = prCreate.exports(sc.localData.payloadValues.prHeaderX);
    headerX(sc.localData.mapping.header.PurchaseRequisitionType) = sc.localData.payloadValues.x;
    var itemObj = docObj[itemNumCounter];
    var tableContent = [];
    var tableContentX = [];
    var rowContent = [];
    var rowContentX = [];
    var rowCount = 0;
    var errorFlag = 0;
    //Insert item row with values
    for (var itemObjContentKey in itemObj) {
    var itemObjContent = itemObj[itemObjContentKey];
    // Insert PR Item row with values
    var sheetNameToTableMapping = sc.localData.sheetMapping[itemObjContentKey];
    tableContent[count] = prCreate.tables(sheetNameToTableMapping);
    rowContent[rowCount] = tableContent[count].Rows.Add;
    rowContent[rowCount](sc.localData.mapping.header.ItemNumber) = convertItemNumber(itemNumCounter);
    if (sheetNameToTableMapping != sc.localData.payloadValues.prAddrDelivery && sheetNameToTableMapping != sc.localData.payloadValues.prItemText) {
    tableContentX[count] = prCreate.tables(sheetNameToTableMapping + sc.localData.payloadValues.x);
    rowContentX[rowCount] = tableContentX[count].Rows.Add;
    rowContentX[rowCount](sc.localData.mapping.header.ItemNumber) = convertItemNumber(itemNumCounter);

    It is important for us to note that for the export("PRHEADER"), there is a corresponding export("PRHEADERX") which is used to flag only the parameters, the values of which are to be updated. Similarly for each corresponding Table as well, There may exist a TableX which is only used as a flag table to update the values against which the value "X" is updated.

    The data from the excel has been structured as an object on the basis of the SheetNames which correspond to the Table names and accordingly the data is mapped to the Table rows from the corresponding Excel Sheet rows. Then each sheet object is traversed on the basis of their key values which correspond to the Internal PR number counter in the Input file.

    Once all the necessary rows have been added to the tables, We will call prCreate.Call() to make the BAPI Call.

    The hard-coded values above are all stored in a config.json file present in the local folder.

    Following are some of the values used in the code above :

    "bapiCall": {
    "WAIT" : "WAIT"
    "excelToBapiMapping": {
    "header" : {
    "PurchaseRequisitionType" : "PR_TYPE",
    "ItemNumber": "PREQ_ITEM"
    "payloadValues" : {
    "x" : "X",
    "prHeaderX" : "PRHEADERX",
    "prType" : "PR_Type",
    "header" : "Header",

  4. Call ‘BAPI_TRANSACTION_COMMIT’ or ‘BAPI_TRANSACTION_ROLLBACK’: Once we invoke the Call() function, We can import the value of "PRHEADEREXP" to check if the PR got successfully created or not. There are multiple ways of verifying the same, We can check the length of the value, which will always be of a fixed length, the value of which can be found in the documentation. The "RETURN" table contains all the messages corresponding to the BAPI Call. We can traverse through the table by making use of enumerators as showing in the code below. If the check for the successful creation of PR is passed, then we need to make the "BAPI_TRANSACTION_COMMIT" Call for the values to be successfully written in the tables, the same of which will reflect in the EBAN table. Similarly, An else condition can be added for the "BAPI_TRANSACTION_ROLLBACK" which ensures that the buffer is cleared.
    var prNumber = prCreate.imports(sc.localData.payloadValues.prHeaderExp);
    var result = prCreate.tables(sc.localData.payloadValues.returnVal);​
    sc.localData.output[docNumCounter] = [];
    //Iterate through the tables parameter
    for (var enumerator = new Enumerator(result.Rows); !enumerator.atEnd(); enumerator.moveNext()) {
    var returnTableRow = enumerator.item();
    if (prNumber(sc.localData.payloadValues.pReqNo).length == 10) { sc.localData.output[docNumCounter].push([returnTableRow(sc.localData.payloadValues.type), returnTableRow(sc.localData.payloadValues.message)]); rootData.messages.info.push(sc.localData.messageCode[returnTableRow(sc.localData.payloadValues.type)] + " : " + returnTableRow(sc.localData.payloadValues.message));
    //Commit transaction only if PR has been successfully created
    var commit = BAPI.Add(sc.localData.bapiCall.TRANSACTION_COMMIT);
    var wait = commit.exports(sc.localData.bapiCall.WAIT);
    wait.value = sc.localData.payloadValues.x;
    } else {
    sc.localData.output[docNumCounter].push([returnTableRow(sc.localData.payloadValues.type), returnTableRow(sc.localData.payloadValues.message)]); rootData.messages.error.push(sc.localData.messageCode[returnTableRow(sc.localData.payloadValues.type)] + " : " + returnTableRow(sc.localData.payloadValues.message));


Once we have an understanding of the basic steps of implementation used in the reference bot 48M, We can leverage the usage of BAPI Calls to automate different business processes on S/4HANA On-Premise 1709 and prior systems.

Please feel free to post your comments. Also, if you have come across any issues related to the implementation of Create Simple Purchase Requisition from Excel using BAPI, do share your questions and concerns.

I would like to thank vijay.g for his blog on calling BAPI using SAP Intelligent RPA which helped in the implementation of a proper business use case like Create Simple Purchase Requisition.

More Information about the SAP S/4HANA template bots, check out the following links:

SAP Best Practices Explorer: SAP Best Practices for SAP Intelligent Robotic Process Automation Integration with SAP S/4HANA

SAP Intelligent RPA store:  here

Release Note: 2788986 – Release Strategy for SAP Intelligent Robotic Process Automation Store for SAP S/4HANA

Stay tuned to know more about the SAP S/4HANA template bots.