Tuesday 4 June 2019

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:

SAP Fiori, ABAP Development, SAPUI5, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

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

SAP Fiori, ABAP Development, SAPUI5, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

◈ Define the entity type name ZZSM04 in the following wizard:

SAP Fiori, ABAP Development, SAPUI5, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

◈ Select the upper check box to import the entire structure of the Search help:

SAP Fiori, ABAP Development, SAPUI5, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

◈ Select the key fields:

SAP Fiori, ABAP Development, SAPUI5, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

◈ Open the generated Entity Set ZzSm04Set and select the boxes Addressable and Searchable:

SAP Fiori, ABAP Development, SAPUI5, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

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

SAP Fiori, ABAP Development, SAPUI5, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

You can change the labels for the fields later as well via the T Button.

◈ Generate the runtime objects, result should be:

SAP Fiori, ABAP Development, SAPUI5, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

3. Add the service definition in /IWFND/MAINT_SERVICE, result should be:

SAP Fiori, ABAP Development, SAPUI5, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

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:

SAP Fiori, ABAP Development, SAPUI5, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

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.

No comments:

Post a Comment