Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
Showing results for 
Search instead for 
Did you mean: 

Disclaimer: This blog post is only applicable for the SAP Cloud SDK version of at most 2.19.2. With version 3.0.0 and later this feature is discontinued, so there will be no updated tutorial. Feel free to check out our other Tutorials on the SAP Cloud SDK.

The following steps will explain how to use the SAP 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 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.

! Note ! In general, SAP S/4HANA Cloud exposes APIs in the form of OData and SOAP services. In addition, you can access selected BAPIs explicitly mentioned in the overview table below using the SAP Cloud SDK. BAPIs in SAP S/4HANA Cloud are otherwise not accessible from applications on SAP Cloud Platform, except for the selected BAPIs mentioned below. If you want to submit an improvement request for additional APIs (in the form of OData services), kindly refer to the customer influence program “SAP S/4HANA Cloud Integration”.

We will 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 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 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. In case of SAP S/4HANA Cloud, only selected BAPIs are accessible as described below.

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 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 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 to your application class path. That's how you are able to use the BAPI VDM from the start. You will find the service interfaces and default implementation classes in the following Java package:

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 DefaultFinancialTransactionService class.
final ErpConfigContext erpConfigContext = ...
return new DefaultFinancialTransactionService().getList().execute(erpConfigContext).getListOfSelectedTransactions();

Here, DefaultFinancialTransactionService is the implementation class of the interface FinancialTransactionService representing the 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 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. Instantiate Business object service class (or use provided instance, for example with dependency injection)

  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.

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

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

final CostCenterCreateInput costCenterInput = CostCenterCreateInput
.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"

List<ReturnParameter> messages = new DefaultCostCenterService()
.createMultiple(controllingArea, costCenterInput)

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 java.util.List;

import javax.annotation.Nonnull;

import org.joda.time.LocalDate;


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); = id;
this.description = description;

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

final CostCenterCreateInput costCenterInput = CostCenterCreateInput
.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"

return new DefaultCostCenterService()
.createMultiple(controllingArea, costCenterInput)

This can already be used in your own CostCenter showcase project. All it takes is an additional servlet HTTP method definition inside with doPost(...) similar to doGet(...):
@WebServlet( "/costcenters" )
public class CostCenterServlet extends HttpServlet

protected void doPost( final HttpServletRequest request, final HttpServletResponse response )
throws ServletException,
final ErpConfigContext configContext = new ErpConfigContext();

final List<ReturnParameter> result = new CreateCostCenterCommand(

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

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 Cloud SDK

final ErpEndpoint erpEndpoint = ...

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

.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("CURRENCY", "WAERS", "EUR")
.field("COSTCTR_HIER_GRP", "KHINR", "0001")
.field("COMP_CODE", "BUKRS", "1010")
.field("PROFIT_CTR", "PRCTR", "YB101")


return query.execute( erpEndpoint )


BAPI VDM query with SAP Cloud SDK

final ErpConfigContext erpConfigContext = ...
final ControllingArea controllingArea = ControllingArea.of("A000");

final CostCenterCreateInput costCenterInput = CostCenterCreateInput
.validFrom(new LocalDate())
.validTo(new LocalDate().plusYears(1))

return new DefaultCostCenterService()
.createMultiple(controllingArea, costCenterInput)
.execute( erpConfigContext )

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.


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

  • Java interfaces for each BAPI
    - to allow dependency injection and easy mocking of SAP S/4HANA during testing


Available VDM classes for BAPI

There is a limited set of VDM interfaces available for BAPI. The SAP Cloud SDK features all BAPIs from communication arrangement 0180, which is part of the finance domain: "SAP Cloud Platform - Financials Integration (SAP_COM_0180)".

For each interface from the following overview, there is a default implementation class with the name of the interface prefixed with Default.

Business Object Service Interface BAPI Method
AccountingActivityAllocationService check
AccountingDocumentService check
AccountingManualCostAllocationService check
AccountingPrimaryCostsService check
CostCenterService createMultiple
FixedAssetService change
ForeignExchangeOptionService change
ForeignExchangeService createSwap
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.

See the next tutorial in the series here: Step 12 with SAP Cloud SDK: Logging with SAP Cloud SDK.


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.
    ], 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();"Successfully created new cost center!");
    .fail(function () {"Failed to create cost center!");

    onInit: function () {
    var view = this.getView();
    url: "/costcenters",
    headers: {"X-CSRF-Token":"Fetch"}
    .done(function (data, status, jqXHR) {
    var csrfToken = jqXHR.getResponseHeader("X-CSRF-Token");
    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="" xmlns:mvc="sap.ui.core.mvc"
    displayBlock="true" xmlns="sap.m">
    <Page title="Cost Center Explorer">

    <!-- Add input to create new cost centers -->
    <HBox width="500px" justifyContent="SpaceAround" alignItems="End" id="__hbox1">
    <Label text="Cost Center ID" />
    <Input width="100%" id="inputCostCenterId" />
    <Label text="Cost Center Long Text" />
    <Input width="100%" id="inputCostCenterDescription" />
    <Button text="Create" width="80px" id="__button0" press="createCostCenter" />

    <!-- Add this between the content tags -->
    <List headerText="Cost Centers"
    items="{costCenter>/}" >
    description="{costCenter>costCenterID}" />


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



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?