Introduction

The RFC/BOR Generator allows to generate OData services based on existing RFC function modules or BOR-Interfaces without writing a single line of code. The problem with using the RFC/BOR generator is however that not all RFC function modules are well suited to be used in the generator because of their interface structure and that there is not always a RFC function module available that can be mapped to a method of an OData service. Also some customers prefer to develop RFC function modules rather than performing a code based implementation of an OData service.

As a result it is sometimes necessary to develop an appropriate RFC function module that can be used with the RFC/BOR generator.

This how-to guide therefore describes how to implement an OData service starting by creating RFCs (ABAP functions) that reflect the signature of OData EntityTypes. The RFCs are used to generate an OData service with no manual code within the service provider implementation.

You may use this approach if

  • You don’t want to implement an OData service provider class
  • There is the add-on IW_BEP is not available at the business backend

In this guide the Cross Application Time Sheet will be used as a basis. The service will contain two entities: TimeConfirmation and AttendanceAbsence and a bidirectional association between the entities. To simplify our example, it will support the confirmation of attendances and absences only.

/wp-content/uploads/2014/06/image001_467005.png

Prerequisites

This document assumes the following:

  • You have access to a NetWeaver 7.0, 7.01, 7,02, 7.31 into which the SAP NetWeaver Gateway ABAP add-ons have been installed. (For detailed installation requirements please see SAP Note 1569624)
  • You have access to an ECC system with the cross application timesheet (CA-TS) installed and configured.
  • You have at least a basic understanding of ABAP development.

It should be understood that an SAP NetWeaver Gateway system is not a new type of SAP system; it is merely a set of ABAP add-ons that can be applied to a SAP NetWeaver 7.00, 7.01, 7.02 and 7.31  (For detailed installation requirements please see SAP Note 1569624).

The add-ons, taken together, form the Gateway system; however, the add-ons can be installed either all together on a single ABAP system or split across two ABAP systems. For a detailed discussion of the different deployment options please see the blog SAP NetWeaver Gateway deployment options in a nutshell from Andre Fischer.

Although there are several ABAP add-ons making up a Gateway system, for the purposes of this discussion, the important ones are GW_CORE and IW_BEP because these two components determine in which system your development and configuration are performed.

a) GW_CORE and IW_BEP in the same system If GW_CORE and IW_BEP are installed in the same SAP system, then this system will call the ABAP functions that are described in this how-to guide using a business system using an RFC destination. Such an installation scenario is recommended if the backend business system does not contain the software component IW_BEP.

b) GW_CORE and IW_BEP in different systems If GW_CORE and IW_BEP are in different systems, then the system containing GW_CORE will be your Gateway server, and the system containing IW_BEP will be your backend business system. This deployment option requires less remote call between the ABAP system for processing $expand query options for example.

/wp-content/uploads/2014/06/image002_467051.png

In order to perform the tasks in this tutorial, you will need to log on to whichever ABAP system contains the IW_BEP component with a user id having sufficient authorization to perform ABAP development and has been registered as a developer.

You will also need to log on to the ABAP system containing the GW_CORE add-on in order to perform a single configuration task.

Task 1: Create ABAP DDIC Structures

Step 1: Create the DDIC Structure That Contains the Elements of the EntityType

The first step is to create one structure per OData entity that reflects exactly the structure of the OData entity. Please keep in mind that the ABAP types will be mapped to EDMX types later on.  You will find details on the type mapping at gateway documentation:
Mappings and the ABAP Type Editor – SAP NetWeaver Gateway – SAP Library

Use transaction SE11 or SE80 to create the structure ZGW_TIMECONF_CATS_RECORD.

/wp-content/uploads/2014/06/image003_467053.jpg

You may optionally create a structure for the AttendanceAbsenceType entity. However, in this example the structure of table T544T is used to demonstrate projection and renaming of attributes later on.

Step 2: Create a DDIC structure for the OData filters

The OData service generator at SEGW is able to map range tables to OData filters. There a structure for each column that shall support filtering an additional structure is required.
Create a structure ZGW_TIMECONF_SEL_DATE to support filtering on the element workdate.

/wp-content/uploads/2014/06/image004_467072.jpg

Create a structure ZGW_TIMECONF_SEL_AWART to support filtering on the element ABS_ATT_TYPE.

/wp-content/uploads/2014/06/image005_467074.jpg

Task 2: Create the Functions for the OData Standard Operations and the Entity TimeConfirmation

One of the goals of this how-to is demonstrate the implementation of an OData service without manual code within the Service Provider Implementation. To achieve this, a dedicated function for each supported OData operation is required.  Most of these functions are pretty simple. However, the Get Entity Set requires some thoughts.

Step 1: Create a GetEntitySet Function

There are two types of import parameters that can be mapped with SEGW without manual code.

  • Each column that supports OData filters need to be reflected with a range table. In this exercise at Task 1 there are two examples.
    These range tables are additionally required to support to-many associations. The generated code will use the element that is specified at the referential constrains of an association to map is to a range table.
  • To optimize on performance you may add a max hits parameter as well. The generator at SEGW is able to connect this parameter to the $top and $skip query option.

As a result of such a request the requested entices shall be returned or an error message. This ideally achieved by table parameters of the function

  • In this example the DDIC structure ZGW_TIMECONF_CATS_RECORD is used to define return parameter for the entity set.
  • For potential error messages always use the DDIC structure BAPIRET2.
  1. Create a Function Group with name Z_GW_TIMECONFIRMATION for example.
  2. Create a Function with name Z_GW_TIMECONF_GET_ENTITYSET and choose the Processing Type Remote-Enabled module at the attributes tab.
    /wp-content/uploads/2014/06/image006_467087.png
  3. Add the importing parameter iv_maxhits
    /wp-content/uploads/2014/06/image007_467088.png
  4. Add table parameters for the entityset, the filter and the error messages
    /wp-content/uploads/2014/06/image008_467089.png
  5. Add the following code at the Source code tab

  DATA lv_employee_number TYPE hr_pernr.
  DATA lv_lines TYPE i.
  DATA ls_filter_date LIKE LINE OF filter_date.

  DATA ls_sel_employee  TYPE bapihrselemployee.
  DATA lt_sel_employee LIKE STANDARD TABLE OF ls_sel_employee WITH DEFAULT KEY.
  DATA lt_catsrecords TYPE STANDARD TABLE OF bapicats2 WITH DEFAULT KEY.
  DATA ls_entity LIKE LINE OF entityset.
  FIELD-SYMBOLS: <fs_catsrecords> TYPE bapicats2.

  DATA lv_entity_count TYPE i.

  CALL FUNCTION ‘HR_GETEMPLOYEEDATA_FROMUSER’
    EXPORTING
      username                  = sy-uname
    IMPORTING
      employeenumber            = lv_employee_number
      return                    = return
    EXCEPTIONS
      user_not_found            = 1
      countrygrouping_not_found = 2
      infty_not_found           = 3
      OTHERS                    = 4.
  IF sy-subrc <> 0.
MESSAGE e001(ztime_confirmation) WITH sy-uname.
*   Infotype Communication (105) is not maintained for user &1.
  ENDIF.

  ls_sel_employee-option = ‘EQ’.
  ls_sel_employee-sign = ‘I’.
  ls_sel_employee-high = lv_employee_number.
  ls_sel_employee-low = lv_employee_number.
  APPEND ls_sel_employee TO lt_sel_employee.

  DESCRIBE TABLE filter_date LINES lv_lines.
  READ TABLE filter_date INTO ls_filter_date INDEX 1.
  IF lv_lines NE 1 OR ls_filter_date-option NE ‘BT’ OR  ls_filter_date-sign NE ‘I’.
* error handling for unsuported filter
  ENDIF.
  IF lv_lines = 0.
    ls_filter_date-low  = ‘18000101’.
    ls_filter_date-high = ‘99991231’.
  ENDIF.

  CALL FUNCTION ‘BAPI_CATIMESHEETRECORD_GETLIST’
    EXPORTING
      fromdate        = ls_filter_date-low
      todate          = ls_filter_date-high
    TABLES
      sel_employee    = lt_sel_employee
      catsrecords_out = lt_catsrecords
      return          = return.

  LOOP AT lt_catsrecords ASSIGNING <fs_catsrecords>
     WHERE abs_att_type IN filter_attabs.
    IF ( lv_entity_count > iv_maxhits AND iv_maxhits NE 0 ).
      EXIT.
    ENDIF.
    IF <fs_catsrecords>-status < ’40’ AND <fs_catsrecords>-catshours IS NOT INITIAL.
      MOVE-CORRESPONDING <fs_catsrecords> TO ls_entity.
      APPEND ls_entity TO entityset.
      ADD 1 TO lv_entity_count.
    ENDIF.
  ENDLOOP.

ENDFUNCTION.

Step 2: Create a GetEntity Function

The GetEntity function returns a single record specified by the key.  Consequently there is an import parameter for the key, an exporting structure for result and a tables parameter for potential error messages.

To keep the size of this how-to guide in limits, you find the parameters and the source code of the function in table below. Please keep mind that the function needs to be RFC enabled and consequently all parameter “Pass Value” be checked.

Function Name Z_GW_TIMECONF_GET_ENTITY
Import
Parameter Name Associated Type
IV_KEY CATSCOUNTE
Export
Parameter Name Associated Type
ENTITY ZGW_TIMECONF_CATS_RECORD
Tables
Parameter Name Associated Type
RETURN BAPIRET2

Source Code:

  DATA ls_sel_employee  TYPE bapihrselemployee.
  DATA lt_sel_employee LIKE STANDARD TABLE OF ls_sel_employee WITH DEFAULT KEY.
  DATA lt_catsrecords TYPE STANDARD TABLE OF bapicats2 WITH DEFAULT KEY.
  FIELD-SYMBOLS: <fs_catsrecords> TYPE bapicats2.

  DATA lv_pernr  TYPE catsdb-pernr.
  DATA lv_workdate TYPE catsdb-workdate.

* there is no function available that reads by primary key
*  1. select form the db directly
*  2. use retrieved workdate and pernr as parameter for the bapi
* So authority check and exits are procesed
  SELECT SINGLE pernr workdate INTO (lv_pernr , lv_workdate)
     FROM catsdb WHERE counter = iv_key.
  IF sy-subrc NE 0.
* error handling
  ENDIF.
  ls_sel_employee-low = lv_pernr.
  ls_sel_employee-option = ‘EQ’.
  ls_sel_employee-sign = ‘I’.
  APPEND ls_sel_employee TO lt_sel_employee.
  CALL FUNCTION ‘BAPI_CATIMESHEETRECORD_GETLIST’
    EXPORTING
      fromdate        = lv_workdate
      todate          = lv_workdate
    TABLES
      sel_employee    = lt_sel_employee
      catsrecords_out = lt_catsrecords
      return          = return.

  LOOP AT lt_catsrecords ASSIGNING <fs_catsrecords> WHERE counter = iv_key.
    MOVE-CORRESPONDING <fs_catsrecords> TO entity.
  ENDLOOP.
  IF entity-counter IS INITIAL.
** error handling
  ENDIF.

Task 3: Create the Functions to read the Attendance and Absence Types

The Attendance and Absence Type entity is used to demonstrate a bidirectional association. On the UI this entity could be used to fill a dropdown box on a UI and to retrieve the texts describing the attendance/absence codes.

Step 1: Create a GetEntitySet Function

In this example there is a filter on the attendance/absence codes is supported.

Function Name Z_GW_TIMECONF_GET_ATT_ABS_SET
Import
Parameter Name Associated Type
FILTER_ATTABS ZGW_TIMECONF_SEL_AWART
ATTENDANCE_ABSENCE T554T

Source Code

  DATA lv_employee_number TYPE hr_pernr.
  DATA lt_attendance_absence LIKE attendance_absence[].
  FIELD-SYMBOLS <fs_attabs> LIKE LINE OF attendance_absence[].

  CALL FUNCTION ‘HR_GETEMPLOYEEDATA_FROMUSER’
    EXPORTING
      username                  = sy-uname
    IMPORTING
      employeenumber            = lv_employee_number
    EXCEPTIONS
      user_not_found            = 1
      countrygrouping_not_found = 2
      infty_not_found           = 3
      OTHERS                    = 4.
  IF sy-subrc <> 0.
    MESSAGE e001(ztime_confirmation) WITH sy-uname.
*   Infotype Communication (105) is not maintained for user &1.
  ENDIF.

  CALL FUNCTION ‘CATS_GET_ABSENCE_ATTENDANCE’
    EXPORTING
      i_pernr            = lv_employee_number
    TABLES
      attendance_absence = lt_attendance_absence.

  LOOP AT lt_attendance_absence ASSIGNING <fs_attabs> WHERE awart IN filter_attabs.
    APPEND <fs_attabs> TO attendance_absence.
  ENDLOOP.

Step 2: Create a GetEntity Function

Function Name
Import
Parameter Name Associated Type
IV_ATT_ABS_CODE AWARRT
Export
Parameter Name Associated Type
ATTENDANCE_ABSENCE T554T

Source Code

   DATA lt_att_abs LIKE STANDARD TABLE OF attendance_absence.

  CALL FUNCTION ‘Z_GW_TIMECONF_GET_ATT_ABS_SET’
    TABLES
      attendance_absence = lt_att_abs.

  READ TABLE lt_att_abs INTO attendance_absence
      WITH KEY awart = iv_att_abs_code.

Task 4: Create a Service Builder Project

In this and the following tasks you use the functions created in the previous tasks to generate the data model and the service.
Use transaction SEGW and create a project (CTRL+F5)

/wp-content/uploads/2014/06/image009_467114.png

Task 5: Define the Data Model

Step 1: Create EnityType and EntitySet for TimeConfirmation.

At task 1 you created a structure that reflects that contains all attributes of the entity time confirmation. To create the EntityType and EntitySet TimeConfirmation left click on DataModel at the navigation tree and choose import RFC/BOR Interface.

/wp-content/uploads/2014/06/image010_467116.jpg

At the first step of the wizard you specify the name of the EnityType. Choose Remote Function Call and use the Function Z_GW_TIMECONF_GET_ENTITY.
Make sure that you have ticked “Create Default Entity Set”. So you don’t need to create the entity set later on.

/wp-content/uploads/2014/06/image011_467120.jpg

Choose all properties of ENTITY data source parameter.

/wp-content/uploads/2014/06/image012_467121.png

Select counter as the key of the Entity.

/wp-content/uploads/2014/06/image013_467122.jpg

Step 2: Create EnityType and EntitySet for AttendanceAbsenceType

Repeat the same steps described at Step 1: Create EnityType and EntitySet of TimeConfirmation.  to create the EntityType and EntitySet for Attendance and Absence Types.

  1. Use Context Menu of Data Model to choose import and RFC/BOR interface.
  2. At the first step of the wizard specify AttendanceAbsenceType as EntityTypeName and Z_GW_TIMECONF_GET_ATT_ABS as the function name.
  3. At the second step of the wizard choose AWART and ATEXT of the parameter ATTENDANCE_ABSENCE
  4. Choose a meaningful name for AWART and ATEXT like in the screenshot below.
  5. Check “Is Key” for AWART

/wp-content/uploads/2014/06/image014_467124.jpg

Step 3: Create an association

Create the association between the two entities of the Service.
In the navigation tree left-click on Association and choose create to start the association wizard.
At the first wizard step specify the association name, the entities, the cardinalities and the navigation properties as shown in the screen shot below.

/wp-content/uploads/2014/06/image015_467125.jpg

At the second wizard step you specify the referential constraints. This constraint is used by generator to resolve the association during runtime.

/wp-content/uploads/2014/06/image016_467126.jpg

Task 6: Map Data Model to Data Source

Finally you need to map the EntitySets to the RFCs.

Step 1: Create the Mapping for the EntitySet AttaendanceAbsenceTypeSet

GetEntity

Left click at CreateEntity of AttendanceAbsenceTypeSet in the section Service Implementation. At the context menu choose Map to Data Source

/wp-content/uploads/2014/06/image017_467127.jpg

Choose Remote Function Call and the function Z_GW_TIMECONF_GET_ATT_ABS.

/wp-content/uploads/2014/06/image018_467128.png

First Drag IV_ATT_ABS_CODE from the right tree titled data source parameters to the field data source parameters of the “Mapping of Operation …” table. Then click on propose mapping. The system will add an additional line of the output mapping of AttendanceAbsenceTypeCode.

/wp-content/uploads/2014/06/image019_467132.jpg

GetEntitySet

Continue with mapping for GetEntitySet to the function Z_GW_TIMECONF_GET_ATT_ABS_SET.

OData Operation GetEntitySet
Function Name Z_GW_TIMECONF_GET_ATT_ABS_SET
Input
Entity Set Property Data Source Parameter
AttendanceAbsenceTypeCode IV_ATT_ABS_CODE
Output
Entity Set Property Data Source Parameter
AttendanceAbsenceTypeCode ATTENDANCE_ABSENCE\AWART
AttendanceAbsenceTypeText ATTENDANCE_ABSENCE\ATEXT

Step 2: Create the Mapping for the EntitySet AttaendanceAbsenceTypeSet

GetEntitySet

First drag and drop the range tables to the right EntitySet Properties. Then click on propose mapping to get a defaulted mapping. The system will insert two new lines for the output mapping of Workdate and AbsAttType

Click on the data source parameter IV_MAXHITS. Above the tree with data source parameters you find a row of icons. Click on the second icon from the right to map the $top (max hits) query option to the data source parameter IV_MAXHITS.

OData Operation GetEntitySet
Function Name Z_GW_TIMECONF_GET_ENTITYSET
Input
Entity Set Property Data Source Parameter
Workdate FILTER_DATE
AbsAttType FILTER_ATTABS
Output
Entity Set Property Data Source Parameter
Counter ENTITYSET/COUNTER
Workdate ENTITYSET/WORKDATE
Employeenumber ENTITYSET/EMPLOYEENUMBER
AbsAttType ENTITYSET/ABS_ATT_TYPE
Catshours ENTITYSET/CATSHOURS
Unit ENTITYSET/UNIT

GetEntity

First drag and drop the data source parameter IV_KEY the property Counter. Than choose Propose Mapping.

OData Operation GetEntity
Function Name Z_GW_TIMECONF_GET_ENTITY
Input
Entity Set Property Data Source Parameter
Counter IV_KEY
Output
Entity Set Property Data Source Parameter
Counter ENTITYSET/COUNTER
Workdate ENTITYSET/WORKDATE
Employeenumber ENTITYSET/EMPLOYEENUMBER
AbsAttType ENTITYSET/ABS_ATT_TYPE
Catshours ENTITYSET/CATSHOURS
Unit ENTITYSET/UNIT

Create

First drag and drop the data source parameter RS_ENTITY_OUT\COUNTER the property Counter. Than choose Propose Mapping.

OData Operation Create
Function Name Z_GW_TIMECONF_CREATE_ENTITY
Input
Entity Set Property Data Source Parameter
Counter RS_ENTITY_OUT\COUNTER
Output
Entity Set Property Data Source Parameter
Counter ENTITYSET/COUNTER
Workdate ENTITYSET/WORKDATE
Employeenumber ENTITYSET/EMPLOYEENUMBER
AbsAttType ENTITYSET/ABS_ATT_TYPE
Catshours ENTITYSET/CATSHOURS
Unit ENTITYSET/UNIT

Delete

Propose Mapping will default a correct mapping for this operation.

OData Operation Delete
Function Name Z_GW_TIMECONF_DELETE_ENTITY
Input
Entity Set Property Data Source Parameter
Counter IV_KEY

Update

Propose Mapping will default a correct mapping for this operation as well.

OData Operation Create
Function Name Z_GW_TIMECONF_CREATE_ENTITY
Input
Entity Set Property Data Source Parameter
Counter ENTITYSET/COUNTER
Workdate ENTITYSET/WORKDATE
Employeenumber ENTITYSET/EMPLOYEENUMBER
AbsAttType ENTITYSET/ABS_ATT_TYPE
Catshours ENTITYSET/CATSHOURS
Unit

ENTITYSET/UNIT

Task 7: Generate and Register the Service

To generate the service choose menu Project – Generate Service
Use the section Service Maintenance in the navigation tree to register and maintain your service.
You find further details on service maintenance at the SAP help portal:

Service Maintenance – SAP NetWeaver Gateway – SAP Library

Task 8: Test the Service

To be able to test this example

  • the Cross Application Time Sheet need to be configured at your system
  • your logon user need to be assigned to an employee id (Infotpye 105)
  • a default CATS profile needs to be maintained

You may use transaction /IWFND/GW_CLIENT to test your service.

  • Select all Time Confirmations: <service root>/TimeConfirmationSet
  • Select all Time Confirmations expanded with Attendance Absence Types: <service root>/TimeConfirmationSet?expand=to_AttendanceAbsence
  • Select all AttendacneAbsence Types :<service root>/AttendanceAbsenceSet
  • Select all AttendacneAbsence Types expanded with Time Confirmations:<service root>/AttendanceAbsenceSet?expand=to_TimeConfirmation
To report this post you need to login first.

10 Comments

You must be Logged on to comment or reply to a post.

  1. Krishna Kishor Kammaje

    I feel this document is trying to solve a wrong problem.( except in option a).

    This is like trying to put-on weight to fit to the oversized shirt.

    Document says Also some customers prefer to develop RFC function modules rather than performing a code based implementation of an OData service.“. In this case consultants should educate customers on code based approach and outside-in approach rather than creating RFCs for oData services.

    regards

    Krishna

    (0) 
    1. Andre Fischer

      Hi Krishna,

      I agree that creating Gateway services using code based implementation has a lot of advantages over using the RFC/BOR generator.

      See my blog To code or not to code …

      But in the past I have met several customers that really preferred to use RFC’s rather than doing an implementation of a data provider class.

      So since we cannot hinder our customers to use the generators we are delivering 😉 we want to make most out of this situation by explaining how a RFC function module is best developed when you want to use the RFC / BOR generator.

      Though as a consultant I would recommend to perform a code based implementation I have to leave it to our customers to take a decision what to use.

      Best Regards,

      Andre

      (0) 
      1. Krishna Kishor Kammaje

        Hi Andre, I completely understand that customers are insisting for that, and I have been at the receiving end for the last one year. 🙂 But this situation is mainly because various demos by SAP have concentrated mainly on BAPI based generation.

        When I asked WHY? with some of those customers, they said that they thought that is the only way to create oData services.

        Luckily SAP did not stress/demo too much on BORs. Otherwise we would have ended up wrapping every FM/Class/Report as BORs by now. 😉

        (0) 
      2. Ron Sargeant

        Hi Andre,

        I agree with Krishna’s sentiments that BAPI is generally not a good fit for GW services. This should come as no surprise as I have aired my disapproval in the Gateway spaces many times.

        During early product marketing, FM’s have been dangled as a carrot to get customers to adopt Gateway based on familiar components. Unfortunately, those components weren’t designed to fit an ROA pattern and when they do fit it tends to be luck (or marketing manipulation).

        Now customers think they have to eat the carrot in order to proceed when they would probably have gone in that direction without a carrot to confuse matters 😆

        I think it is a ridiculous situation to be in when it is suggested that I write a function module to fit into a class-based framework that uses an awkward mapping tool to integrate it. The amount of design and development overhead that this introduces into the process is in no-one’s interest. Matthias’s example in this blog is a case in point, all of this code could have gone into one place.

        I thought SAP were keen for customers to follow best practice, not say “here it is, make whatever mess with it you like”!

        How many Fiori UI2/app services are generated from FM’s? I haven’t come across one yet. Given the need to expedite the release of Fiori, why no function library, if this technique is on a par with code-based implementation?

        I’ve also made the point that OData is NOT based on ABAP Dictionary and the binding to such is really a convenience rather than a necessity. Basing on FM does nothing to aid this transition. 

        For good or bad, the product is out there but I’d really like to see RFC and BOR take a back seat (on the edge of a cliff if possible).

        Regards

        Ron.

        (0) 
  2. Joao Sousa

    I’m afraid I don’t really see this as using RFC to generate oData services, it’s more of “How to Create a oData service while making the coder think he is really creating it via RFC”. Like someone said, like gaining weight to fit a large shirt.

    BAPI are not designed for a oData approach and that should be made crystal clear.

    You made a large effort and I would like to say it is positive, but in this case I must be honest and I really think it’s misguided.

    (0) 
    1. convy sera

      hi  all,

      how to pass tables parameters of a RFC as input in the ODATA service.

      We use filter for import parameters in the odata but how to achive table as input

      (0) 

Leave a Reply