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 likeChanging
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
- a scalar value (string, number, amount, …)
- a structure (value consisting of several fields, roughly equivalent to a dictionary in other programming languages)
- 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.