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.
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 |
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,
-
By using the rfcConnection global object and calling the method getFunction(..) (which uses the default client) e.g.
const myFunction = rfcConnection.getFunction('Z_TEST')
-
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')
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 behaviour. 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"
}
]
})
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 behaviours 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 https://help.sap.com/doc/abapdocu_755_index_htm/7.55/en-US/abenrfc_limitations.htm)
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
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);
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 modularise 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 to call methods of this class directly in your code, e.g
const myValue = rfcConnection.getFunction(..);
See RFCHost for further documentation on these methods.
Global Variable sap
Global variable sap
is an instance of SAPHost.
Use this variable to call methods of this class directly in your code, e.g
const myValue = sap.login(..);
See SAPHost for further documentation on these methods.
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");
Please note that |
- Since
-
23.0.4
- Parameters
-
-
interfaceName
: stringthe name of the XMI interface
-
version
: stringthe 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
: stringextcompany attribute for BAPI_XMI_LOGON
-
extproduct
: stringextproduct attribute for BAPI_XMI_LOGON
-
interfaceName
: stringthe name of the XMI interface
-
version
: stringthe 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
: stringthe name of the XMI interface
-
beginContext( )
beginContext(): void
Begins a stateful call sequence for calls on the given destination.
This call is equivalent to the JCoContext call: |
- Since
-
23.0.4
endContext( )
endContext(): void
Ends a stateful call sequence for calls on the specified destination.
This call is equivalent to the JCoContext call: |
- 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
: stringthe name of the function module to call
-
- Returns
-
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
-
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
: stringparameter name from result of the function module call
-
- Returns
-
any
the value of the specified parameter
results( )
results(): object
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
: stringthe name of the parameter to set
-
value
: anythe value of the parameter
-
- Returns
-
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
: objectthe parameters to set to call the function module
-
- Returns
-
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
});
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
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
isRemote( )
isRemote(): boolean
Check if system is remote.
- Since
-
24.3
- Returns
-
boolean
true if system is remote
- See Also
isSapControlAvailable( )
isSapControlAvailable(): boolean
Check if sap control is available.
- Since
-
24.3
- Returns
-
boolean
true if sap control is available
- See Also
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
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
: stringclient to login to
-
- Returns
-
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
: anypassed in credentials
-
- Returns
-
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
});
To use this pattern, the Avantra user must exist in each client with the same password and sufficient authorisations to execute this function module. |
- See Also
- Type
-
string[]
- Modifiers
-
Readonly
remote
readonly remote: boolean
Check if system is remote.
- See Also
- Type
-
boolean
- Modifiers
-
Readonly
- Since
-
24.3
sapControlAvailable
readonly sapControlAvailable: boolean
Check if sap control is available.
- See Also
- Type
-
boolean
- Modifiers
-
Readonly
- Since
-
24.3