Skip to Content
Technical Articles

Custom data selection for a Fiori list report application using DDIC Search help

In this blog you will see how a DDIC Search help can serve as a simple to use data source for a Fiori list report.

Why use a DDIC search help for a Fiori list report ?

Search helps are well known and heavily used to find and select SAP objects. They are part of the DDIC development objects and select data either directly DDIC-table based or code based via an ABAP interface.

Search helps serve as UI elements and are part of SAP GUI or Webdynpro applications. But they can be exposed via SAP Gateway to oDATA/ UI5 interfaces too.

The latter does not only allow to use them as a value help in an UI5 application.  The technology can easily be adapted to generate a Fiori List report application in the same way CDS Views are exposed.  The result list of a Search help is basically the same as a result list of a CDS select.

The advantage of this approach is that some restrictions of CDS / DDL developments can be overcome and this without deep knowledge of SAP Gateway or UI5 or Javascript. The main additional effort concerns the annotations which are manually defined for the list and the detail screen. This can be done easily via the annotation modeler in SAP WebIDE.

Restrictions of CDS based Fiori list reports

The restrictions that can be solved by this approach compared to a pure CDS based UI are:

  • Add difficult calculations which cannot be done in CDS or only on the DB layer but are available as function modules (example: purchase order history),
  • Add data from other sources than the data base (example: SM50, SM04, SM12 lists or file contents or RFC results),
  • Avoid performance issues in CDS especially before HANA which can be solved by sequential selects in ABAP.

What is the technical approach ?

The approach can roughly be described as follows:

  • Create (or reuse an existing) DDIC Search help.
    This Search help can contain a Search help exit which means the selection can be done by your own ABAP coding!
  • Create a gateway object and import the Search help
    • Don’t forget some flags to make the resulting oDATA entity searchable
  • Generate the gateway runtime objects
    • Add some coding for the defaultSearchElement
  • Add the service definition
  • Create a new Fiori element report in SAP WebIDE based on the service
    • Add a local annotation file for Select fields, List fields, Detail fields, Header fields

Here is an example tutorial which displays the list of the current users (like transaction SM04) in a Fiori elements list.
Please follow the step-by-step guide in detail:

  1. Define an elementary Search help and develop a Search help exit

    • SE11 -> Search help:
    • Add a new function module ZZ_SHLP_EXIT_SM04 with the following coding and parameters:
      FUNCTION zz_shlp_exit_sm04.
      *"----------------------------------------------------------------------
      *"*"Local Interface:
      *"  TABLES
      *"      SHLP_TAB TYPE  SHLP_DESCR_TAB_T
      *"      RECORD_TAB STRUCTURE  SEAHLPRES
      *"  CHANGING
      *"     VALUE(SHLP) TYPE  SHLP_DESCR_T
      *"     VALUE(CALLCONTROL) LIKE  DDSHF4CTRL STRUCTURE  DDSHF4CTRL
      *"----------------------------------------------------------------------
      
        TYPES: BEGIN OF ty_struc,
                 timestamp        TYPE  swftrctxt1,
                 server_name      TYPE  ssi_servername,
                 logon_hdl        TYPE  ssi_logon_hdl,
                 logon_id         TYPE  ssi_logon_id,
                 session_hdl      TYPE  ssi_session_hdl,
                 user_name        TYPE  ssi_user_name,
                 logon_type       TYPE  ssi_logon_type,
                 logon_sub_type   TYPE  ssi_logon_sub_type,
                 tenant           TYPE  ssi_tenant_id,
                 request_time     TYPE  ssi_timestamp,
                 memory           TYPE  ssi_memsize,
                 location_info    TYPE  ssi_logon_location_info,
                 application      TYPE  ssi_application,
                 application_info TYPE  ssi_application_info,
                 rfc_hdl          TYPE  ssi_rfc_hdl,
                 rfc_type         TYPE  ssi_rfc_type,
                 trace            TYPE  ssi_trace_level,
                 priority         TYPE  ssi_priority,
                 memory_brutto    TYPE  ssi_memsize_brutto,
                 memory_abap      TYPE  ssi_memsize_abap,
                 memory_hyper     TYPE  ssi_memsize_hyper,
                 memory_heap      TYPE  ssi_memsize_heap,
                 open_tasks       TYPE  ssi_open_tasks,
                 act_program      TYPE  ssi_main_program,
                 websocket_handle TYPE  ssi_websocket_handle,
                 sap_gui_version  TYPE  ssi_sap_gui_version,
                 paging_blocks    TYPE  ssi_sap_paging_blocks,
                 state            TYPE  ssi_logon_state,
                 client_ip_addr   TYPE  ssi_logon_client_ip,
               END OF ty_struc.
      
        DATA: rc           TYPE sy-subrc,
              session_list TYPE ssi_session_list,
              server_info  TYPE REF TO cl_server_info,
              lv_record    TYPE ty_struc,
              lt_record    TYPE TABLE OF ty_struc,
              time_t_bias  TYPE p VALUE '19700101000000'.
      
        CALL FUNCTION 'F4UT_OPTIMIZE_COLWIDTH'
          TABLES
            shlp_tab    = shlp_tab
            record_tab  = record_tab
          CHANGING
            shlp        = shlp
            callcontrol = callcontrol.
      
        IF callcontrol-step = 'SELECT'.
          TRY.
              CREATE OBJECT server_info.
      
              session_list = server_info->get_session_list( with_application_info = 1 ).
            CATCH cx_ssi_no_auth.
      
          ENDTRY.
      
          SORT session_list BY tenant user_name.
      
          LOOP AT session_list ASSIGNING FIELD-SYMBOL(<f>).
            MOVE-CORRESPONDING <f> TO lv_record.
            DATA(r_tstmp) = cl_abap_tstmp=>add(  
                  tstmp =     time_t_bias " UTC Time Stamp
                  secs  =     <f>-request_time " Time Interval in Seconds
                ).
            CONVERT TIME STAMP r_tstmp TIME ZONE sy-zonlo INTO DATE DATA(dat) TIME DATA(tim).
            lv_record-timestamp = 
             |{ dat(4) }-{ dat+4(2) }-{ dat+6(2) } { tim(2) }:{ tim+2(2) }:{ tim+4(2) }|.
      
            APPEND lv_record TO lt_record.
          ENDLOOP.
      
          CALL FUNCTION 'F4UT_RESULTS_MAP'
            EXPORTING
      *       SOURCE_STRUCTURE   =
              apply_restrictions = 'X'
            TABLES
              shlp_tab           = shlp_tab
              record_tab         = record_tab
              source_tab         = lt_record
            CHANGING
              shlp               = shlp
              callcontrol        = callcontrol
            EXCEPTIONS
              illegal_structure  = 1
              OTHERS             = 2.
          callcontrol-step = 'DISP'.
        ENDIF.
      
      ENDFUNCTION.​
  2. Go to the SAP Gateway Service Builder (SEGW)

    • Add a new project ZSM04
    • Go to ZSM04->Data Model->Import->Search Help
    • Define the entity type name ZZSM04 in the following wizard:
    • Select the upper check box to import the entire structure of the Search help:
    • Select the key fields:
    • Open the generated Entity Set ZzSm04Set and select the boxes Addressable and Searchable:

    • Double click the Properties of the Entity Type to mark Sortable and Filterable:

      You can change the labels for the fields later as well via the T Button.
    • Generate the runtime objects, result should be:
  3. Add the service definition in /IWFND/MAINT_SERVICE, result should be:

  4. Go to SAP WEB IDE (hana.ondemand.com)

    Choose New Project from Template -> List Report Application. In the service catalog choose your ZSM04_SRV.  There is no annotation file yet (ok) . In Data Binding choose your oDATACollection=ZzSm04Set as defined in the gateway and click Finish.
    You have now created the UI5 list report in your workspace. In theory, it can already be executed but without selections and no active columns (could be added by settings in the UI) and no details screen.

  5. Add annotations.

    To enable search fields, detail screen fields and a list a local annotation file needs to be added. This is the main difference to the CDS driven approach where the annotations come from the CDS DDL Source or the meta data extensions developed in Eclipse and ADT.
    For the example, see the annotation file which needs to be added first to the project: Place the cursor to the webapp folder in the generated project and do right mouse -> new -> annotation file. Just accept the proposed name and click next -> finish. The result is a annotation0.xml in the webapp folder. Open the file, choose Code Editor and copy the following content:

    <edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
    	<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Aggregation.V1.xml">
    		<edmx:Include Alias="Aggregation" Namespace="Org.OData.Aggregation.V1"/>
    	</edmx:Reference>
    	<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Authorization.V1.xml">
    		<edmx:Include Alias="Auth" Namespace="Org.OData.Authorization.V1"/>
    	</edmx:Reference>
    	<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Capabilities.V1.xml">
    		<edmx:Include Alias="Capabilities" Namespace="Org.OData.Capabilities.V1"/>
    	</edmx:Reference>
    	<edmx:Reference Uri="https://wiki.scn.sap.com/wiki/download/attachments/448470974/Common.xml?api=v2">
    		<edmx:Include Alias="Common" Namespace="com.sap.vocabularies.Common.v1"/>
    	</edmx:Reference>
    	<edmx:Reference Uri="https://wiki.scn.sap.com/wiki/download/attachments/448470971/Communication.xml?api=v2">
    		<edmx:Include Alias="Communication" Namespace="com.sap.vocabularies.Communication.v1"/>
    	</edmx:Reference>
    	<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.xml">
    		<edmx:Include Alias="Core" Namespace="Org.OData.Core.V1"/>
    	</edmx:Reference>
    	<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Measures.V1.xml">
    		<edmx:Include Alias="Measures" Namespace="Org.OData.Measures.V1"/>
    	</edmx:Reference>
    	<edmx:Reference Uri="https://wiki.scn.sap.com/wiki/download/attachments/448470968/UI.xml?api=v2">
    		<edmx:Include Alias="UI" Namespace="com.sap.vocabularies.UI.v1"/>
    	</edmx:Reference>
    	<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Validation.V1.xml">
    		<edmx:Include Alias="Validation" Namespace="Org.OData.Validation.V1"/>
    	</edmx:Reference>
    	<edmx:DataServices>
    		<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm">
    			<Annotations Target="ZSM04_SRV.ZzSm04">
    				<Annotation Term="UI.SelectionFields">
    					<Collection>
    						<PropertyPath>Application</PropertyPath>
    						<PropertyPath>Timestamp</PropertyPath>
    					</Collection>
    				</Annotation>
    				<Annotation Term="UI.HeaderInfo">
    					<Record Type="UI.HeaderInfoType">
    						<PropertyValue Property="TypeName" String="sm04 list"/>
    						<PropertyValue Property="TypeNamePlural" String="sm04 list"/>
    						<PropertyValue Property="Title">
    							<Record Type="UI.DataField">
    								<PropertyValue Property="Value" Path="UserName"/>
    							</Record>
    						</PropertyValue>
    					</Record>
    				</Annotation>
    				<Annotation Term="UI.Identification">
    					<Collection>
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="ServerName"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="UserName"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="Tenant"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="LocationInfo"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="Application"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="ApplicationInfo"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="ActProgram"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="ClientIpAddr"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="Memory"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="Timestamp"/>
    						</Record>
    					</Collection>
    				</Annotation>
    				<Annotation Term="UI.LineItem">
    					<Collection>
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="ServerName"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="UserName"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="Tenant"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="LocationInfo"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="Application"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="ApplicationInfo"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="ActProgram"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="ClientIpAddr"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="Memory"/>
    						</Record>" "
    						<Record Type="UI.DataField">
    							<PropertyValue Property="Value" Path="Timestamp"/>
    						</Record>
    					</Collection>
    				</Annotation>
    				<Annotation Term="UI.Facets">
    					<Collection>
    						<Record Type="UI.ReferenceFacet">
    							<PropertyValue Property="Target" AnnotationPath="@UI.Identification"/>
    						</Record>
    					</Collection>
    				</Annotation>
    			</Annotations>
    		</Schema>
    	</edmx:DataServices>
    </edmx:Edmx>

    Save everything, place the cursor on your project root and click run. In case a popup appears select flpSandbox.html. The test launchpad of the Webide opens up and you can start your application. You should get a result like this:


    Even the selections should already work except the first defaultSearch Field.

  6. Add the implementation for defaultSearch.

    The field will be used for the selection on User. To achieve this, coding needs to be added to the DPC_EXT class of the gateway project. Basically, the parameter search_string from the oDATA call which is passed from the first default search field in the Fiori UI needs to be passed to the search help selections. Here is the coding:

    class ZCL_ZSM04_DPC_EXT definition
      public
      inheriting from ZCL_ZSM04_DPC
      create public .
    
    public section.
      methods /IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_ENTITYSET
        redefinition .
      methods /IWBEP/IF_SB_GENDPC_SHLP_DATA~GET_SEARCH_HELP_VALUES
        redefinition .
    protected section.
      data MV_SEARCH_STRING type STRING .
    private section.
    ENDCLASS.
    
    CLASS ZCL_ZSM04_DPC_EXT IMPLEMENTATION.
     METHOD /iwbep/if_mgw_appl_srv_runtime~get_entityset.
        mv_search_string = iv_search_string . "save the default search for later use
        TRY.
            CALL METHOD super->/iwbep/if_mgw_appl_srv_runtime~get_entityset
              EXPORTING
                iv_entity_name           = iv_entity_name
                iv_entity_set_name       = iv_entity_set_name
                iv_source_name           = iv_source_name
                it_filter_select_options = it_filter_select_options
                it_order                 = it_order
                is_paging                = is_paging
                it_navigation_path       = it_navigation_path
                it_key_tab               = it_key_tab
                iv_filter_string         = iv_filter_string
                iv_search_string         = iv_search_string
                io_tech_request_context  = io_tech_request_context
              IMPORTING
                er_entityset             = er_entityset
                es_response_context      = es_response_context.
          CATCH /iwbep/cx_mgw_busi_exception .
          CATCH /iwbep/cx_mgw_tech_exception .
        ENDTRY.
      ENDMETHOD.
    
      METHOD /iwbep/if_sb_gendpc_shlp_data~get_search_help_values.
        DATA: lt_selopt TYPE ddshselops .
        lt_selopt[] = it_selopt[] .
    * map the default search field to UserName
        IF NOT mv_search_string IS INITIAL.
          APPEND INITIAL LINE TO lt_selopt ASSIGNING FIELD-SYMBOL(<f_sel>).
          <f_sel>-shlpfield = 'USER_NAME' .
          <f_sel>-shlpname = iv_shlp_name .
          <f_sel>-sign = 'I'.
          <f_sel>-low = mv_search_string.
          IF mv_search_string CA '*'.
            <f_sel>-option = 'CP'.
          ELSE.
            <f_sel>-option = 'EQ'.
          ENDIF.
        ENDIF.
        CALL METHOD super->/iwbep/if_sb_gendpc_shlp_data~get_search_help_values
          EXPORTING
            iv_shlp_name      = iv_shlp_name
            iv_maxrows        = 9999
            iv_sort           = space
            iv_call_shlt_exit = 'X'
            it_selopt         = lt_selopt
          IMPORTING
            et_return_list    = et_return_list
            es_message        = es_message.
      ENDMETHOD.
    ENDCLASS.

    The result

    Now the Fiori list report is fully functional. It is easy and convienient to use a DDIC search help which exists since R/3 4.0 as a Fiori list report. It is easier as to write the oDATA service completely by hand. In some cases you might reuse an already existing object which can include ABAP code for the data selection as well. Because of the ABAP coding you can select any source of data and any calculation you might need.

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