2024 Oct 07 10:21 PM - edited 2024 Oct 08 8:54 AM
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:
"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.
2024 Oct 08 6:29 AM
2024 Oct 08 8:35 AM
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""
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
2024 Oct 08 9:28 AM
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.
2024 Oct 08 9:50 AM
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.
2024 Oct 08 8:23 AM
For any question about programming issues: what do you get, what do you expect, what did you try?
2024 Oct 08 8:51 AM
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
2024 Oct 08 9:32 AM - edited 2024 Oct 08 9:33 AM
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)
2024 Oct 08 9:56 AM
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?
2024 Oct 08 10:21 AM
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.
2024 Oct 08 10:18 AM
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.
2024 Oct 08 1:39 PM
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!
2024 Oct 13 2:49 PM
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:
Solution:
"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.