Connector for SAP Business Suite - Developer's Manual part 3 - Integration scenario: Scripting

The Connector for SAP Business Suite provides various functions for Velocity scripts. The script API of the connector must be registered in order to this. Within the script API, the connector provides various functions, the "simpleRFC API" which can be used for calling BAPI/RFC functions in Velocity scripts. The main focus of the scripting function of the Connector for SAP Business Suite is on Groovy. In conjunction with the integration on SAP systems, Groovy can be used to call SAP BAPI/RFC functions. These can be used, for example, to generate or manipulate SAP data (e.g. orders). SAP processes (e.g. workflows) can be initiated with this functionality. A configured Connector for SAP Business Suite with an active View & Write or Developer API license is required to use the Groovy functionality.

1. Scenario overview - SAP sales order

Using the example of an SAP sales order, this document will demonstrate how a typical integration project should be carried out. SAP sales orders can be triggered by internet shops when a customer saves their order compiled as a backet, for example.

2. Requirements

In this scenario, it is assumed that an SAP R/3 System with a configured SD order recording is available. SAP R/3 is the leading system and defines the framework conditions. Conversely, this also means that a specific result - the SAP SD order - is expected in the SAP world. The sales order can be generated with SAP transactions using SAP-typical user interfaces (the SAPGUI). For the sales order, the orders would be recorded using transaction "VA01". If this is not the case, that is, if SAP is not yet able to implement the basic requirements using its own means, it is still too early for an integration scenario with Intrexx. Here, the functional prerequisites in SAP (an SAP implementation project, SAP Customizing) need to be created first. This is not part of this documentation but standard SAP business. The functional requirements for the SAP sales order can be formulated as follows for this example: The customer logs in to the Intrexx portal. They can record an order from the menu. There he compiles an order that is then sent to the SAP System. If this is successful, the order number needs to be send to the customer immediately after their submission. The order should be available in SAP R/3 as an SD sales order (transaction "VA01") without any manual intermediate steps. Put simply, this example scenario expects that

3. Process simulation in SAP

As a fully configured SAP system is assumed here, it needs to be possible to record an SAP sales order as if it the customer will receive it via an alternative interface. There, this should be simulated in SAP via SAP standard transactions. This is the transaction "VA01" for the SAP SD sales order.

Only identifying the most necessary entries for recording an order correctly has proven itself as wise. For the SAP SD sales order, this is the following information:

Information Example Source
Sales order type YTA Constant, predefined by the SAP side
Sales organization 1000 Constant, predefined by the SAP side
Distribution channel 10 Constant, predefined by the SAP side
Division 10 Constant, predefined by the SAP side
Sold-to party 10000 Context of the logged-in user, Customer number
Sales order number TEST0001 Customer entry, from the user interface
Sales order date 20.08.2008 Customer entry, from the user interface
Material B100000 Customer entry, from the user interface
Amount 10 Customer entry, from the user interface

Depending on the Customizing settings in the SAP system, additional information may also be required.

After the order is saved, a check is performed by the SAP department as to whether the order's content is correct and sufficient.

The latter should definitely already be checked at this stage. For example, the SAP order could be correct based on the configured SAP Customizing. Because a sales order is followed by processes such as delivery and invoicing, the entire processes needs to also be correctly handleable. Assessing this is the job of the respective department.

4. Analysis of available SAP BAPI functions

In the last step, the SAP standard transaction and the information required to simulate the required SAP process was determined. This step analyzes which functions SAP provides to the external systems in order to generate the same result - an identical-looking sales order. The SAP system contains a number of functions with API character that can and should be used by SAP itself and by external functions. In most cases, these are the so-called function modules. Some of these function modules can be used for external systems. These then have an additional property: they can be called remotely. These are referred to as Remote Function Calls (RFC for short), some of these have BusinessObject character. This means that they are specially flagged and represent object-oriented access to an SAP BusinessObject. SAP AG calls the total set of BusinessObjects and their access routines "Business API" (BAPI). The individual function module is also referred to here as a "BAPI". SAP recommends the Business API with all its BAPI function modules for external access to SAP objects and their processes. Every integration project should therefore go via the BAPI functions.

4.1. Transaction BAPI

In many cases, the SAP transaction "BAPI" helps identify the BAPI functions for the desired result. Here, you can research whether official BAPI functions are provided for the desired SAP object. You will find what you're looking for very quickly for the SD sales order example.

For the example of creating an SD sales order, the SAP BusinessObject "BUS2032" and the object method (the BAPI function module "BAPI_SALESORDER_CREATEFROMDAT2") are available.

4.2 Business Object Repository (BOR)

The Business Object Repository is primarily used for the SAP business workflow functionality. All of the SAP objects important for the SAP workflow have a defined BusinessObject here.

The provided search aids should help you find the BusinessObject you're looking for quickly.

Once you've found the BusinessObject, you can identify the usable BAPI functions very quickly in the methods. Click on the button to navigate directly to the corresponding function module.

4.3 Search for function modules callable per remote

If the previous steps proved unsuccessful, you have the ability to enter terms to search through all of the function modules callable per remote. The function module "RFC_FUNCTION_SEARCH" is well-suited for this and can be called via the transaction "SE37".

Using the entry parameters, you can search for *ORDER*, for example.

The results list then contains every function module that can be called by an external component - whether this fulfills the desired purpose of course needs to be checked.

4.4 Proxy function modules

If all previous attempts to achieve the desired result in the SAP system were unsuccessful, there is one final way: individual programming. To do this, you must create at least one function module shell, that is classified as RFC-enabled. The function module must pass calls on to other function modules that may not be flagged as remotely callable. The customer-specific RFC function module then only acts as a proxy. Alternatively, this function module can also call other functions available in ABAP (for example, subroutines) or generate and process batch input maps. The latter naturally requires extensive ABAP knowhow. Furthermore, it should be noted that this procedure is not recommended by SAP and therefore no support is provided for this. This form of implementation should be chosen if there are no alternatives and the customer is aware of the risk involved and can be held responsible for it. For reasons of simplification, it can sometimes be useful to use your own proxy function modules, although official BAPI function modules exist for the required requirement. Especially for external systems, the call is much clearer if only the absolutely required parameters have to be filled instead of the sometimes very extensive parameters. Proxy function modules can then link different BAPI calls and, if necessary, extend the parameters to SAP Customizing (for example, determine the correct sales organization in SAP).

5. Parameter concept

Once it is clear which function modules - preferably BAPI function modules - can be called remotely, an external call must be simulated. The system confirms whether the previously determined information is actually sufficient to generate the required SAP object. All function modules offer a test option via transaction "SE37", you can use this to check the functions and their parameters. Here, you can simulate the view of an external system. The parameters available in the function module interface must be populated in the same way as identified in the simulation.

The mimimum information from the previous steps must be entered into the corresponding parameter fields here. The test workbench via transaction "SE37" supports simple parameters, structures and table parameters. Documentation for the function module can be of great help for identifying the right fields.

Because entering the parameters can be a lot of work, these entries can be saved. You can access your entries again later via "Test data". Once all of the parameters have been entered, the function module can be started with F8.

The function modules usually report the success of a call using export parameters, for example, a document number generated by the system (see area highlighted in red). BAPI functions must often be confirmed by additionally calling the function module "BAPI_TRANSACTION_COMMIT". Otherwise, a document number or similar is generated but the document is not found in the system. If you want to test this type of BAPI function, you must either be satisfied with the successful generation of a document number or program the function module call from an SAP report (transaction "SE38") in the SAP system with a subsequent commit. In most cases, however, the document number generated is already sufficient to assume that the SAP object was successfully created. BAPI functions also usually have a property that documents messages about the success or failure of internal processes, documented in a table of the structure "BAPIRET2" (the parameter "RETURN" in this case). Here, all lines with the type E, A or X are evaluated as errors. In these cases, the document number should also be missing in the export. The messages often contain detailed information as to why the function could not be processed successfully, for example, mandatory fields could not be populated.

6. Designing the user interface

The next step involves creating a first draft of a user interface for the integration scenario. In this case, the user should enter all of the parameters required for the SAP function, or be able to identify them from the context. The potentially available constant templates (e.g. Order type) can be supplemented in the script later. For test cases, this information can also come from predefined edited fields.

6.1 Create the application

Create a new application based on the application template Basic application. Create the following edit fields on the edit page. Each time, select the option

"New data field" and

select the stated data type in the subsequent dialog in each case: Once all of the edit pages have been created, the edit page should look something like this:

The application title can be changed to "Create SAP sales order with Groovy" by double-clicking on the application structure.

6.2 Overview table on the start page

On the start page "All Entries", delete all elements apart from the table.

Select the following fields as table columns:

Save the application and publish it in the portal menu structure.

6.3. The application in the browser

On the edit page, you will see the created edit fields with the default entries. Enter test data in the empty fields that aren't read-only. Click on "OK".

The new data set is now shown in the table on the start page.

6.4. Summary

The created application enables you to record all data required for further processing in SAP. For the sake of simplification, only one order item can be recorded.

7. Process integration

This section contains the actual integration of an SAP system with the goal of generating an SAP sales order from the data entered via a process. The SAP sales order is generated with Groovy script using an SAP function module.

7.1 Generate the Groovy script

SAP function modules can provide very complex parameters. To avoid errors while creating the script, a script generator is provided with Intrexx. The complete description of this can be found here.

7.2. Create the process

In the Processes module, create a new process. As the source of the new process, select "Event initiated by change of a record".

Click on "Next".

Select the application created in this part of the Developer's Manual.

Select the record event "Insert" and click on "OK".

7.3. Groovy action

When a new record is added in the application, an SAP sales order should be created with a Groovy script. To do this, connect a Groovy script action with the previously created data group event handler.

Open the properties dialog of the Groovy script action by double-clicking on it.

Click on "Next".

Open the Intrexx editor.

The Groovy script generated earlier can be inserted here.

7.4. Identify fields from the data group

The script now needs to be modified.

Please note that United Planet assumes no liability for any errors caused by incorrect modifications.

To begin with, the data fields from the Intrexx application need to be identified.

To do this, we will add a section to the script where the Intrexx data fields are referenced. After the variable definition, you can open a list of the data group's data fields by right-clicking. If a data field is selected from this list, the corresponding script is inserted at the position of the cursor.

Here is a complete list of the data field references:
//----------- get fields from intrexx application
String l_auart     = g_record["62DA58045C6CFD8563C3DEF0BAAE7C735D795DEA"].value /* datafield Auftragsart <string> */
String l_vkorg     = g_record["52F0E7D8EA1F753A9DDF0C1935CCAAA830A2366F"].value /* datafield Verkaufsorganisation <string> */
String l_vtweg     = g_record["FFAEE9A26755F1D3B97A8CA6BACE62CE7F9B323C"].value /* datafield Vertriebsweg <string> */
String l_spart     = g_record["C070F4974E8D089E39F161B42DD69F4F8BB8B5A1"].value /* datafield Sparte <string> */
String l_kunnr     = g_record["FD187855859411C25BE71F49712CA25BDDE35BB1"].value /* datafield Kundennummer <string> */
String l_bstnk     = g_record["23921A42F9BDF289B2C9525EB6B4136502E906D6"].value /* datafield Bestellnummer <string> */
String l_bstdt     = g_record["976D72ABB914AFACE9CFE6B8B68B3CC39FD21A4C"].value /* datafield Bestelldatum <datetime> */
String l_matnr     = g_record["370F41779D4901CB3A4291E618FD006AE1777581"].value /* datafield Artikel Nr. <string> */
String l_qunty     = g_record["F5FDA4922F324BA082A87A8BE2D6C169B59B9318"].value /* datafield Menge <integer> */

7.5. Populate SAP parameters

The data field variables now need to be assigned to the correct parameters of the function module. In the script, the parameters are pre-generated. The empty value "l_value" is assigned to the parameters.

g_sap.setImpStrucParField(l_function, "ORDER_HEADER_IN","DOC_TYPE", l_value); // ABAP Type C: 4,0
These should now be replace. The value "l_value" is replaced by the variable "l_auart" in this example. Generated coding for tables can be modified as follows:
// Imported Table ORDER_PARTNERS (ABAP Structure: BAPIPARNR) - Belegpartner
l_table = g_sap.getTable(l_function,"ORDER_PARTNERS");
for(int i = 0; i < 1; i++){
	g_sap.setTableField(l_table, "PARTN_ROLE", "AG");  // ABAP Type C: 2,0
	g_sap.setTableField(l_table, "PARTN_NUMB", l_kunnr);  // ABAP Type C: 10,0
} // Table ORDER_PARTNERS end
Generally, a FOR loop is generated for tables. If only one table row is required (as is the case in this simplified example), the 0 needs to be replaced by a 1. Unrequired parameters can be deleted to keep the script small and manageable. If they are needed later, then they can be copied individually from the generated coding of the SAP groovy generator.

7.6. Evaluate SAP call

An SAP call is performed at this point in the generated script.
// Execute SAP function
if(g_sap.executeSapFunction(l_client, l_function)) {
if(l_trace) + "Fill export parameters")
// ---- get and check results
l_value = g_sap.getExpPar(l_function,"SALESDOCUMENT")	// ABAP Type: C 20,0 - Number of Generated Document
The following script starts the SAP function module, analyzes the returned order number and writes this to the current data set in the Intrexx data group. A rollback is performed in the case of errors.
if(g_sap.executeSapFunction(l_client, l_function)){
//  SALESDOCUMENT (ABAP Type: C 10,0) - Number of the generated document
	String l_vbeln = g_sap.getExpPar(l_function,"SALESDOCUMENT");
		return null;
//  save vbeln to datagroup
	l_conn = g_dbConnections.systemConnection;
	l_lid = g_record["66B1F38BA125C4C22444D27AC9108B91A208EE16"].getValue(); // datafield (PK) (S) ID
	l_stmtOrderUpdate = g_dbQuery.prepare(l_conn, "UPDATE XTABLE9EB02DDB SET SAP_VBELN = ? WHERE LID = ?");
	l_stmtOrderUpdate.setString(1, l_vbeln);
	l_stmtOrderUpdate.setInt(2, l_lid);
//  final commit
	g_sap.BapiCommit(l_client, false); // set to true if commit should wait
	return null;
The following coding is well-suited for finding errors by writing notifications from the BAPIRET2 messages to the log file.
// output bapi return messages
      for(int i = 0; i < l_function.getTableParameterList().getTable("RETURN").getNumRows(); i++){
If an error occurs, the results from the BAPI messages are written to the portal log file. The log file can be found in the Processes module via the Process menu / Analyze log file. After the script has been completed, the process can be published.

8. Test

8.1. Generate new data set

In the application created earlier, you can now create a test data set. The order number is write-protected and is empty. When you click on "OK", the data set is added and this triggers the process with the Groovy script action.

The data set just created is now supplemented by the SAP order number. If an error occurs or the SAP order number isn't entered, please check the log file.

8.2. View SAP order

With the SAP order number, the correspdonding order can be viewed in SAP with the transaction "VA03".

8.3. Check completeness

Now, the department now needs to check and confirm whether the order in SAP is identical to the data set created in the Intrexx application. Additionally, subsequent processes such as delivery or invoicing need to be checked.

9. Possible extensions

9.1. RFC functions with item data

In our example, we have deliberately avoided processing multiple order items. This doesn't correspond to the reality of many scenarios. Therefore, this section will look at the steps required when multiple positions should be processed with Groovy script. For this purpose, the data group, which saves the data for the order header, is seen as the parent data group. Beneath this parent data group, child data groups, also known as subordinate data groups, are used that contain the order items to the header datasets. The child data group contains the ID of the parent dataset in the data field "FKLID".

You can see a very simplified sales processing here. The data group "Order items" has been created as a child data group of the "Sales orders" data group; this contains the header data. Now, a change to a dataset from the "Sales orders" data group could trigger the Groovy script in a corresponding process. But with this, there should be a check that the entire order is complete. The dataset for the sales order is saved in Intrexx before the first order item is created. The process from Intrexx's side will therefore look something like this:
  1. Enter and confirm header data in the application
  2. Enter items in the application
  3. Complete entire order in the application, e.g. with a manually or automatically set checkbox
  4. Further processing with a Groovy script action
// get db connection
def l_conn = g_dbConnections.systemConnection

// get Lid for parent record
def l_fkLid = g_record["BBCAC884A9397E98BD683B76386D58A39A8B56BC"].getValue(); // datafield (PK) (S) ID

// db query postions
def l_stmtPositions = g_dbQuery.prepare(l_conn, "SELECT LID, STR_BARCODEDERPOSITIO_616DDB16 FROM XDATAGROUP12A091EF WHERE FKLID = ?")
l_stmtPositions.setInt(1, l_fkLid)
def l_rsPositions = l_stmtPositions.executeQuery()

// loop all positions
{"Record: " + it.value(1) + " = " + it.value(2))
The Groovy script requires the actual technical names of the data group "Order times" and the data field "Barcode". These can be identified on the Application structure tab and then inserted into the example script at the corresponding postions. The script outputs the order item information in the log file. Once a complete dataset with items has been recorded, the following information is available there.

INFO  2008-08-24 14:31:59,203 - de.uplanet.lucy.server.workflow.GroovySkriptCall[WebConnectorWorker-localhost:8102-8]
Record: 34 = 1000
INFO  2008-08-24 14:31:59,203 - de.uplanet.lucy.server.workflow.GroovySkriptCall[WebConnectorWorker-localhost:8102-8]
Record: 35 = 2000
If the item data should be entered into a table of an SAP function module, as in the previous example, the script for the loop needs to be changed slightly.
// loop all positions
l_table = g_sap.getTable(l_function,"IT_POSITION");
	String l_barcode = it.value(2);
	g_sap.setTableField(l_table, "BARCODE", l_barcode);

9.2. Using the Groovy API in Velocity

The methods of the Groovy API are implemented in the business logic of the Connector for SAP Business Suite as the Java class "net.initall.ixapi.groovy.IxSapGroovyAPI". The available methods can be found in the Appendix. To be able to use the methods in Velocity as well, the Groovy API needs to be registered as a "callable". Callables are an expansion concept for integrating any Java classes. These Java classes can be registered in the portal configuration file "customcallables.cfg". To use the Groovy API of the Connector for SAP Business Suite, you should use the context name "GSAP".
<?xml version="1.0" encoding="UTF-8"?>
	xsi:schemaLocation="urn:schemas-unitedplanet-de:lucy:server:velocity:callables callables.xsd">
  <item contextName="GSAP" className="net.initall.ixapi.groovy.IxSapGroovyAPI" use="instance"/>
As of Business Logic 2010, a fast-track configuration is available which takes over these settings automatically.

9.3. Using the simpleRFC functionality

The simpleRFC API provides functions for calling BAPI/RFC functions in Velocity sripts. Velocity scripts can be integrated into Intrexx view pages, for example. Please note that Velocity scripts are performed on the server while the delivered HTML pages are being generated. Parameters need to be transferred via request values or via the sessions. The license "View & Write" is the minimum requirement for this. However, with the license "View Only", a simple call can be used that can call a simple remote-capable function module with an import and export parameter. The complete API is described in the Appendix. Notes on its usage are provided in the following sections.

9.3.1. VUsing the trigger functionality (simple call)

For simple calls of RFC functions, a special function of the simpleRFC API is available. This can be used for typical triggers sich as calculating current data in SAP or similar. With this, an import parameter with the string type can be populated and an export parameter can be requested. The RFC functions require a special interface:
function z_demo_trigger_simplerfc .
*"*"Local interface:

concatenate 'Reply from SAP. Input value was:'
            into result separated by ' '.

As the import parameter, the parameter "INPUT" with a character data type (e.g. string) is expected. If a result should be written back, this needs to be done via the export parameter "RESULT". This function module can then be started from a view page, where a static text element with the option "Programming, only default language" has been created. The call is specified via the simpleRFC API as programming text:
$GSAP.simpleRfcTrigger("saperp", "system", "Z_DEMO_TRIGGER_SIMPLERFC", "external value") 

Loading the view page will now always call the function module and displays the result of the export parameter "RESULT" as text. In Intrexx, you can access the displayed value of the static text field with JavaScript (Attribute *.textContent). By integrating request values, a conditional execution can be implemented in the Velocity script.
#set($command = $!Request.get("rq_command"))
#if($command == "trigger")
$GSAP.simpleRfcTrigger("saperp", "system", "Z_DEMO_TRIGGER_SIMPLERFC", "external value") 
This simple call of BAPI/RFC functions is available in the license "View Only".

9.3.2. Using the entire simpleRFC API

The following example demonstrates the use of the simpleRFC API on view pages for complex function modules. The complete API is described in the Appendix. The majority of API methods' have "boolean" results. With this, the SAP Connector shows whether the last call of an API method was successful or not. The error handling needs to take place in its own Velocity script. A typical simpleRFC script has the following structure: As an example, a simple function will be used. This provides the stock levels of promotional gifts in the export table "ET_STOCK". This table needs to be populated accordingly:
function /iatl/messe_iphone_get_stock.
*"*"Locale interface:

  define append_line.
    et_stock-matnr = &1.
    et_stock-maktx = &2.
    et_stock-count = &3.
    append et_stock.

  append_line 'GA0001' 'Peppermints UP Design'  500.
  append_line 'GA0002' 'Puzzle'           	500.
  append_line 'GA0003' 'Porsche UP Design'      20.
  append_line 'GA0004' 'Skateboard UP Design'   100.
  append_line 'GA0005' 'Intrexx Xtreme 4.5 10 Users' 10.

The export structure has a simple structure and contains the material number, material abbreviation and the stock as an integer value.

This SAP function is used on a view page.

The script for calling the SAP function module via the simpleRFC API is entered in the static text element. The result is an HTML output of the returned table. This is transferred to the request parameter "sapstock". This first text field can also be moved to the hidden area. A second static text element displays the current request value and thus the currently identified HTML output of the stock as text. The script of the first static text field displays the usage of the simpleRFC methods as an example:
## open connection
Open Connection: $GSAP.simpleRfcConnect("saperp","system")

## function open
<br>Load Function: $GSAP.simpleRfcFunctionLoad("/IATL/MESSE_IPHONE_GET_STOCK")

## execute without commit
<br>Execute: $GSAP.simpleRfcFunctionExecute(false)

## loop export table
<br>Set Focus Table: $GSAP.simpleRfcSetFocusTable("ET_STOCK")
<br>Records found: $GSAP.simpleRfcTableGetCount()
#set($strOutput = "<table>")
#foreach($line in $GSAP.simpleRfcTableGetEntries())
 <br> List Item: $line
  Set Table Line: $GSAP.simpleRfcTableSetLine($line)	

#set($strMatnr = $GSAP.simpleRfcGetParameterValue())

#set($strText = $GSAP.simpleRfcGetParameterValue())

#set($strCount = $GSAP.simpleRfcGetParameterValue())

#set($strOutput = $strOutput + "<tr><td>" + $strMatnr + "</td><td>" + $strText + "</td><td>" + $strCount + "</td></tr>")
#set($strOutput = $strOutput + "</table>")

## function close
<br>Close Function: $GSAP.simpleRfcFunctionClose(false)

## close Connection
<br>Close Connection: $GSAP.simpleRfcClose()

## set output

Especially the populating and reading of parameters from the function module interface can be complex here. Each parameter needs to be focussed first before it can be read and set. Structures and tables require a doubled focussing - to begin with the table / structure is put in focus, then the column (the field). The simpleRFC API allows access to tables via special API methods, e.g. to identify the amount, or to position or added new rows. In short, the following VM code loops over all available table lines:
#foreach($line in $GSAP.simpleRfcTableGetEntries())
		#set($strMatnr = $GSAP.simpleRfcGetParameterValue())

10. More information

Developer's Manual part 4
API Developer's Manual