Skip to main content

Calling SAP Function Modules

Using SAP Remote Function Calls (RFC) type function modules is an easy way to retrieve data from SAP ABAP systems for use in Checks and also to perform actions and update data for use in Automations.

SAP provides a library of standard, released function modules called SAP Business Application Programming Interfaces, or BAPIs for short, to access and manipulate standard SAP data however your SAP ABAP development team will be able to create custom function modules that you can also call.

note

Calling SAP Function Modules and access to the sap global object in particular is only available if the script is running for a system type of SAP System or SAP Instance.

Code Walkthrough

The object RFCJCOFunction is the key object that represents a function module in SAP and provides the ability to set it's parameters, to execute it and to retrieve the results.

Getting a function module object

There are two ways to create an RFCJCOFunction:

  1. By using the rfcConnection global object and calling the method getFunction(..) (which uses the default client) e.g.
const myFunction = rfcConnection.getFunction('Z_TEST')
  1. By using the sap global object to specify a client using the login(..) method before the getFunction(..) call. e.g.
const myFunction = sap.login("100").getFunction('Z_TEST')
tip

You can use the method getClients() to retrieve a list of clients on the SAP system to use with login(..)

Setting parameters

SAP distinguishes between different types of parameters, Importing, Changing, Tables that can be used as inputs to a function module to control it's behavior. In Avantra JavaScript extensions, we can set all of these input parameters using the withParameter(..) or withParameters(..) methods on the RFCJCOFunction you have just retrieved.

For example:

const myFunction = rfcConnection.getFunction('Z_TEST')
.withParameter("IV_INPUT", "TEST")
.withParameter("CV_CHANGING", "TEST2")
or this:

const myFunction = rfcConnection.getFunction('Z_TEST')
.withParameters({
IV_INPUT: "TEST",
CV_CHANGING: "TEST2"
})

To set structure like parameters or tables of structures, Javascript maps and arrays are used.

const myFunction = rfcConnection.getFunction('Z_TEST')
.withParameters({
// Structured importing parameter
IS_INPUT: {
FIELD1: "TEST",
FIELD2: 21.11,
FIELD3: "X"
},
// Tables parameter T_TAB with 2 records
T_TAB: [
// Row 1
{
VALUE1: "TEST"
// ...
},
// ROW 2
{
VALUE1: "TEST2"
}
]
})
tip

Although parameters on function module in ABAP are case-insensitive, when using these functions in JavaScript you must use uppercase parameter names and field names in structures.

SAP RFC Parameter Types

In creating SAP RFCs, the importing, exporting, changing and tables parameters are defined using SAP types called data elements or domains. These contain information about their use (names, descriptions etc.) but more importantly for us, these also refer to a specific built in data type that defines the type of data expected.

When passing values from a script to the SAP API, it is important to know the data type of these parameters to pass the value in the correct format.

Here we've listed all the possible data types and therefore all the different types of data you can pass to an SAP RFC from a script in Avantra:

const rfcInputs = {
INT2_FIELD: 2, // or "2" (as a string)
ACCP_FIELD: "202202", // for 2nd period of 2022
TIMS_FIELD: "11:33:21",
CLNT_FIELD: "001",
DEC_FIELD: 22.121,
CURR_FIELD: 27.5,
INT1_FIELD: 1, // or "1" (as a string)
UNIT_FIELD: "EA",
FLTP_FIELD: 23.4413212321,
INT4_FIELD: 4, // or "4" (as a string)
RAW_FIELD: [10, 82, 57, -49, -32, 106, 30, -25, -118, -99, 26, 121, -86, 94, 8, 106],
QUAN_FIELD: 100.123,
CHAR_FIELD: "character field",
PREC_FIELD: 123, // or "123" (as a string)
DATS_FIELD: "20220224", // for 24th February 2022
LANG_FIELD: "E",
CUKY_FIELD: "GBP",
NUMC_FIELD: "0123"
}

Executing and checking the results

Once the parameters are set, call execute( ) to run the function module on SAP.

Once a function module is executed, the return values from SAP, any Exporting, Changing and Tables parameters, are available through the result(..) and results( ) method calls.

For example:

myFunction.execute();

const error1 = myFunction.result("EV_ERROR");
// or
const error2 = myFunction.results().EV_ERROR;

If the function module returns structures or tables these are available as Javascript objects and arrays respectively. For example:

// Retrieves the table parameter T_OUT_TAB
const { T_OUT_TAB: myResults } = myFunction.results();

// Print out the message component for each line in the results table
myResults.forEach(result => console.log(result.message));

Handling exceptions

Sometimes a RFC can throw an exception. This is reported by default as an error in Avantra, either the automation step will not be successful or the check will have an error associated with it.

Within your JavaScript coding, you can react to these exceptions and give your users a more meaningful error message or change the logic executed to handle this situation differently. For example, write a single check that supports multiple SAP versions where some function modules or behaviors have changed in later versions.

For example:

 try {
// Check if the function exists
// - This throws an exception if the function doesn't exist
const fExists = rfcConnection.getFunction("FUNCTION_EXISTS")
.withParameter("FUNCNAME", "ZAVANTRA_CHECK123")
.execute();

// If we are here, this function exists, we can call it
const check = rfcConnection.getFunction("ZAVANTRA_CHECK123")
.withParameter("SOME_PARAM", "VALUE")
.execute();

// Process the result...
check.status = OK;

} catch (e) {
const exName = e.getMessage(); // ABAP exception name = FUNCTION_NOT_EXIST

if (exName === "FUNCTION_NOT_EXIST") {
check.status = DISABLED;
check.message = "Check is disabled because function is not present";
} else {
check.status = CRITICAL;
check.message = "An unknown error occurred: " + exName;
}
}

Known Limitations of RFC Calls

Internally, Avantra uses the SAP Java Connector (SAP JCo) library to execute function modules on SAP. These function modules need to be marked as a "Remote-Enabled Module" in the Attributes tab of the function module definition.

When creating RFCs on SAP, it is important to take note of SAP's limitations for this code. These can be found in the SAP NetWeaver documentation for your ABAP version. (for example SAP Help Documentation: RFC - Restrictions)

Additionally, prior to 21.11, a legacy API existed for calling function modules on SAP. This didn't hide the complexities of the SAP JCo library from JavaScript and was difficult to use. This legacy API still exists for backward compatibility of your scripts but we strongly recommend you migrate to this new API to simplify your check coding.

Examples

Example: Call an RFC function to count the number of work processes

This example simply calls RFC function module TH_WPINFO, evaluates the number of work processes, and displays them in a table. It also uses custom monitoring parameters and outputs custom performance data.

// prepare result table
check.addTableColumn("WP_INDEX");
check.addTableColumn("WP_PID");
check.addTableColumn("WP_ITYPE");
check.addTableColumn("WP_TYP");


// execute the function
const func = rfcConnection
.getFunction("TH_WPINFO")
.execute();


// read WPLIST table
const wps = func.result("WPLIST")

wps.forEach(wp => {
check.newRow();
check.addCell(wp.WP_INDEX);
check.addCell(wp.WP_PID);
check.addCell(wp.WP_ITYPE);
check.addCell(wp.WP_TYP);
});

// make sure that custom monitoring parameter type cust.WorkProcessesWarnCount
// is created with a default value
const wpWarnLimit = Number(monitoredSystem.getMoniParam("cust.WorkProcessesWarnCount"));
const wpCount = wps.length;

if (wpWarnLimit && wpCount >= wpWarnLimit) {
check.status = WARNING;
check.message = wpCount + " work processes found (more than limit of " + wpWarnLimit + ")";
} else {
check.status = OK;
check.message = wpCount + " work processes found";
}

// make sure custom performance counter Work Processes is created
performance.writePerformanceValue("Work Processes", wpCount);

Example: Loop over clients and check if ABAP user SAP* user is intentionally locked

This example supplements the built-in security-related check with another common use case: to check if the ABAP user SAP* is locked in all clients. Please note that for this example to work, the Avantra RFC user needs to be defined in all clients with the same user and password as the one specified in the SAP system properties. This example also uses Javascript functions to modularize code and prevent repetitive coding.

// setup check result table
check.addTableColumn("User");
check.addTableColumn("Client");
check.addTableColumn("Groups");
check.addTableColumn("Wrong Logons");
check.addTableColumn("Locally Locked");
check.addTableColumn("Globally Locked");
check.addTableColumn("No User or Password");


let userCnt = 0;
let clientCnt = 0;
let errors = "";

const users = [ "SAP*" ];

// "sap" is the central object. It is hosted by the Avantra Agent and simply
// available in JavaScript.

// "sap.clients" is an array of clients of this SAP system
sap.clients.forEach(client => {
users.forEach(username => {
try {
// sap.login() does a login to the client given in the Avantra
// properties of the SAP system and uses the defined
// username and password
// sap.login(client) uses the defined username and password for
// a given client
const func = sap.login(client)
.getFunction("BAPI_USER_GET_DETAIL")
.withParameter("USERNAME", username)
.execute();

check.newRow();
check.addCell(username);
check.addCell(client);

const {
GROUPS: groups,
ISLOCKED: {
WRNG_LOGON: wrongLogonsSAP,
LOCAL_LOCK: localLockSAP,
GLOB_LOCK: globalLockSAP,
NO_USER_PW: noPasswordSAP
}
} = func.results();

// Comma seperated list of groups:
check.addCell(groups.map(group => group.USERGROUP).join(", "))

// Locked due to wrong number of logins
check.addCell(lockedCode2HumanReadable(wrongLogonsSAP).text);

// Locked locally
const localLock = lockedCode2HumanReadable(localLockSAP);
check.addCell(localLock.text, localLock.locked ? OK : CRITICAL);

// Locked globally
const globalLock = lockedCode2HumanReadable(globalLockSAP);
if (globalLock.locked) {
// user is globally locked
check.addCell(globalLock.text, OK);

} else if (!localLock.locked) {
// if not yet locked, mark this field CRITICAL as well
check.addCell(globalLock.text, CRITICAL);

} else {
// if already locked locally, mark this one as neutral
check.addCell(globalLock.text);
}

// Doesn't exist or no password
check.addCell(lockedCode2HumanReadable(noPasswordSAP).text);

if (!localLock.locked && !globalLock.locked) {
errors += `User ${username} is not intentionally locked in client ${client}.\n`;
}

// Increment user count
userCnt++;
} catch (e) {
console.log(e.stack);
errors += e + ";";
}
});

// Increment client count
clientCnt++;
});

// write result status and message
check.status = OK;
let msg = userCnt + " user(s) in " + clientCnt +" client(s) tested.";
if (errors !== "") {
msg += "\n" + errors;
check.status = CRITICAL;
} else {
msg += "\n" + "All users are locked."
}

check.message = msg;



// Utility function to interpret SAP response
function lockedCode2HumanReadable(lockCode) {
let text = "";
let locked = false;

switch (lockCode) {
case "L":
text = "Locked";
locked = true;
break;
case "U":
text = "Unlocked";
break;
case "":
text = "User does not exist";
locked = true;
break;
default:
}

return {
text,
locked
}
}

API Reference

Global Variable rfcConnection

Global variable rfcConnection is an instance of RFCHost.

Use this variable directly in your scripts, for example:

const result = rfcConnection.getFunction(...);

See RFCHost for the full API.


Global Variable sap

Global variable sap is an instance of SAPHost.

Use this variable directly in your scripts, for example:

const result = sap.login(...);

See SAPHost for the full API.


Class RFCHost

This object provides quick access to the default client of the SAP system in order to call remote function modules (RFCs) and is accessed through global variable rfcConnection. Use method getFunction with the function name to then configure and execute an RFC.

Alternatively use global variable sap to create an instance of this object logged into a different SAP client.

bapiXmiLogin

bapiXmiLogin(interfaceName: string, version: string): boolean

Make an xmi login in order to execute xmi calls which require a login to the given interface. This function executes the RFC function BAPI_XMI_LOGON to login to the given interface.

rfcConnection.bapiXmiLogin("XBP", "3.0");
// your RFC calls here
rfcConnection.bapiXmiLogoff("XBP");
tip

Please note that Avantra is passed for extcompany and Avantra Agent for extproduct.

Since: 23.0.4

Parameters

  • interfaceName (string) - the name of the XMI interface
  • version (string) - the version of the interface to use

Returns: boolean - true if the login to the xmi interface was successful, false if not


bapiXmiLogin

bapiXmiLogin(extcompany: string, extproduct: string, interfaceName: string, version: string): boolean

Make an xmi login in order to execute xmi calls which require a login to the given interface. This function executes the RFC function BAPI_XMI_LOGON to login to the given interface.

rfcConnection.bapiXmiLogin("your_company_name", "your_product_name", "XBP", "3.0");
// your RFC calls here
rfcConnection.bapiXmiLogoff("XBP");

Since: 23.0.4

Parameters

  • extcompany (string) - extcompany attribute for BAPI_XMI_LOGON
  • extproduct (string) - extproduct attribute for BAPI_XMI_LOGON
  • interfaceName (string) - the name of the XMI interface
  • version (string) - the version of the interface to use

Returns: boolean - true if the login to the xmi interface was successful, false if not


bapiXmiLogoff

bapiXmiLogoff(interfaceName: string): void

Logoff from a specific xmi session that function bapiXmiLogin was used to login to.

Since: 23.0.4

Parameters

  • interfaceName (string) - the name of the XMI interface

beginContext

beginContext(): void

Begins a stateful call sequence for calls on the given destination.

tip

This call is equivalent to the JCoContext call: JCoContext.begin(JCoDestination destination);

Since: 23.0.4


endContext

endContext(): void

Ends a stateful call sequence for calls on the specified destination.

tip

This call is equivalent to the JCoContext call: JCoContext.end(JCoDestination destination);

Since: 23.0.4


getFunction

getFunction(rfcFunctionName: string): RFCJCOFunction

Create a reference to an RFC function module call to allow it to be configured and then executed on the SAP system.

Parameters

  • rfcFunctionName (string) - the name of the function module to call

Returns: RFCJCOFunction - the RFC function module object to manipulate parameters and execute


Class RFCJCOFunction

The RFCJCOFunction class represents an SAP Remote Function Call (RFC) that can be executed on an SAP system. There are methods to set parameters (withParameters), methods to run the function (execute) and methods to get the return values (results).

Please see the method documentation for examples of using this API.

execute

execute(): RFCJCOFunction

Call SAP to execute the function module given the parameters passed to the withParameter and withParameters methods.

This method call is synchronous and the results of executing the function can be accessed by the result and results methods.

Since: 21.11

Returns: RFCJCOFunction - this function object to allow for method chaining


result

result(parameterName: string): any

Return the named outputted parameter value that is passed to this method.

SAP function modules can return values from Exporting, Changing and Table parameter lists and this method will return the matching parameter value from any of these lists.

Also consider using the results method to return all values if you need to handle all results in your program.

const func = rfcConnection.getFunction('Z_TEST_FM')
// Supply any parameters needed here
.execute();

const myValue = func.result("EV_VALUE");
// Where EV_VALUE is an exporting parameter on function module Z_TEST_FM

const changingValue = func.result("CV_VALUE");
// Where CV_VALUE is a changing parameter on function module Z_TEST_FM

const table = func.result("T_VALUE")
// where T_VALUE is a table parameter on function module Z_TEST_FM
table.length; // Provides number of rows
table[0].COLUMN; // Retrieves 1st records' value of field COLUMN

const { COLUMN: aValue } = func.result("ES_STRUCT");
// where ES_STRUCT is an exporting structure of function module Z_TEST_FM
// aValue is then the same value as `func.result("ES_STRUCT").COLUMN`

Since: 21.11

Parameters

  • parameterName (string) - parameter name from result of the function module call

Returns: any - the value of the specified parameter


results

results(): any

Return all the outputted parameter values from the execution of the function module.

SAP function modules can return values from Exporting, Changing and Table parameter lists and this method will return all values to the caller for further processing.

If there are many results that are not needed by the calling program, it might be more performant to use the result method to individually return specific parameter values.

const func = rfcConnection.getFunction('Z_TEST_FM')
// Supply any parameters needed here
.execute();

const allResults = func.results();
// Returns all the 'outputted' values from executing a function module (exporting, changing & table parameters)

const changingValue = allResults.CV_VALUE; // Same as func.getResult("CV_VALUE")
// Where CV_VALUE is a changing parameter on function module Z_TEST_FM

// The benefit of func.results() is to utilise Javascript destructuring, e.g.
const { CV_VALUE: changingValue, T_VALUE: tableValue } = func.results()
// where T_VALUE is a table parameter on function module Z_TEST_FM
// This gives you the same as func.result("CV_VALUE") and func.result("T_VALUE") in one line

table.length; // Provides number of rows
table[0].COLUMN; // Retrieves 1st records' value of field COLUMN

Since: 21.11

Returns: object - the value of all result parameters (exporting, changing, tables)


withParameter

withParameter(parameterName: string, value: any): RFCJCOFunction

Set a value for a specific parameter individually that is used to call the function module in execute.

SAP function modules accept Import, Changing and Tables parameters as input to control their logic. This method allows you to supply any of those parameters to the function module. An exception is thrown if the parameter doesn't exist on any of the parameter lists.

const func = rfcConnection.getFunction('Z_TEST_FM')
// Primitive domain type importing parameter
.withParameter("IV_PARAM", "TEST")
// Flat table type importing parameter
.withParameter("IT_PARAM", [
"ITEM 1",
"ITEM 2"
])
// Table of structured importing parameter
.withParameter("IT_PARAM_T", [
{
COLUMN_1: "VALUE"
}
])
// Structured changing parameter
.withParameter("CS_INPUT", {
FIELD1: "TEST",
FIELD2: 21.11,
FIELD3: "X"
}),
// Tables parameter T_TAB with 2 records
.withParameter("T_TAB", [
// Row 1
{
VALUE1: "TEST",
// ... other fields
},
// ROW 2
{
VALUE1: "TEST2",
// other fields
}
]);

Multiple calls to this method are allowed to set different parameter values, or alternatively you can use withParameters to supply a map of different parameter values to set all at once. Multiple calls to the set the same parameter value simply overwrite the previous value, for example multiple calls to set a table parameter will only result in the value of the last call.

Since: 21.11

Parameters

  • parameterName (string) - the name of the parameter to set
  • value (any) - the value of the parameter

Returns: RFCJCOFunction - this function object to allow for method chaining


withParameters

withParameters(parameters: object): RFCJCOFunction

Set the value of one or multiple parameter values that are used to call the function module in execute.

SAP function modules accept Importing, Changing and Tables parameters as input to control their logic. This method allows you to supply any of those parameters to the function module. An exception is thrown if the parameter doesn't exist on any of the parameter lists.

The example below is not exhaustive of the different combinations but you can see you can set importing, changing and tables parameters with one call and the value of these parameters are read from corresponding Javascript objects. Note that they are always capitalized.

const func = rfcConnection.getFunction('Z_TEST_FM')
.withParameters({
// Primitive domain type importing parameter
IV_PARAM: "TEST",
// Flat table type importing parameter
IT_PARAM: [
"ITEM 1",
"ITEM 2"
],
// Table of structured importing parameter
IT_PARAM_T: [
{
COLUMN_1: "VALUE"
}
],
// Structured changing parameter
CS_INPUT: {
FIELD1: "TEST",
FIELD2: 21.11,
FIELD3: "X"
},
// Tables parameter T_TAB with 2 records
T_TAB: [
// Row 1
{
VALUE1: "TEST",
// ... other fields
},
// ROW 2
{
VALUE1: "TEST2",
// other fields
}
]
});

Multiple calls to this method are allowed to set different parameter values, or alternatively you can use withParameter to supply parameters individually. Multiple calls to the set the same parameter value simply overwrite the previous value, for example multiple calls to set a table parameter will only result in the value of the last call.

Since: 21.11

Parameters

  • parameters (object) - the parameters to set to call the function module

Returns: RFCJCOFunction - this function object to allow for method chaining


Class SAPHost

The SAPHost class represents an SAP system and provides methods for listing and logging into different SAP clients on that system. Method login returns an RFCHost object that allows remote function calls (RFCs) on SAP to be called to provide additional data for checks or enable automation processes.

getClients

getClients(): string[]

Retrieve an array of clients in the system, for example [ "000", "001", "100" ].

Use this, along with the login method, to call client specific function modules on the SAP ABAP system.

// Call a function module (Z_TEST_FM) on each client in the system
sap.clients.forEach(client => {
const func = sap.login(client).getFunction('Z_TEST_FM')
// Supply any parameters needed here
.execute();

// Do something with the function module results
});
tip

To use this pattern, the Avantra user must exist in each client with the same password and sufficient authorisations to execute this function module.

Returns: string[] - the list of clients in the SAP system

See Also: clients


getSid

getSid(): string

The SID of the ABAP system this check/automation runs on.

Since: 23.1

Returns: string - the SID of the ABAP system

See Also: sid


isRemote

isRemote(): boolean

Check if system is remote.

Since: 24.3

Returns: boolean - true if system is remote

See Also: remote


isSapControlAvailable

isSapControlAvailable(): boolean

Check if sap control is available.

Since: 24.3

Returns: boolean - true if sap control is available

See Also: sapControlAvailable


login

login(client: string): RFCHost

Connects to a non-default client (specified by the parameter) in order to call function modules on it.

const func = sap.login("100").getFunction('Z_TEST_FM')
// Supply any parameters needed here
.execute();

// Do something with the function module results
tip

To use this pattern, the Avantra user must exist in the target client with the same password and sufficient authorisations to execute this function module.

Parameters

  • client (string) - client to login to

Returns: RFCHost - an instance of RFCHost to call RFC function modules on


loginWith

loginWith(credentials: any): RFCHost

Uses the passed in credentials to connect to the SAP system in order to call function modules on it. The passed in credentials can system defined credentials or credentials which are passed in as input parameters to automation steps.

const func = sap.loginWith("namespace.userx").getFunction('Z_TEST_FM')
// Supply any parameters needed here
.execute();

// Do something with the function module results

Since: 21.11.6

Parameters

  • credentials (any) - passed in credentials

Returns: RFCHost - an instance of RFCHost to call RFC function modules on


clients

readonly clients: string[]

Retrieve an array of clients in the system, for example [ "000", "001", "100" ].

Use this, along with the login method, to call client specific function modules on the SAP ABAP system.

// Call a function module (Z_TEST_FM) on each client in the system
sap.clients.forEach(client => {
const func = sap.login(client).getFunction('Z_TEST_FM')
// Supply any parameters needed here
.execute();

// Do something with the function module results
});
tip

To use this pattern, the Avantra user must exist in each client with the same password and sufficient authorisations to execute this function module.

Accessors

Type: string[]

Modifiers: readonly


remote

readonly remote: boolean

Check if system is remote.

Accessors

Type: boolean

Modifiers: readonly

Since: 24.3


sapControlAvailable

readonly sapControlAvailable: boolean

Check if sap control is available.

Accessors

Type: boolean

Modifiers: readonly

Since: 24.3


sid

readonly sid: string

The SID of the ABAP system this check/automation runs on.

Accessors

Type: string

Modifiers: readonly

Since: 23.1