Skip to Content

The following steps will explain how to use the SAP S/4HANA Cloud SDK’s Virtual Data Model (VDM) to simplify the communication with your SAP S/4HANA System.

Note: This post is part of a series. For a complete overview visit the SAP S/4HANA Cloud SDK Overview.

Goal of this blog post

In this blog post we will take a look at how BAPI works for reading and writing data from an SAP S/4HANA system, especially when OData is not yet available but BAPI endpoints are. You will understand how to use the Virtual Data Model (VDM), why it is the recommended way and where to find more details about it.

We will also advance the example application from Step 5: Resilience to demonstrate the usage of the BAPI VDM inside your SAP S/4HANA Cloud application on SAP Cloud Platform. You will be able to create new cost centers with underlying BAPI operations.

If you want to follow this tutorial, we highly recommend checking out these previous tutorials:

For a complete overview visit the SAP S/4HANA Cloud SDK Overview.

Note: This tutorial requires access to an SAP ERP system and an activated communication scenario 0180 from the finance domain – “SAP Cloud Platform – Financials Integration (SAP_COM_0180)“. The activation of this scenario will be covered very soon in an upcoming blog post.

 

Virtual Data Model

The data stored in an S/4HANA system is inherently complexly structured and therefore difficult to query manually. Therefore, SAP S/4HANA Cloud SDK introduces a Virtual Data Model (VDM) that aims to abstract from this complexity and provide data in a semantically meaningful and easy to consume way.

The preferred way to consume data from an S/4HANA system is via the OData protocol, see Step 10: Virtual Data Model for OData. But when OData is not yet provided in a suitable way for specific operations, then often enough BAPIs present a useful, already implemented alternative to establishing ERP interactivity. Although considered as bridge technology, you can use BAPIs as a functional second choice.

Since the official SAP strategy is to gradually replace BAPI with OData, an established BAPI operation may be discontinued at some point in the future. For this compatibility issue, SAP S/4HANA Cloud SDK has a developer oriented solution: a BAPI Virtual Data Model. With the enabled data model library, BAPI function calls – and BAPI complexity in general – can be hidden from the developer. This allows, to easily update, replace or migrate internal functionality with newer SAP system versions, without having the software architect bother about compatibility. The benefits are outstanding. But before we dive into the example source code about cost center creation, let’s briefly introduce BAPI first.

 

Introduction to BAPI

A BAPI (Business Application Programming Interface) is a programming interface that enables external access to business processes and data in the ERP system. BAPIs are defined by methods, given by the Business Object Repository. They offer an object-oriented view of business components in the system.

You may also know BAPIs from the convenient BAPI Explorer in your SAP GUI. Just use the transaction code “BAPI“, and the program starts:

Here you can browse the BAPIs of your SAP system, in a hierarchical order:

  • Business Object
  • BAPI
  • Parameters / Description / Remote function details

In order to examine BAPI details, like implementation, parameter types and tables, you can take a look into the function module itself. By double clicking on the name, the SAP GUI opens an elaborate overview window with all of its details.

The following information about function modules is provided:

  • General properties
  • Function parameters: Import, Export, ChangingTables
  • Exception
  • Sources

To correctly operate a single BAPI, you need to have some knowledge about its structure and field constraints. Of course the details about parameters can be looked up, but any mistake will result in an error response. Now, to lower the risk of making mistakes during development, the SAP S/4HANA Cloud SDK provides the VDM for BAPI. By using the relevant Java classes you effectively operate on prepared BAPI function calls, which guarantees to produce valid queries. By using this library, time and resources will be saved during your software developing process.

 

Virtual Data Model: Queries to BAPI

Try it out, by working with one of our archetypes; it automatically adds the Maven dependency s4hana-all:s4hana-all to your application class path. That’s how you are able to use the BAPI VDM from the start. You will find the service classes in the following Java package:

import com.sap.cloud.sdk.s4hana.datamodel.bapi.services.*;

Next, before we continue with the showcase project of the CostCenter, we’ll have a look at a short code sample, the FinancialTransactionService.

Sample: “getList” by FinancialTransactionService

The easiest way to get started, is to check and run the BAPI VDM by using some very basic BAPI function. An example function, which does not invoke changes or require any mandatory parameter, can be called with the BAPI method getList() from the FinancialTransactionService class.

final ErpConfigContext erpConfigContext = ...
return FinancialTransactionService.getList().execute(erpConfigContext).getListOfSelectedTransactions();

Here, FinancialTransactionService is an accessible Business Object in your S/4HANA system, by applying the published getList() function module. Since no further mandatory parameters are needed, the function call can be prepared just like this. The execution itself happens with the execute method. Internally, the SAP S/4HANA Cloud SDK serializes the function query to SOAP for HTTP destinations on CloudEdition or to JCo for RFC destinations on OnPremise systems. Connection, authentication, processing and deserialization is done automatically. Only a reference to an ERP configuration context is required. This variable is usually provided by your application. Here, we decide to read the list of selected transactions as result.

The general API usage stays the same for all BAPI VDM queries [optional]:

  1. Business object service class
  2. BAPI name [mandatory parameters]
  3. [parameters]
  4. execute( ErpConfigContext )
  5. [function query result]

Showcase: “createMultiple” by CostCenterService

In order to extend the CostCenter showcase project, let’s take advantage of the BAPI VDM. It allows us to easily create new cost center items.

import com.sap.cloud.sdk.s4hana.datamodel.bapi.types.*;

final ErpConfigContext erpConfigContext = ...
final String id = ...
final String description = ...

final ControllingArea controllingArea = ControllingArea.of("A000"); // ERP type "CACCD"

final CostCenterCreateInput costCenterInput = CostCenterCreateInput
        .builder()
        .validFrom(new LocalDate())
        .validTo(new LocalDate().plusYears(1))
        .costcenter(CostCenter.of(id))
        .name(id)
        .descript(description)
        .currency(CurrencyKey.of("EUR"))                    // ERP type "WAERS"
        .costcenterType(IndicatorForCostCenterType.of("E")) // ERP type "KOSAR"
        .personInCharge(CostCenterManager.of("USER"))       // ERP type "VERAK"
        .costctrHierGrp(SetId.of("0001"))                   // ERP type "SETNR"
        .compCode(CompanyCode.of("1010"))                   // ERP type "BUKRS"
        .profitCtr(ProfitCenter.of("YB101"))                // ERP type "PRCTR"
        .build();

List<ReturnParameter> messages = CostCenterService
        .createMultiple(controllingArea, costCenterInput)
        .execute(erpConfigContext)
        .getMessages();

To start at the beginning, let’s assume you have an ERP configuration context, like previously stated. Also consider a cost center id and description as user input. Although, as you can see here, the VDM powered source code is already descriptive, let’s roughly summarize the control flow:

  • Instantiation of Controlling Area. This will later be used as first mandatory BAPI parameter. This class wraps a primitive String type. It automatically applies formatting and basic validation rules inherent to domain types in the SAP system.
  • Instantiation of CostCenter CreateInput. This will be used as second mandatory BAPI parameter. This class wraps a custom set of available values for the function call. By using the builder pattern you can easily instantiate your desired object, while keeping type safety. Such classes, which provide a set of values, are inherent to structure types in the SAP system.
  • Call to CostCenterService, to prepare a createMultiple BAPI remote function module call. The mandatory values are provided. With no further customization, execute the query. We decide to retrieve the default BAPI result messages, to check for notifications from the SAP system, like internal error messages.

Please note, the values for BAPI parameters may look different for your S/4HANA system. To help you finding the correct values, the domain types are commented above.

 

Use the VDM for your resilient BAPI call

You can simply wrap the previous VDM request into an ErpCommand, making it resilient like in Step 5: Resilience with Hystrix. Just like in the blog posts before, we create a new class and take ErpConfigContext as constructor parameter, as well as user provided variables:

import lombok.NonNull;
import org.joda.time.LocalDate;
import com.sap.cloud.sdk.s4hana.datamodel.bapi.types.*;

public class CreateCostCenterCommand extends ErpCommand<List<ReturnParameter>>
{
    private final String id;
    private final String description;

    public CreateCostCenterCommand(
            @NonNull final ErpConfigContext configContext,
            @NonNull final String id,
            @NonNull final String description )
    {
        super(CreateCostCenterCommand.class, configContext);
        this.id = id;
        this.description = description;
    }

    @Override
    protected List<ReturnParameter> run() throws Exception {
        final ControllingArea controllingArea = ControllingArea.of("A000"); // ERP type "CACCD"

        final CostCenterCreateInput costCenterInput = CostCenterCreateInput
                .builder()
                .costcenter(CostCenter.of(id))
                .name(id)
                .descript(description)
                .validFrom(new LocalDate())
                .validTo(new LocalDate().plusYears(1))
                .currency(CurrencyKey.of("EUR"))                    // ERP type "WAERS"
                .costcenterType(IndicatorForCostCenterType.of("E")) // ERP type "KOSAR"
                .personInCharge(CostCenterManager.of("USER"))       // ERP type "VERAK"
                .costctrHierGrp(SetId.of("0001"))                   // ERP type "SETNR"
                .compCode(CompanyCode.of("1010"))                   // ERP type "BUKRS"
                .profitCtr(ProfitCenter.of("YB101"))                // ERP type "PRCTR"
                .build();

        return CostCenterService
                .createMultiple(controllingArea, costCenterInput)
                .execute(getConfigContext())
                .getMessages();
    }
}

This can already be used in your own CostCenter showcase project. All it takes is an additional servlet HTTP method definition inside CostCenterServlet.java with doPost(...) similar to doGet(...):

@WebServlet( "/costcenters" )
public class CostCenterServlet extends HttpServlet
{
    ...

    @Override
    protected void doPost( final HttpServletRequest request, final HttpServletResponse response )
            throws ServletException,
            IOException
    {
        final SapClient sapClient = new SapClient("SAPCLIENT-NUMBER"); // adjust SAP client to your respective S/4HANA system
        final ErpConfigContext configContext = new ErpConfigContext(ErpDestination.getDefaultName(), sapClient);

        final List<ReturnParameter> result = new CreateCostCenterCommand(
                configContext,
                request.getParameter("id"),
                request.getParameter("description")
        ).execute();

        // reset cached get results
        new GetCachedCostCentersCommand(configContext).getCache().invalidateAll();

        response.setContentType("application/json");
        response.getWriter().write(new Gson().toJson(result));
    }
}

Now you are able to easily create CostCenters with predefined values by having a simple HTTP post request. All it takes are user provided values for id and description. Already, our backend work is done!

 

Comparison to the generic way of implementing a BAPI requests

The whole cost center creation could have been realized without the help of BAPI VDM, but then we wouldn’t have been able to take advantage of the very useful comfort features. To visualize the impressive difference, let’s take a look into the direct comparison.

Generic BAPI query approach with SAP S/4HANA SDK
final ErpEndpoint erpEndpoint = ...

final BapiQuery query = new BapiQuery("BAPI_COSTCENTER_CREATEMULTIPLE")
        .withExporting("CONTROLLINGAREA", "KOKRS", "A000");

query.withTable("COSTCENTERLIST", "BAPI0012_CCINPUTLIST")
    .row()
    .field("COSTCENTER", "KOSTL", id)
    .field("NAME", "KTEXT", id)
    .field("DESCRIPT", "KLTXT", description)
    .field("VALID_FROM", "DATAB", new LocalDate())
    .field("VALID_TO", "DATBI", new LocalDate().plusYears(1))
    .field("COSTCENTER_TYPE", "KOSAR", "E")
    .field("CURRENCY", "WAERS", "EUR")
    .field("PERSON_IN_CHARGE", "VERAK", "USER")
    .field("COSTCTR_HIER_GRP", "KHINR", "0001")
    .field("COMP_CODE", "BUKRS", "1010")
    .field("PROFIT_CTR", "PRCTR", "YB101")
    .end();

query.withTableAsReturn("BAPIRET2");

return query.execute( erpEndpoint )
        .get("RETURN")
        .getAsCollection()
        .asList(ReturnParameter.class);

 

BAPI VDM query with SAP S/4HANA SDK
final ErpConfigContext erpConfigContext = ...
final ControllingArea controllingArea = ControllingArea.of("A000");

final CostCenterCreateInput costCenterInput = CostCenterCreateInput
        .builder()
        .costcenter(CostCenter.of(id))
        .name(id)
        .descript(description)
        .validFrom(new LocalDate())
        .validTo(new LocalDate().plusYears(1))
        .currency(CurrencyKey.of("EUR"))
        .costcenterType(IndicatorForCostCenterType.of("E"))
        .personInCharge(CostCenterManager.of("USER"))
        .costctrHierGrp(SetId.of("0001"))
        .compCode(CompanyCode.of("1010"))
        .profitCtr(ProfitCenter.of("YB101"))
        .build();

return CostCenterService
        .createMultiple(controllingArea, costCenterInput)
        .execute( erpConfigContext )
        .getMessages();

The differences appear quite obvious. Without the VDM a developer depends on expert knowledge about SAP table and function schematics. Not only are the definite table-, column- and parameter-names required, but you will also need to provide the data type name to each of them. And these String values are very cryptic and difficult to maintain. Fortunately we have the virtual data model.

Advantages

For the developer

  • Descriptive language naming for java classes, methods and fields (English)
    – to maintain code and logic
  • Javadoc on all VDM functions
    – to look up details and usage
  • Fluent programming and code completion
    – to allow rapid development
  • Implicit ERP types
    – to hide BAPI protocol complexity
  • Deserialized BAPI function query result data
    – to process information without parsing manually
  • Separation of required and optional parameters
    – to prevent error cases
  • Predefined values for individual types
    – to improve programming comfort
  • Java type safety for primitives
    – to strengthen API reliability and code quality

 

Available VDM classes for BAPI

Currently there is a limited set of VDM classes available for BAPI. For the early release we feature all BAPIs from communication arrangement 0180, which is part of the finance domain: “SAP Cloud Platform – Financials Integration (SAP_COM_0180)“. In the future we will expand the selection to additional scenarios.

 

Business Object Service Class BAPI Method
AccountingActivityAllocationService check
post
AccountingDocumentService check
post
AccountingManualCostAllocationService check
post
AccountingPrimaryCostsService check
post
CostCenterService createMultiple
FixedAssetService change
createFromData1
getList
ForeignExchangeOptionService change
create
getDetail
ForeignExchangeService createSwap
dealChange
dealCreate
dealGet
FinancialTransactionService getList

 

Please keep in mind, even with enabled VDM most BAPI methods still require some knowledge about the SAP S/4HANA system. You will need to know what values to fill in which parameters. That’s why depending on the BAPI, the effort may differ.

 

Appendix: Required Changes to UI5 Frontend

In case you have already followed the instructions from blog post Step 9: Implement and Deploy a Frontend Application, you can simply advance your application to also run the BAPI function calls, we have established here. What we only want to change, are two existing files to make the cost center creation work as expected:

  • controller/View1.controller.js
  • view/View1.view.xml

 

  1. Edit the controller/View1.controller.js to add the createCostCenter action. Also please notice the additional MessageToast inclusion in the controller header. This allows us to display popup messages.
    sap.ui.define([
        "sap/ui/core/mvc/Controller",
        "sap/ui/model/json/JSONModel",
        "sap/m/MessageToast"
    ], function(Controller, JSONModel, MessageToast) {
        "use strict";
        return Controller.extend("blog-tutorial.controller.View1", {
    
            createCostCenter: function () {
                var that = this;
    
                var costCenterData = {
                    id : this.getView().byId("inputCostCenterId").getValue(),
                    description : this.getView().byId("inputCostCenterDescription").getValue()
                };
    
                return jQuery.ajax({
                        type: "POST",
                        url: "/costcenters",
                        data: costCenterData
                    })
                    .then(function (costCenters) {
                        that.onInit();
                        MessageToast.show("Successfully created new cost center!");
                    })
                    .fail(function () {
                        MessageToast.show("Failed to create cost center!");
                    });
            },
    
    
            onInit: function () {
                var view = this.getView();
                jQuery.get({
                        url: "/costcenters",
                        headers: {"X-CSRF-Token":"Fetch"}
                    })
                    .done(function (data, status, jqXHR) {
                        var csrfToken = jqXHR.getResponseHeader("X-CSRF-Token");
                        $.ajaxSetup({
                            headers: {"X-CSRF-Token": csrfToken}
                        });
                        var model = new JSONModel(data);
                        view.setModel(model, "costCenter");
                    });
            }
        });
    });
    

     

  2. Edit the view/View1.view.xml to add two input fields and a submit button:
    <mvc:View controllerName="blog-tutorial.controller.View1" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc"
    	displayBlock="true" xmlns="sap.m">
    	<App>
    		<pages>
    			<Page title="Cost Center Explorer">
    				<content>
    
    					<!-- Add input to create new cost centers -->
    					<HBox width="500px" justifyContent="SpaceAround" alignItems="End" id="__hbox1">
    						<VBox>
    							<Label text="Cost Center ID" />
    							<Input width="100%" id="inputCostCenterId" />
    						</VBox>
    						<VBox>
    							<Label text="Cost Center Long Text" />
    							<Input width="100%" id="inputCostCenterDescription" />
    						</VBox>
    						<Button text="Create" width="80px" id="__button0" press="createCostCenter" />
    					</HBox>
    					
    					<!-- Add this between the content tags -->
    					<List headerText="Cost Centers"
                            items="{costCenter>/}" >
                            <StandardListItem
                                title="{costCenter>costCenterDescription}"
                                description="{costCenter>costCenterID}" />
                        </List>
    				</content>
    			</Page>
    		</pages>
    	</App>
    </mvc:View>

     

  3. You’re done! It’s time to compile, package and deploy the application.

 

Troubleshooting

If the BAPI connection fails, please follow the checklist:

  • Is a network connection possible?
    Please check with your browser or any other custom HTTP query testing tool. Check whether a proxy is in use. Or overly strict firewall settings are in place.
  • Are you using the correct BAPI destination type?
    For SAP S/4HANA OnPremise systems your configured destination variable should be of type “RFC” instead of “HTTP” (Cloud Edition).
  • Are you using the correct ERP destination?
    When not specified, the default is used: ErpQueryEndpoint
    Is a destination with this name configured?
  • Are you using the correct ERP credentials?
    Does the user have access to the BAPI?

 

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply