Calling BAPIs and Function Modules

About Function Modules and BAPIs

Function Modules are the primary method of interfacing with SAP (ABAP). With Rfc Connector, you can call any RFC-enabled function module which exists in the SAP system.

BAPIs are special function modules which are documented and released by SAP for external usage. You can get a list of available BAPIs in your system with the BAPI Explorer (transaction code BAPI).

For the following examples, we will use the BAPI_FLIGHT_GETLIST module, which is available in all SAP systems. It is part of the flight data model, which is provided by SAP for demonstration and development purposes. The flight sample data has to be generated once; if you don‘t get any data when calling BAPI_FLIGHT_GETLIST, run the report SAPBC_DATA_GENERATOR once to create sample entries.

Anatomy of a function module

Function modules can have the following type of parameters:

  • Incoming parameters are sent from the client to the SAP server. These are „write-only“, e.g. not changed by the server.
  • Exporting parameters are sent from the SAP server back to the client. They are read-only, e.g. changing Exporting parameters on the client has no effect.
  • Changing parameters are sent from the client to the SAP server, and sent back to the client afterwards. The server may change some or all parameters during the call.
  • Tables parameters are parameters which consist of multiple rows. They behave like Changing parameters, e.g. they are sent from the client to the server, and back.

You can inspect possible parameters and their type with the Function Builder (Transaction SE37).

Tip: In SAP GUI, you can jump to the definition of most programming objects (e.g. structures, tables and data types) by double-clicking them. This is called „forward navigation“.

ABAP data types

Each Importing, Exporting and Changing parameter can either be

  1. a scalar value (string, number, amount, …)
  2. a structure (value consisting of several fields, roughly equivalent to a dictionary in other programming languages)
  3. a table (an array of structures of the same type)

Tables parameters must alway be of type „table“.

All types in SAP are defined in the „data dictionary“ (DDIC), which contains metadata such as type, length, possible values etc.

Importing the function prototype

To import a function prototype, use session.ImportCall(). It will return a FunctionCall object, which you can use to read and write parameters:

FunctionCall fn = session.ImportCall("BAPI_FLIGHT_GETLIST", True);

Note: the second parameter, ignoreUnsupportedParams, is only relevant for RfcSession. It allows to ignore complex parameters (for example a table of tables) which are not supported by the transport layer. SoapSession and NwRfcSession support all possible data types, so the parameter is ignored in this case.

Setting outgoing parameters

// AIRLINE is a scalar parameter, use Importing["FIELDNAME"].value 
// to access
fn.Importing["AIRLINE"].value = "LH";

// DESTINATION_FROM is a struct parameter. You can access single 
// columns through the Fields property:
fn.Importing["DESTINATION_FROM"].Fields["AIRPORTID"].value = "SFO";

// DATE_RANGE is a table parameter. To write data into tables, 
// first add a row to the Rows property, then fill the Fields 
// of the appended row:
IRfcFields row = fn.Tables["DATE_RANGE"].AddRow();
row["SIGN"].value = "I";         // include all flights
row["OPTION"].value = "BT";      // between
row["LOW"].value = "20170101";   // first date
row["HIGH"].value = "20173112";  // last date

Calling the function

To call the function, use session.CallFunction():

try {
  session.CallFunction(fn, True);
} catch (COMException ex) {
  // something went wrong, check session.ErrorInfo to get details
}

Note: The second parameter to CallFunction determines whether the call will be executed synchronously or asynchronously. Synchronous calls will return only after the result has been received from SAP, whereas asynchronous calls will return immediately (the call is sent and received in a background thread). You can use asynchronous calls to keep the main thread alive (for example, to redraw or operate a message pump).

Retrieving incoming data

Since we made our call synchronous (blocking), we can process the result right here. If the call would have been asynchronous, we would have to either poll the CallActive property, or subscribe to the CallFinished-Event.

To read the rows of the FLIGHT_LIST table, we can simply enumerate the Rows property with foreach:

foreach (RfcFields row in fn.Tables["FLIGHT_LIST"].Rows) {
  Console.WriteLine(row["AIRLINE"].value + " " + row["FLIGHTDATE"].value);
}

Parameter conversion

Rfc Connector automatically converts values between SAP Types and VARIANT using the following conversion methods:

SAP Type VARIANT Typecode Description
CHAR, STRING VT_BSTR string
NUM, BCD VT_BSTR string consisting only of digits (0-9), decimal point(.) and minus sign(-)
BYTE, XSTRING VT_ARRAY|VT_UI1 array of bytes
FLOAT VT_R8 double
DATE VT_DATE date
TIME VT_TIME time

Working with SAP Dates and Times

SAP internally represents dates as 8-character strings (YYYYMMDD) and times as 6-character strings (HHMMSS), whereas VARIANT and .NET have a special DateTime type.

For data going to SAP, Rfc Connector accepts both formats, in addition to date strings using the current locale/culture (for example, 03/31/2010 in en-us culture). Data coming from SAP will automatically be converted into DateTime objects. For time values, only the Hour, Minute and Second are considered, whereas the other members of the DateTime object are undefined.

SAP also has a 15-character TIMESTAMP type (YYYYMMDDHHMMSS), which is rarely used and NOT converted by Rfc Connector in either direction (since it is not a real ABAP datatype, but merely a database convention)

' dates in Visual Basic
fn.Importing("DATE").value = "20100331" ' this is safe for all cultures
fn.Importing("DATE").value = "03/31/2010" ' not recommended, will cause error in non-US locale
fn.Importing("DATE").value = new DateTime(2010,03,31) ' safe for all cultures

Dim dt as DateTime
dt = fn.Exporting("DATE").value
year = dt.Year
// dates in C#
fn.Importing["DATE"].value = "20100331";   // safe for all cultures
fn.Importing["DATE"].value = "03/31/2010"; // error in non-US locale
fn.Importing["DATE"].value = new DateTime(2010,03,31); // safe for all cultures

DateTime dt = fn.Exporting["DATE"].value;

Commit and Rollback

BAPIs which create or change data (for example BAPI_SALESORDER_CREATEFROMDAT2), will not directly write to the database, instead a database transaction is used. This means that to persist the data, you have to call BAPI_TRANSACTION_COMMIT after calling BAPIs which change data.

You can call multiple BAPIs in a row and finally commit everything at once. However, if you do not call BAPI_TRANSACTION_COMMIT before disconnecting, the updated data will be discarded (rolled back).

You can find more information about the BAPI transaction model in the SAP Library.