Skip to Content
Technical Articles
Author's profile photo Andre Fischer

How to generate the DDL source code for custom entities that are implemented by remote function calls

Introduction

SAP CP ABAP environment can serve as a platform to built side-by-side extensions for:

  • SAP S/4HANA Cloud via OData
  • SAP S/4HANA (on-Premises) via OData and RFC
  • SAP Business Suite (on-Premises) via OData (using SAP Gateway) and RFC

To implement such scenarios the ABAP Restful Programming Model offers the option to built CDS views as custom entities.

Using the Annotation @QueryImplementedBy a class is specified that implements the select method of the interface if_a4c_rap_query_provider.

Rather than querying the data from database tables or other CDS views the developer can retrieve the data via OData or RFC calls. In SAP CP ABAP Environment these are remote calls to one of the system types mentioned above.

For an example that uses OData calls see the SAP Online Documentation:

https://help.sap.com/viewer/c0d02c4330c34b3abca88bdd57eaccfc/Cloud/en-US/6a064c09c508435a81357898e8e65d06.html

Blog series

This blog is part of a blog series about developing a side-by-side Extension for on-premise SAP Systems in SAP Cloud Platform ABAP Environment using RFC communication

The challenge

As I wrote in a previous blog How to call a remote function module in your on-premise SAP system from SAP Cloud Platform – ABAP Environment it is now possible to call RFC function modules in an SAP on-premise system from SAP CP ABAP environment.

The problem that a developer faces is however that the SAP CP ABAP environment is an “empty” platform.

Opposed to a SAP Business Suite or SAP S/4HANA System the structures of table or input parameters of BAPI’s are not available.

On the other hand RFC function modules must be called with appropriate structures or internal tables as importing or tables parameters.

The solution

I developed a small class zcl_rfc_custom_entity_helper that you can run in your development system that generates the appropriate DDL source code for a custom entity as well as ABAP source Code for a type defintion based on the basic data Elements available in SAP CP ABAP environment.

I used the following documents from the SAP Online Help.

ABAP DDIC types
https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abenddic_builtin_types.htm
ABAP CDS types
https://help.sap.com/doc/abapdocu_750_index_htm/7.50/en-US/abencds_typing.htm

Please note:

If you use the report you have to replace the value ‘bapi_epm_product_header’ for the variable lv_rfc_input_structure by the name of the DDIC structure you are interested in.

The output for the DDIC struture BAPI_EPM_PRODUCT_HEADER which is used by the BAPI BAPI_EPM_PRODUCT_GET_LIST is the following:

//######################### DDL source code ################################
// DDL source code for custom entity for BAPI_EPM_PRODUCT_HEADER
// generated on: 20190301 at: 140243 in: UIA 
ProductId : abap.char( 10 ) ; 
TypeCode : abap.char( 2 ) ; 
Category : abap.sstring( 40 ) ; 
Name : abap.sstring( 255 ) ; 
Description : abap.sstring( 255 ) ; 
SupplierId : abap.char( 10 ) ; 
SupplierName : abap.sstring( 80 ) ; 
TaxTarifCode : abap.int1 ; 
@Semantics.unitOfMeasure: true
MeasureUnit : abap.unit( 3 ) ; 
@Semantics.quantity.unitOfMeasure: 'WeightUnit' 
WeightMeasure : abap.quan( 13, 3 ) ; 
@Semantics.unitOfMeasure: true
WeightUnit : abap.unit( 3 ) ; 
Price : abap.dec( 23, 4 ) ; 
@Semantics.currencyCode: true
CurrencyCode : abap.cuky( 5 ) ; 
@Semantics.quantity.unitOfMeasure: 'DimUnit' 
Width : abap.quan( 13, 3 ) ; 
@Semantics.quantity.unitOfMeasure: 'DimUnit' 
Depth : abap.quan( 13, 3 ) ; 
@Semantics.quantity.unitOfMeasure: 'DimUnit' 
Height : abap.quan( 13, 3 ) ; 
@Semantics.unitOfMeasure: true
DimUnit : abap.unit( 3 ) ; 
ProductPicUrl : abap.char( 255 ) ; 

 

"######################### ABAP source code ################################
" ABAP source code for type definition for BAPI_EPM_PRODUCT_HEADER
" generated on: 20190301 at: 140243 in: UIA 
TYPES : BEGIN OF ty_BAPI_EPM_PRODUCT_HEADER, 
ProductId TYPE c LENGTH 10, 
TypeCode TYPE c LENGTH 2, 
Category TYPE c LENGTH 40, 
Name TYPE c LENGTH 255, 
Description TYPE c LENGTH 255, 
SupplierId TYPE c LENGTH 10, 
SupplierName TYPE c LENGTH 80, 
TaxTarifCode TYPE int1 , 
MeasureUnit TYPE c LENGTH 3, 
WeightMeasure TYPE p LENGTH 7 DECIMALS 3, 
WeightUnit TYPE c LENGTH 3, 
Price TYPE p LENGTH 12 DECIMALS 4 , 
CurrencyCode TYPE c LENGTH 5, 
Width TYPE p LENGTH 7 DECIMALS 3, 
Depth TYPE p LENGTH 7 DECIMALS 3, 
Height TYPE p LENGTH 7 DECIMALS 3, 
DimUnit TYPE c LENGTH 3, 
ProductPicUrl TYPE c LENGTH 255, 
end OF ty_BAPI_EPM_PRODUCT_HEADER. 

Source code

The source code of this class will be published on GitHub soon.

CLASS zcl_rfc_custom_entity_helper DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.



CLASS zcl_rfc_custom_entity_helper IMPLEMENTATION.

  METHOD if_oo_adt_classrun~main.


***************************************************************************************
* ABAP DDIC types
* https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abenddic_builtin_types.htm
***************************************************************************************
* ABAP CDS types
* https://help.sap.com/doc/abapdocu_750_index_htm/7.50/en-US/abencds_typing.htm
***************************************************************************************
    DATA:
      lo_struct_desc           TYPE REF TO cl_abap_structdescr,
      lo_type_desc             TYPE REF TO cl_abap_typedescr,
      lo_elem_desc             TYPE REF TO cl_abap_elemdescr,
      lt_comp_table            TYPE cl_abap_structdescr=>component_table,
      lv_length                TYPE i,
      lv_p_length              TYPE i,
      lv_decimals              TYPE i,
      lv_abap_cds_data_type    TYPE abap_typename,
      lv_abap_ddic_data_type   TYPE string,
      lv_property              TYPE string,
      ls_ddic_fields           TYPE dfies,
      lv_ddl_source_code_line  TYPE string,
      lt_ddl_source_code       TYPE STANDARD TABLE OF string,
      lv_abap_source_code_line TYPE string,
      lt_abap_source_code      TYPE STANDARD TABLE OF string,
      lv_reffield_abap         TYPE dd03l-reffield,
      lv_refproperty           TYPE string.

    DATA: lv_rfc_input_structure TYPE string VALUE 'bapi_epm_product_header'.

    "name of structure must be in uppercase because otherwise reference fields for currency and unit
    "are not found in DD03L
    lv_rfc_input_structure = to_upper( lv_rfc_input_structure ).

    lo_type_desc =  cl_abap_typedescr=>describe_by_name( lv_rfc_input_structure ).

    "check that the object is a structure
    TRY.
        IF lo_type_desc->kind = lo_type_desc->kind_struct.
          lo_struct_desc ?= lo_type_desc.
          "this method retrieves the components also from structures with append structures
          /iwbep/cl_mgw_med_model_util=>get_structure_components(
            EXPORTING
              io_structure_descriptor = lo_struct_desc
            CHANGING
              ct_components = lt_comp_table ).
        ELSE.
          "@todo: raise a more meaningful exception here
          RAISE EXCEPTION TYPE /iwbep/cx_mgw_med_exception
            EXPORTING
              textid = /iwbep/cx_mgw_med_exception=>otr_text_read_error.
        ENDIF.

      CATCH /iwbep/cx_mgw_med_exception.
        "handle exception
        EXIT.
    ENDTRY.

    "add a comment that contains the name of the structure being used as input
    lv_ddl_source_code_line = |// DDL source code for custom entity for { lv_rfc_input_structure }|.
    lv_abap_source_code_line = |" ABAP source code for type definition for { lv_rfc_input_structure }|.
    APPEND lv_ddl_source_code_line TO lt_ddl_source_code.
    APPEND lv_abap_source_code_line TO lt_abap_source_code.
    "add a comment that contains information when and where the code has been generated
    lv_ddl_source_code_line = |// generated on: { sy-datum } at: { sy-uzeit } in: { sy-sysid } |.
    lv_abap_source_code_line = |" generated on: { sy-datum } at: { sy-uzeit } in: { sy-sysid } |.
    APPEND lv_ddl_source_code_line TO lt_ddl_source_code.
    APPEND lv_abap_source_code_line TO lt_abap_source_code.

    "first line of TYPES statement
    lv_abap_source_code_line = |TYPES : BEGIN OF ty_{ lv_rfc_input_structure }, | .
    APPEND lv_abap_source_code_line TO lt_abap_source_code.

    LOOP AT lt_comp_table INTO DATA(ls_comp_table)  .

      "only elements
      IF ls_comp_table-type->kind <> cl_abap_typedescr=>kind_elem.
        CONTINUE.
      ENDIF.

      lo_elem_desc  ?= ls_comp_table-type.

      lo_elem_desc->get_ddic_field(
          EXPORTING
              p_langu      = sy-langu
          RECEIVING
              p_flddescr   = ls_ddic_fields
          EXCEPTIONS
              not_found    = 1
              no_ddic_type = 2
              OTHERS       = 3
              ).


      lv_property = to_mixed( val = ls_comp_table-name sep = '_' ).
      lv_length = ls_ddic_fields-leng.
      lv_p_length = lv_length DIV 2 + 1.
      lv_decimals = ls_ddic_fields-decimals.

      "add the ABAP field name of the structure as a comment
      "lv_ddl_source_code_line = |// { ls_comp_table-name } |.
      "APPEND lv_ddl_source_code_line TO lt_ddl_source_code.

      "first check for the domain name
      CASE ls_ddic_fields-domname.

        WHEN 'TZNTSTMPL' OR 'TZNTSTMPSL' OR 'TZNTSTMPS' OR 'TIMESTAMP_CHAR'.
          lv_ddl_source_code_line  = |{ lv_property } : timestampl; | .
          lv_abap_source_code_line = |{ lv_property } TYPE timestampl , | .
        WHEN 'TZNTIMELOC'.
          lv_abap_cds_data_type = 'tims'.
          lv_abap_ddic_data_type = ls_ddic_fields-domname.
          lv_ddl_source_code_line = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
          lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
        WHEN OTHERS.

          "if none of the obvious domain names is found check the data type
          CASE ls_ddic_fields-datatype.

            WHEN 'CHAR'.
              "if char is used sap.displayformat = uppercase is enforced
              IF ls_ddic_fields-lowercase = abap_true.
                lv_abap_cds_data_type = 'sstring'.
                lv_abap_ddic_data_type = 'c'.
              ELSE.
                lv_abap_cds_data_type = 'char'.
                lv_abap_ddic_data_type = 'c'.
              ENDIF.
              lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length } ) ; | .
              lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_length }, | .
            WHEN 'CLNT'.
              lv_abap_cds_data_type = 'clnt'.
              lv_abap_ddic_data_type = 'c'.
              lv_p_length = 3.
              lv_ddl_source_code_line = |//{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
              lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_p_length }, | .
            WHEN  'CUKY'.
              lv_ddl_source_code_line = '@Semantics.currencyCode: true'.
              APPEND lv_ddl_source_code_line TO lt_ddl_source_code.

              lv_abap_cds_data_type = 'cuky'.
              lv_abap_ddic_data_type = 'c'.
              lv_p_length = 5.
              lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length } ) ; | .
              lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_p_length }, | .
            WHEN  'CURR'.
              SELECT SINGLE reffield INTO lv_reffield_abap FROM dd03l WHERE tabname = lv_rfc_input_structure AND fieldname = ls_comp_table-name.
              IF lv_reffield_abap IS NOT INITIAL.
                lv_refproperty = to_mixed( val = lv_reffield_abap sep = '_' ).
              ELSE.
                lv_refproperty = '<enter property name>'.
              ENDIF.
              lv_ddl_source_code_line = |@Semantics.amount.currencyCode: '{ lv_refproperty }' |.
              APPEND lv_ddl_source_code_line TO lt_ddl_source_code.

              lv_abap_cds_data_type = 'curr'.
              lv_abap_ddic_data_type = 'p'.
              lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length }, { lv_decimals } ) ; | .
              lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_p_length } DECIMALS { lv_decimals } , | .

            WHEN  'DATS' .
              lv_abap_cds_data_type = 'dats'.
              lv_abap_ddic_data_type = 'dats'.
              lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
              lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
            WHEN  'DEC'.
              lv_abap_cds_data_type = 'dec'.
              lv_abap_ddic_data_type = 'p'.
              lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length }, { lv_decimals } ) ; | .
              lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_p_length } DECIMALS { lv_decimals } , | .
            WHEN  'FLTP'.
              lv_abap_cds_data_type = 'fltp'.
              lv_abap_ddic_data_type = 'f'.
              lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
              lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
            WHEN  'INT1'.
              lv_abap_cds_data_type = 'int1'.
              lv_abap_ddic_data_type = 'int1'.
              lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
              lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
            WHEN  'INT2'.
              lv_abap_cds_data_type = 'int2'.
              lv_abap_ddic_data_type = 's'.
              lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
              lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
            WHEN  'INT4'.
              lv_abap_cds_data_type = 'int4'.
              lv_abap_ddic_data_type = 'i'.
              lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
              lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
            WHEN  'INT8'.
              lv_abap_cds_data_type = 'int8'.
              lv_abap_ddic_data_type = 'int8'.
              lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
              lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
            WHEN  'NUMC'.
              lv_abap_cds_data_type = 'numc'.
              lv_abap_ddic_data_type = 'n'.
              lv_ddl_source_code_line = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length } ) ; | .
              lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_length }, | .
            WHEN  'QUAN'.
              SELECT SINGLE reffield INTO lv_reffield_abap FROM dd03l WHERE tabname = lv_rfc_input_structure AND fieldname = ls_comp_table-name.
              IF lv_reffield_abap IS NOT INITIAL.
                lv_refproperty = to_mixed( val = lv_reffield_abap sep = '_' ).
              ELSE.
                lv_refproperty = '<enter property name>'.
              ENDIF.
              lv_ddl_source_code_line = |@Semantics.quantity.unitOfMeasure: '{ lv_refproperty }' |.
              APPEND lv_ddl_source_code_line TO lt_ddl_source_code.
              lv_abap_cds_data_type = 'quan'.
              lv_abap_ddic_data_type = 'p'.
              lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length }, { lv_decimals } ) ; | .
              lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_p_length } DECIMALS { lv_decimals }, | .
            WHEN  'STRG'.
              lv_abap_cds_data_type = 'string'.
              lv_abap_ddic_data_type = 'string'.
              lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length } ) ; | .
              lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
            WHEN  'DATS' .
              lv_abap_cds_data_type = 'dats'.
              lv_abap_ddic_data_type = 'd'.
              lv_ddl_source_code_line = |{ lv_property } : timestampl; | .
              lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
            WHEN 'TIMS'.
              lv_abap_cds_data_type = 'tims'.
              lv_abap_ddic_data_type = 't'.
              lv_ddl_source_code_line = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
              lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
            WHEN  'UNIT'.
              lv_ddl_source_code_line = '@Semantics.unitOfMeasure: true'.
              APPEND lv_ddl_source_code_line TO lt_ddl_source_code.
              lv_abap_cds_data_type = 'unit'.
              lv_abap_ddic_data_type = 'c'.
              lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length } ) ; | .
              lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_length }, | .
            WHEN OTHERS.
              lv_ddl_source_code_line  = |// { ls_comp_table-name } could not be mapped to a standard data element.|.
              lv_abap_source_code_line = |" { ls_comp_table-name } could not be mapped to a standard data element.|.

          ENDCASE.

      ENDCASE.

      APPEND lv_ddl_source_code_line TO lt_ddl_source_code.
      APPEND lv_abap_source_code_line TO lt_abap_source_code.

    ENDLOOP.

    "last line of TYPES statement
    lv_abap_source_code_line = |end OF ty_{ lv_rfc_input_structure }. | .
    APPEND lv_abap_source_code_line TO lt_abap_source_code.

    out->write( |//######################### DDL source code ################################| ).

    LOOP AT lt_ddl_source_code INTO lv_ddl_source_code_line.
      out->write( lv_ddl_source_code_line ).
    ENDLOOP.

    out->write( |"######################### ABAP source code ################################| ).

    LOOP AT lt_abap_source_code INTO lv_abap_source_code_line.
      out->write( lv_abap_source_code_line ).
    ENDLOOP.

  ENDMETHOD.

ENDCLASS.

 

Assigned Tags

      5 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Marc van Driel
      Marc van Driel

      Thanks for this great series, very useful step-by-step guide.

      To get the same information from an on premise SAP ERP system I had to make some adaptations. I made some minor changes so that I could do everything from the ABAP Cloud system by calling the code through RFC using an adaption of your RFC code from the first article in your series. I’ve attached my code.

       

      Class to execute RFC

      class Z_CL_RFC_CUSTOM_ENTITY_HELPER definition
        public
        final
        create public .
      
      public section.
      interfaces if_oo_adt_classrun.
      protected section.
      private section.
      ENDCLASS.
      
      
      CLASS Z_CL_RFC_CUSTOM_ENTITY_HELPER IMPLEMENTATION.
      
      METHOD IF_OO_ADT_CLASSRUN~MAIN.
      
      DATA: lv_ddl_source TYPE string,
            lv_abap_source TYPE string.
      
      TRY.
      
      DATA(lo_rfc_dest) = cl_rfc_destination_provider=>create_by_cloud_destination(
                                     i_name = |S4H_ON_PREM_RFC|
                                     i_service_instance_name = |OutboundCommunication| ).
      DATA(lv_rfc_dest) = lo_rfc_dest->get_destination_name( ).
      
      CALL FUNCTION 'Z_RFC_CUSTOM_ENTITY_HELPER' DESTINATION lv_rfc_dest
         EXPORTING
               IM_RFC_INPUT_STRUCTURE = 'bapi_epm_product_header'
      
         IMPORTING
               EX_DDL_SOURCE    = lv_ddl_source
               EX_ABAP_SOURCE   = lv_abap_source
         EXCEPTIONS
               entity_not_found = 1
               OTHERS           = 2.
      
      CASE sy-subrc.
                WHEN 0.
                  out->write( |DDL Source: { lv_ddl_source  }.| ).
                  out->write( |ABAP Source: { lv_abap_source  }.| ).
                WHEN 1.
                  out->write( |EXCEPTION ENITY_NOT_FOUND| ).
                WHEN 2.
                  out->write( |EXCEPTION OTHERS| ).
              ENDCASE.
      
            CATCH cx_root INTO DATA(lx_root).
              out->write( lx_root->get_text( ) ).
          ENDTRY.
      
        ENDMETHOD.
      
      ENDCLASS.

       

      Function module in on prem system (remote enabled, with exception ENTITY_NOT_FOUND)

      FUNCTION Z_RFC_CUSTOM_ENTITY_HELPER
        IMPORTING
          VALUE(IM_RFC_INPUT_STRUCTURE) TYPE STRING
        EXPORTING
          VALUE(EX_DDL_SOURCE) TYPE STRING
          VALUE(EX_ABAP_SOURCE) TYPE STRING
        EXCEPTIONS
          ENTITY_NOT_FOUND.
      
      
      
      ***************************************************************************************
      * ABAP DDIC types
      * https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abenddic_builtin_types.htm
      ***************************************************************************************
      * ABAP CDS types
      * https://help.sap.com/doc/abapdocu_750_index_htm/7.50/en-US/abencds_typing.htm
      ***************************************************************************************
          DATA:
            lo_struct_desc           TYPE REF TO cl_abap_structdescr,
            lo_type_desc             TYPE REF TO cl_abap_typedescr,
            lo_elem_desc             TYPE REF TO cl_abap_elemdescr,
            lt_comp_table            TYPE cl_abap_structdescr=>component_table,
            lv_length                TYPE i,
            lv_p_length              TYPE i,
            lv_decimals              TYPE i,
            lv_abap_cds_data_type    TYPE abap_typename,
            lv_abap_ddic_data_type   TYPE string,
            lv_property              TYPE string,
            ls_ddic_fields           TYPE dfies,
            lv_ddl_source_code_line  TYPE string,
            lt_ddl_source_code       TYPE STANDARD TABLE OF string,
            lv_abap_source_code_line TYPE string,
            lt_abap_source_code      TYPE STANDARD TABLE OF string,
            lv_reffield_abap         TYPE dd03l-reffield,
            lv_refproperty           TYPE string,
            cr_lf(2)                 TYPE c VALUE cl_abap_char_utilities=>cr_lf.
      
      *    DATA: lv_rfc_input_structure TYPE string VALUE 'bapi_epm_product_header'.
          DATA: lv_rfc_input_structure TYPE string.
          lv_rfc_input_structure = IM_RFC_INPUT_STRUCTURE.
          "name of structure must be in uppercase because otherwise reference fields for currency and unit
          "are not found in DD03L
          lv_rfc_input_structure = to_upper( lv_rfc_input_structure ).
      
          lo_type_desc =  cl_abap_typedescr=>describe_by_name( lv_rfc_input_structure ).
      
          "check that the object is a structure
          TRY.
              IF lo_type_desc->kind = lo_type_desc->kind_struct.
                lo_struct_desc ?= lo_type_desc.
                "this method retrieves the components also from structures with append structures
                /iwbep/cl_mgw_med_model_util=>get_structure_components(
                  EXPORTING
                    io_structure_descriptor = lo_struct_desc
                  CHANGING
                    ct_components = lt_comp_table ).
              ELSE.
                "@todo: raise a more meaningful exception here
      *          RAISE EXCEPTION TYPE /iwbep/cx_mgw_med_exception
      *            EXPORTING
      *              textid = /iwbep/cx_mgw_med_exception=>ddic_element_not_found.
                RAISE entity_not_found.
              ENDIF.
      
            CATCH /iwbep/cx_mgw_med_exception.
              "handle exception
              RAISE entity_not_found.
      *        EXIT.
          ENDTRY.
      
          "add a comment that contains the name of the structure being used as input
          lv_ddl_source_code_line = |// DDL source code for custom entity for { lv_rfc_input_structure }|.
          lv_abap_source_code_line = |" ABAP source code for type definition for { lv_rfc_input_structure }|.
          APPEND lv_ddl_source_code_line TO lt_ddl_source_code.
          APPEND lv_abap_source_code_line TO lt_abap_source_code.
          "add a comment that contains information when and where the code has been generated
          lv_ddl_source_code_line = |// generated on: { sy-datum } at: { sy-uzeit } in: { sy-sysid } |.
          lv_abap_source_code_line = |" generated on: { sy-datum } at: { sy-uzeit } in: { sy-sysid } |.
          APPEND lv_ddl_source_code_line TO lt_ddl_source_code.
          APPEND lv_abap_source_code_line TO lt_abap_source_code.
      
          "first line of TYPES statement
          lv_abap_source_code_line = |TYPES : BEGIN OF ty_{ lv_rfc_input_structure }, | .
          APPEND lv_abap_source_code_line TO lt_abap_source_code.
      
          LOOP AT lt_comp_table INTO DATA(ls_comp_table)  .
      
            "only elements
            IF ls_comp_table-type->kind <> cl_abap_typedescr=>kind_elem.
              CONTINUE.
            ENDIF.
      
            lo_elem_desc  ?= ls_comp_table-type.
      
            lo_elem_desc->get_ddic_field(
                EXPORTING
                    p_langu      = sy-langu
                RECEIVING
                    p_flddescr   = ls_ddic_fields
                EXCEPTIONS
                    not_found    = 1
                    no_ddic_type = 2
                    OTHERS       = 3
                    ).
      
      
            lv_property = to_mixed( val = ls_comp_table-name sep = '_' ).
            lv_length = ls_ddic_fields-leng.
            lv_p_length = lv_length DIV 2 + 1.
            lv_decimals = ls_ddic_fields-decimals.
      
            "add the ABAP field name of the structure as a comment
            "lv_ddl_source_code_line = |// { ls_comp_table-name } |.
            "APPEND lv_ddl_source_code_line TO lt_ddl_source_code.
      
            "first check for the domain name
            CASE ls_ddic_fields-domname.
      
              WHEN 'TZNTSTMPL' OR 'TZNTSTMPSL' OR 'TZNTSTMPS' OR 'TIMESTAMP_CHAR'.
                lv_ddl_source_code_line  = |{ lv_property } : timestampl; | .
                lv_abap_source_code_line = |{ lv_property } TYPE timestampl , | .
              WHEN 'TZNTIMELOC'.
                lv_abap_cds_data_type = 'tims'.
                lv_abap_ddic_data_type = ls_ddic_fields-domname.
                lv_ddl_source_code_line = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
                lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
              WHEN OTHERS.
      
                "if none of the obvious domain names is found check the data type
                CASE ls_ddic_fields-datatype.
      
                  WHEN 'CHAR'.
                    "if char is used sap.displayformat = uppercase is enforced
                    IF ls_ddic_fields-lowercase = abap_true.
                      lv_abap_cds_data_type = 'sstring'.
                      lv_abap_ddic_data_type = 'c'.
                    ELSE.
                      lv_abap_cds_data_type = 'char'.
                      lv_abap_ddic_data_type = 'c'.
                    ENDIF.
                    lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length } ) ; | .
                    lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_length }, | .
                  WHEN 'CLNT'.
                    lv_abap_cds_data_type = 'clnt'.
                    lv_abap_ddic_data_type = 'c'.
                    lv_p_length = 3.
                    lv_ddl_source_code_line = |//{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
                    lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_p_length }, | .
                  WHEN  'CUKY'.
                    lv_ddl_source_code_line = '@Semantics.currencyCode: true'.
                    APPEND lv_ddl_source_code_line TO lt_ddl_source_code.
      
                    lv_abap_cds_data_type = 'cuky'.
                    lv_abap_ddic_data_type = 'c'.
                    lv_p_length = 5.
                    lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length } ) ; | .
                    lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_p_length }, | .
                  WHEN  'CURR'.
                    SELECT SINGLE reffield INTO lv_reffield_abap FROM dd03l WHERE tabname = lv_rfc_input_structure AND fieldname = ls_comp_table-name.
                    IF lv_reffield_abap IS NOT INITIAL.
                      lv_refproperty = to_mixed( val = lv_reffield_abap sep = '_' ).
                    ELSE.
                      lv_refproperty = '<enter property name>'.
                    ENDIF.
                    lv_ddl_source_code_line = |@Semantics.amount.currencyCode: '{ lv_refproperty }' |.
                    APPEND lv_ddl_source_code_line TO lt_ddl_source_code.
      
                    lv_abap_cds_data_type = 'curr'.
                    lv_abap_ddic_data_type = 'p'.
                    lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length }, { lv_decimals } ) ; | .
                    lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_p_length } DECIMALS { lv_decimals } , | .
      
                  WHEN  'DATS' .
                    lv_abap_cds_data_type = 'dats'.
                    lv_abap_ddic_data_type = 'dats'.
                    lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
                    lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
                  WHEN  'DEC'.
                    lv_abap_cds_data_type = 'dec'.
                    lv_abap_ddic_data_type = 'p'.
                    lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length }, { lv_decimals } ) ; | .
                    lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_p_length } DECIMALS { lv_decimals } , | .
                  WHEN  'FLTP'.
                    lv_abap_cds_data_type = 'fltp'.
                    lv_abap_ddic_data_type = 'f'.
                    lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
                    lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
                  WHEN  'INT1'.
                    lv_abap_cds_data_type = 'int1'.
                    lv_abap_ddic_data_type = 'int1'.
                    lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
                    lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
                  WHEN  'INT2'.
                    lv_abap_cds_data_type = 'int2'.
                    lv_abap_ddic_data_type = 's'.
                    lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
                    lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
                  WHEN  'INT4'.
                    lv_abap_cds_data_type = 'int4'.
                    lv_abap_ddic_data_type = 'i'.
                    lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
                    lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
                  WHEN  'INT8'.
                    lv_abap_cds_data_type = 'int8'.
                    lv_abap_ddic_data_type = 'int8'.
                    lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
                    lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
                  WHEN  'NUMC'.
                    lv_abap_cds_data_type = 'numc'.
                    lv_abap_ddic_data_type = 'n'.
                    lv_ddl_source_code_line = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length } ) ; | .
                    lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_length }, | .
                  WHEN  'QUAN'.
                    SELECT SINGLE reffield INTO lv_reffield_abap FROM dd03l WHERE tabname = lv_rfc_input_structure AND fieldname = ls_comp_table-name.
                    IF lv_reffield_abap IS NOT INITIAL.
                      lv_refproperty = to_mixed( val = lv_reffield_abap sep = '_' ).
                    ELSE.
                      lv_refproperty = '<enter property name>'.
                    ENDIF.
                    lv_ddl_source_code_line = |@Semantics.quantity.unitOfMeasure: '{ lv_refproperty }' |.
                    APPEND lv_ddl_source_code_line TO lt_ddl_source_code.
                    lv_abap_cds_data_type = 'quan'.
                    lv_abap_ddic_data_type = 'p'.
                    lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length }, { lv_decimals } ) ; | .
                    lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_p_length } DECIMALS { lv_decimals }, | .
                  WHEN  'STRG'.
                    lv_abap_cds_data_type = 'string'.
                    lv_abap_ddic_data_type = 'string'.
                    lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length } ) ; | .
                    lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
                  WHEN  'DATS' .
                    lv_abap_cds_data_type = 'dats'.
                    lv_abap_ddic_data_type = 'd'.
                    lv_ddl_source_code_line = |{ lv_property } : timestampl; | .
                    lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
                  WHEN 'TIMS'.
                    lv_abap_cds_data_type = 'tims'.
                    lv_abap_ddic_data_type = 't'.
                    lv_ddl_source_code_line = |{ lv_property } : abap.{ lv_abap_cds_data_type } ; | .
                    lv_abap_source_code_line = |{ lv_property }   TYPE { lv_abap_ddic_data_type } , | .
                  WHEN  'UNIT'.
                    lv_ddl_source_code_line = '@Semantics.unitOfMeasure: true'.
                    APPEND lv_ddl_source_code_line TO lt_ddl_source_code.
                    lv_abap_cds_data_type = 'unit'.
                    lv_abap_ddic_data_type = 'c'.
                    lv_ddl_source_code_line  = |{ lv_property } : abap.{ lv_abap_cds_data_type }( { lv_length } ) ; | .
                    lv_abap_source_code_line = |{ lv_property }  TYPE { lv_abap_ddic_data_type } LENGTH { lv_length }, | .
                  WHEN OTHERS.
                    lv_ddl_source_code_line  = |// { ls_comp_table-name } could not be mapped to a standard data element.|.
                    lv_abap_source_code_line = |" { ls_comp_table-name } could not be mapped to a standard data element.|.
      
                ENDCASE.
      
            ENDCASE.
      
            APPEND lv_ddl_source_code_line TO lt_ddl_source_code.
            APPEND lv_abap_source_code_line TO lt_abap_source_code.
      
          ENDLOOP.
      
          "last line of TYPES statement
          lv_abap_source_code_line = |end OF ty_{ lv_rfc_input_structure }. | .
          APPEND lv_abap_source_code_line TO lt_abap_source_code.
      
      *    write / |//######################### DDL source code ################################| .
          ex_ddl_source = |//######################### DDL source code ################################\n| .
      
      
          LOOP AT lt_ddl_source_code INTO lv_ddl_source_code_line.
      *      write /  lv_ddl_source_code_line .
            CONCATENATE ex_ddl_source lv_ddl_source_code_line INTO ex_ddl_source SEPARATED BY cr_lf.
      
          ENDLOOP.
      
      *    write / |"######################### ABAP source code ################################| .
          ex_abap_source = |######################### ABAP source code ################################\n| .
      
          LOOP AT lt_abap_source_code INTO lv_abap_source_code_line.
      *      write / lv_abap_source_code_line .
       CONCATENATE ex_abap_source lv_abap_source_code_line INTO ex_abap_source SEPARATED BY cr_lf.
      
          ENDLOOP.
      
      
      ENDFUNCTION.

       

       

      Author's profile photo Yevgen Trukhin
      Yevgen Trukhin

      Dats data type didnt work for me. Not on whitelist, I had to revert to using 'd' basic datatype for dates.

      Thanks Yevgen

      Author's profile photo Shani Niza
      Shani Niza

      Hi AndreFisher and Marc Van Driel

      Thanks, This is be very useful. Tried using this and worked fine for me.

      I am able to convert a structure in Onpremise to ABAP environment basic datatypes  in Eclipse using RFC.

       

      Author's profile photo Emily Rascons
      Emily Rascons

      Thanks for this great blog, very useful step-by-step guide.

       

      Author's profile photo Gregor Wolf
      Gregor Wolf

      Hi André,

      you mention:

      The source code of this class will be published on GitHub soon.

      was that already done?

      Best Regards
      Gregor