Application Development Discussions
Join the discussions or start your own on all things application development, including tools and APIs, programming models, and keeping your skills sharp.
cancel
Showing results for 
Search instead for 
Did you mean: 

Dynamic programming fail: pass table TYPE cds as a parameter

cimerman
Participant
0 Kudos
1,006

Dear experts,

this is driving me mad.

I have several calling programs, which calls one wanna-be generic and reusable class. That class is supposed to select data from different CDS view entities and process them. Therefore I need to let the class know what CDS it is supposed to work with. I thought it would be easy but I could not be more wrong.

EDIT: The error I receive is:

Screenshot 2024-10-08 092624.png

If it is not possible, I would be happy to know why.
Thank you.
Denis
btw - posting a question here is almost that impossible as what I am trying to achieve with ABAP.  Isn't that symptomatic?

 

 

"Call from the main program
************** DEFINITION OF THE STRUCTURE FOR DATA FROM CDS **************

  DATA: t_data_obs TYPE STANDARD TABLE OF zfi_data_obs.  " Internal table for passing the CDS structure to the class


************** INSTANTIATION AND CALLING THE CLASS **************
  DATA: go_consumer     TYPE REF TO zfi_obs_interface_consumer.
  TRY.
      CREATE OBJECT go_consumer
        EXPORTING
          i_where_cond     = where_clause
          i_t_code         = 'ZFI_SEND_PAYMENTS'
          i_cds_view       = 'ZFI_DATA_OBS'
          i_t_data_obs     = t_data_obs
          i_wholedoc       = abap_true  " in transaction ZFI_SEND_PAYMENTS always true, only a whole document can be sent to OBS
          i_hotspot_column = 'DocumentNumber'. " CDS field name for hotspot in ALV
    CATCH cx_static_check INTO DATA(lx_error_static).
      MESSAGE lx_error_static->get_text( ) TYPE 'S' DISPLAY LIKE 'E'.
  ENDTRY.
  
  go_consumer->get_data( ).


" Constructor declaration and implementation, private attributes:
METHODS: constructor IMPORTING i_where_cond     TYPE string "where condition from the calling program
                                   i_t_code         TYPE tcode  "calling transaction
                                   i_t_data_obs     TYPE ANY TABLE
                                   i_cds_view       TYPE string "CDS view entity for selecting data
                                   i_wholedoc       TYPE abap_bool "transfer a complete document only?
                                   i_hotspot_column TYPE string
                                   "me->t_items      = VALUE zfi_data_obs( ).
                         RAISING   cx_static_check.

PRIVATE SECTION.
    " Instance variables.
    DATA: where_cond     TYPE string,
          t_code         TYPE tcode,
          cds_view       TYPE string,
          t_items        TYPE REF TO data,
          wholedoc       TYPE abap_bool,
          hotspot_column TYPE char30,
          o_alv          TYPE REF TO cl_salv_table,
          o_events       TYPE REF TO cl_salv_events_table,
          lx_error       TYPE REF TO cx_root.

CLASS ZFI_OBS_INTERFACE_CONSUMER IMPLEMENTATION.

  METHOD constructor.
    cds_view   = i_cds_view.
    where_cond = i_where_cond.
    hotspot_column = i_hotspot_column.
    
    t_items = i_t_data_obs.
    
*  CREATE DATA t_items LIKE i_t_data_obs.
*  FIELD-SYMBOLS: <fs_items> TYPE STANDARD TABLE.
*  ASSIGN t_items->* TO <fs_items>.
*  <fs_items> = i_t_data_obs.
  ENDMETHOD.


  METHOD get_data.
    SELECT * FROM (cds_view) WHERE (where_cond) INTO TABLE @t_items. " Dynamic select from CDS
    me->t_items = t_items.
    me->prepare_alv( ).

  ENDMETHOD.

 

 

 

 

12 REPLIES 12

ulrich_mhrke
Explorer
941

What is your specific problem? Syntax error? Program dump?

0 Kudos
907

Hello Ulrich,

the problem is in the compatibility and I hope it could be only due to incorrect syntax:
 "The type of "I_T_DATA_OBS" cannot be converted to the type of "ME->T_ITEMS""

cimerman_0-1728372395072.png

cimerman_1-1728372440205.png

In the calling program, I have the i_t_data parameter declared as: t_data_obs TYPE STANDARD TABLE OF zfi_data_obs, which should do the job. I tried explicit typing, too, with no effect. ZFI_DATA_OBS is a CDS view entity.

The constructor is importing the parameter as: i_t_data_obs TYPE ANY TABLE.

The receiving object in the class's private attributes is declared as: t_items TYPE REF TO data (basically the only option, which does not trigger a syntax error).

Any ideas or recommendations, please?

Thank you.

Denis

871

Maybe the following (old style) code gives you an idea how to work with type ref to data.

You already have something like this in a comment within your constructor. Use it within method get_data.

I don't know, if it works with CDS views too.

REPORT ytmp.

DATA:
      t_item TYPE REF TO data.

DATA: ddic_name TYPE string VALUE 'BKPF'.

PERFORM get_data.
PERFORM output.

FORM get_data.

  CREATE DATA t_item TYPE STANDARD TABLE OF (ddic_name).

  FIELD-SYMBOLS: <t_item> TYPE STANDARD TABLE.
  ASSIGN t_item->* TO <t_item>.

  SELECT * UP TO 10 ROWS FROM (ddic_name) INTO TABLE <t_item>.

ENDFORM.

FORM output.
  FIELD-SYMBOLS: <t_item> TYPE STANDARD TABLE.
  ASSIGN t_item->* TO <t_item>.

  LOOP AT <t_item> ASSIGNING FIELD-SYMBOL(<item>).
    SKIP.
    DO.
      ASSIGN COMPONENT sy-index OF STRUCTURE <item> TO FIELD-SYMBOL(<cell>).
      IF sy-subrc NE 0.
        EXIT. " leave do
      ENDIF.
      WRITE: / <cell>.
    ENDDO.
  ENDLOOP.

ENDFORM.

 

0 Kudos
832

Thank you for the suggestion, Ulrich. Yes, you can see it right - I tried this already and working all the way long with FS is a solution. Nevertheless, the error was the same, when I tried to assign the imported table to the reference: "The type of "I_T_DATA_OBS" cannot be converted to the type of "ME->T_ITEMS"." 

There is something which makes internal table and CDS view incompatible, although the table is typed by the CDS, and I need to know what it is, or I cannot sleep.

Sandra_Rossi
Active Contributor
0 Kudos
909

For any question about programming issues: what do you get, what do you expect, what did you try?

0 Kudos
885

You are right, Sandra. Frustrated in the middle of a night, I did not consider this.

I get error: "The type of "I_T_DATA_OBS" cannot be converted to the type of "ME->T_ITEMS" and "T_ITEMS is not an internal table".

I expect: pass atable  structure corresponding to the structure of a CDS view entity as a parameter, so that I can populate it with data of that CDS in the receiving class.

I tried: declaring t_data_obs in the calling program as TYPE STANDARD TABLE, as table, and explicitely. I tried, desperate, also this in the receiving class:

*  CREATE DATA t_items LIKE i_t_data_obs.
*  FIELD-SYMBOLS: <fs_items> TYPE STANDARD TABLE.
*  ASSIGN t_items->* TO <fs_items>.
*  <fs_items> = i_t_data_obs.

I assume that the problem lies in declaring the receiving structure as a reference to data in the private attributes:

t_items TYPE REF TO data,

and I hope someone can give me a recommendation for the correct declaration.

Regards,

Denis

Sandra_Rossi
Active Contributor
869

problem:

METHODS ... IMPORTING ... i_t_data_obs TYPE ANY TABLE ...
...
DATA t_items TYPE REF TO data.
...
t_items = i_t_data_obs. <=== fails

solution:

" store a pointer to the internal table
t_items = REF #( i_t_data_obs ).

(or GET REFERENCE OF ... INTO ... in old ABAP)

 

0 Kudos
826

Many thanks Sandra. Yes, this is it. 
I cannot see how to mark this as a solution. Is this feature in the new forum anymore?

0 Kudos
816

You have posted to Discussions instead of Q&A, so you can't mark any solution. How to ask a question, see here: Solved: How to ask a question in SAP Community >= 2024 - SAP Community.

818

I expect this will lead to a dump if you try to change the data at t_items within the method get_data or another place.

The reason is, that it changes the data at i_t_data_obs too which is not allowed for importing parameters. If you really want this, you can do something like the following. There the table is given as changing parameter to the generic class.

REPORT ytmp.

CLASS lcl_caller DEFINITION.

  PUBLIC SECTION.
    METHODS:
      start.

  PRIVATE SECTION.
    DATA:
          mt_data TYPE STANDARD TABLE OF bkpf.

ENDCLASS.

CLASS lcl_handler DEFINITION.

  PUBLIC SECTION.
    METHODS:
      set_data_ref
        IMPORTING
          iv_type TYPE string
        CHANGING
          ct_data TYPE STANDARD TABLE,
      get_data.

  PRIVATE SECTION.
    DATA:
      mr_data TYPE REF TO data,
      mv_type TYPE string.

ENDCLASS.


END-OF-SELECTION.
  NEW lcl_caller( )->start( ).

CLASS lcl_caller IMPLEMENTATION.

  METHOD start.

    DATA(lo_handler) = NEW lcl_handler( ).
    lo_handler->set_data_ref(
      EXPORTING
        iv_type = 'BKPF'
      CHANGING
        ct_data = mt_data
    ).
    lo_handler->get_data( ).


    WRITE: lines( mt_data ).
  ENDMETHOD.

ENDCLASS.

CLASS lcl_handler IMPLEMENTATION.

  METHOD set_data_ref.

    mv_type = iv_type.
    GET REFERENCE OF ct_data INTO mr_data.

  ENDMETHOD.

  METHOD get_data.

    FIELD-SYMBOLS: <lt_data> TYPE STANDARD TABLE.

    ASSIGN mr_data->* TO <lt_data>.

    SELECT * UP TO 10 ROWS FROM (mv_type) INTO TABLE <lt_data>.

  ENDMETHOD.

ENDCLASS.

 

0 Kudos
730

Tested, you are right. It would not even compile, since reference is not an internal table which I could select data into.

What you suggest is working, but I would need to refactor the whole class. 🙂

The most important for me is that I understand the reason why it_table TYPE STANDARD TABLE of CDS_view_entity is not compatible with the CDS_view_entity, when passed as a parameter. It IS compatible, in fact, but there is no way to declare the target variable dynamically as a table, at least not any I know.

I am tempted to try with RTTS now, but I need to move on and I will select the data in the calling program. I will get back to this problem later, and if I find a simple solution, I will let you know.

Thank you for the discussion!

cimerman
Participant
521

Dear @Sandra_Rossi @ulrich_mhrke ,

as promised, I am coming back with the solution. The key was that, although it might seem suprising, it is possible in ABAP to type data from a variable when you use dynamic data type creation using CREATE DATA.

Situation: multiple programs calling the same generic class, passing a table name and data. The structure of tables and data differ program by program. The class displays the data in ALV (among others).

Problem:

  1. I need to declare receiving structure in the class as TYPE REF TO DATA, that is the only option. But assigning a reference or internal table to generic data type leads to an types mismatch error.
  2. I cannot use RTTS to type (describe) the internal table in the class, because the structure does not exist in dictionary. In fact, the source of data is a SELECT from a CDS entity view.
  3. I need to pass an internal table to CL_SALV_TABLE=>FACTORY CHANGING parameter, not a reference (field symbol).

Solution:

  1. Pass data and table name separately. We will end up playing with two field symbols, one for data, another for table.
  2. Wrap data to a reference and pass it to the constructor.
  3. Create a data reference for an internal table based on the provided name: CREATE DATA it_table TYPE TABLE OF (lv_name).
  4.  Create an intermediate work area that points to the dynamically created internal table: ASSIGN it_table->* TO FIELD-SYMBOL(<fs_it_table>).
  5. Do the same with the data: ASSIGN data->* TO <fs_data>.
  6. Use APPEND LINES OF <fs_data> TO <fs_it_table> statement to copy data from the source to the dynamically created internal table.
  7. Simply dereference the table when passing it to ALV factory.
"Calling program

DATA: go_test TYPE REF TO z_fi_interface_dynamic,
      lr_data_ref TYPE REF TO data.

" Create a data reference for the internal table t_data_obs
GET REFERENCE OF t_data_obs INTO lr_data_ref.

  TRY.
    CREATE OBJECT go_test
        EXPORTING
            data = lr_data_ref " since t_data_obs is a standard internal table, we need to wrap it in a data reference
            name = 'ZFI_DATA_OBS'.
    CATCH cx_static_check INTO DATA(lx_error_static_test).
      MESSAGE lx_error_static_test->get_text( ) TYPE 'S' DISPLAY LIKE 'E'.
  ENDTRY.

  go_test->display_alv( ).
"Generic class

CLASS z_fi_interface_dynamic DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    METHODS: constructor
      IMPORTING
        VALUE(name) TYPE string
        VALUE(data) TYPE REF TO data,
      display_alv.
  PRIVATE SECTION.
    DATA: it_table TYPE REF TO data, " Generic data reference for dynamic typing
          lv_name  TYPE string,
          o_alv    TYPE REF TO cl_salv_table.
ENDCLASS.

CLASS z_fi_interface_dynamic IMPLEMENTATION.
  METHOD constructor.
    lv_name = name.

    " Create a data reference for an internal table based on the provided name
    CREATE DATA it_table TYPE TABLE OF (lv_name).

    " Assign the data reference to a field symbol for dynamic access
    FIELD-SYMBOLS: <fs_it_table> TYPE STANDARD TABLE,
                   <fs_data>     TYPE STANDARD TABLE.

    ASSIGN it_table->* TO <fs_it_table>.
    ASSIGN data->* TO <fs_data>.

    " Make sure both field symbols are assigned before proceeding
    IF sy-subrc = 0.
      " Append the lines of the provided data to the newly created internal table
      APPEND LINES OF <fs_data> TO <fs_it_table>.
    ENDIF.
  ENDMETHOD.

  METHOD display_alv.
    TRY.
        cl_salv_table=>factory(
          IMPORTING r_salv_table = o_alv
          CHANGING  t_table      = it_table->* ).

        " Display the ALV
        o_alv->display( ).

      CATCH cx_salv_msg INTO DATA(lx_salv_msg).
        MESSAGE lx_salv_msg->get_text( ) TYPE 'E'.
    ENDTRY.
  ENDMETHOD.
ENDCLASS.