Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
koolspy_ultimate
Active Contributor
1,913
Introduction: The requirement was to capture the change logs of configuration data along with the respective descriptions (mostly from text tables), which is currently un-available in SAP directly using standards functionalities. This blog will explain the procedure to get the respective data.

Main Part: This blog can be used as reference for several topics like creating dynamic select query, dynamic table creations, dynamic read statements.

Basically my requirement was to capture all the changes related to configuration tables (similar to change pointer of master data), So I have build the below report which will make use of all the above mentioned dynamic statements.

Initially I created a table to Store the tables list (Lets name is Master Table) which contains the config table names, key fields concatenated by pipe, Description table (can be text table of the actual table containing the text value, Description keys (concatenated by pipe), description text field.

To provide example:

RDPR - (Rounding profile table).

RDTX - (Corresponding text table).

So my ZTABLE would be populated as below


 

Now I have created one more table to capture the actual values with history and indicators, which will look like below.


 

Based on the Master table will query and get the ddic info from DD03L tables and then prepare the dynamic select and get the config tables data.

Now for getting the related description for the values again dynamic select query is prepared based on the inputs form the master tables (Fields: DESC_TAB, DESC_KEYS, DESC_TX_FILE).

Note: Initially when the code is executed data will be inserted into the second data table with indicator as "I".

From the next executions if there is any change in the descriptions then the initial record will be updated as archive = 'X" and new record will be inserted into the data table with indicator as "U".

In case if the data is deleted in the original configuration table, then the same will be updated as new record in the data table with indicator as "D" and the original value in the data will be updated as archive.

 

Conclusion: Once the below code is executed we will be having the required output which consists of the complete list of configuration data along with the change logs and with the respective descriptions.
*&---------------------------------------------------------------------*
*& Report ZGET_CHANGE_LOGS
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT zget_change_logs.


TYPES: BEGIN OF ty_ddic,
tabname TYPE tabname,
fieldname TYPE fieldname,
rollname TYPE rollname,
position TYPE tabfdpos,
keyflag TYPE keyflag,
inttype TYPE inttype,
intlen TYPE intlen,
decimals TYPE decimals,
scrtext_l TYPE scrtext_l,
END OF ty_ddic,

BEGIN OF ty_pre_final,
tabname TYPE tabname,
langu TYPE syst_langu,
field TYPE zgoal_sn_field,
fdesc TYPE zgoal_sn_fdesc,
value TYPE zgoal_sn_value,
val_desc TYPE zgoal_sn_vdesc,
desc_field TYPE fieldname,
udate TYPE ersda,
utime TYPE created_at_time,
sno TYPE zgoal_sn_sno,
chngind TYPE cdchngind,
END OF ty_pre_final,

BEGIN OF ty_field_info,
tabname TYPE tabname,
END OF ty_field_info.

DATA: lt_ms_data TYPE TABLE OF zmaster_tab, "List of Tables
lw_ms_data TYPE zmaster_tab,
lt_field_info TYPE TABLE OF ty_field_info,
lw_field_info TYPE ty_field_info,
lt_junk TYPE TABLE OF zmaster_tab, "Cpature the issue list
lt_cfg_data TYPE TABLE OF zdata_table, "Actual Value table
lt_final TYPE TABLE OF zdata_table,
lt_archive TYPE TABLE OF zdata_table,
lw_final TYPE zdata_table,
lt_pre_data TYPE TABLE OF ty_pre_final,
lt_pre_final TYPE TABLE OF ty_pre_final,
lw_pre_data TYPE ty_pre_final,
lt_ms_ddic TYPE TABLE OF ty_ddic,
ls_ms_ddic TYPE ty_ddic,
lt_ms_split TYPE TABLE OF field,
lt_ds_split TYPE TABLE OF field,
ls_split TYPE field.

DATA: lv_sel_mfields TYPE string,
lv_sel_dfields TYPE string,
lv_key1 TYPE string,
lv_key2 TYPE string,
lv_key3 TYPE string,
lv_key4 TYPE string,
lv_key5 TYPE string,
lv_val1 TYPE string,
lv_val2 TYPE string,
lv_val3 TYPE string,
lv_val4 TYPE string,
lv_val5 TYPE string,
lv_key_fields TYPE char300,
lv_main_fdesc TYPE char1024_cs,
lv_val_desc TYPE char1024_cs,
lv_no_cols TYPE i,
lv_intlen TYPE i,
lv_dec TYPE i,
lv_cnt TYPE i,
lv_inttype TYPE inttype,
lv_value TYPE string,
lv_udate TYPE ersda,
lv_utime TYPE created_at_time,
lt_main_comp TYPE cl_abap_structdescr=>component_table,
ls_main_comp LIKE LINE OF lt_main_comp,
l_data TYPE REF TO data,
l_new_tab TYPE REF TO cl_abap_tabledescr,
l_new_type TYPE REF TO cl_abap_structdescr.

FIELD-SYMBOLS: <lt_main_dyntab> TYPE ANY TABLE,
<lt_desc_dyntab> TYPE ANY TABLE,
<lw_main_data> TYPE any,
<lw_desc_data> TYPE any,
<lv_field> TYPE any,
<lv_final> TYPE ty_pre_final.

CONSTANTS: lc_lang TYPE char1 VALUE 'E',
lc_pipe TYPE char1 VALUE '|',
lc_insr TYPE char1 VALUE 'I',
lc_updt TYPE char1 VALUE 'U',
lc_dele TYPE char1 VALUE 'D',
lc_spras TYPE char5 VALUE 'SPRAS'.


INITIALIZATION.

START-OF-SELECTION.

SELECT * FROM zmaster_tab
INTO TABLE lt_ms_data
WHERE langu = lc_lang.

IF sy-subrc = '0'.
SORT lt_ms_data BY langu tabname field.

LOOP AT lt_ms_data INTO lw_ms_data.
lw_field_info-tabname = lw_ms_data-tabname.
APPEND lw_field_info TO lt_field_info.
CLEAR: lw_field_info,lw_ms_data.
ENDLOOP.

LOOP AT lt_ms_data INTO lw_ms_data.
lw_field_info-tabname = lw_ms_data-desc_tab.
APPEND lw_field_info TO lt_field_info.
CLEAR: lw_field_info,lw_ms_data.
ENDLOOP.

IF lt_field_info IS NOT INITIAL.
SORT lt_field_info.
ENDIF.

*--Fetch DDIC Information
SELECT tabname
fieldname
rollname
position
keyflag
inttype
intlen
decimals
scrtext_l FROM dd03m
INTO TABLE lt_ms_ddic
FOR ALL ENTRIES IN lt_field_info
WHERE tabname EQ lt_field_info-tabname
AND ddlanguage EQ lc_lang.
* AND keyflag EQ abap_true.
IF sy-subrc = 0.
SORT lt_ms_ddic BY tabname fieldname.
ENDIF.

*--Fetch the value table data
SELECT * FROM zdata_table
INTO TABLE lt_cfg_data
FOR ALL ENTRIES IN lt_ms_data
WHERE langu EQ lc_lang
AND tabname EQ lt_ms_data-tabname
AND archive NE abap_true.
IF sy-subrc = 0.
SORT lt_cfg_data BY langu tabname field value.
ENDIF.
ENDIF.

LOOP AT lt_ms_data INTO lw_ms_data.
*-------------------Logic for Main data------------------------------
*--Separating the key fields
CLEAR: lt_ms_split, lt_main_comp, lv_sel_mfields, lv_key_fields,
lv_main_fdesc, lv_no_cols.
SPLIT lw_ms_data-field AT lc_pipe INTO TABLE lt_ms_split.
DESCRIBE TABLE lt_ms_split LINES lv_no_cols.

LOOP AT lt_ms_split INTO ls_split.
CLEAR: ls_ms_ddic, ls_main_comp, lv_intlen, lv_dec.
READ TABLE lt_ms_ddic INTO ls_ms_ddic WITH KEY
tabname = lw_ms_data-tabname
fieldname = ls_split
keyflag = abap_true
BINARY SEARCH.
IF sy-subrc = 0.

*--Concatenating fields for select query
CONCATENATE lv_sel_mfields ls_ms_ddic-fieldname
INTO lv_sel_mfields SEPARATED BY space.

*--Concatenating fields to prepare key
CONCATENATE lv_key_fields ls_ms_ddic-fieldname
INTO lv_key_fields SEPARATED BY lc_pipe.

*--Concatenating field descriptions
CONCATENATE lv_main_fdesc ls_ms_ddic-scrtext_l
INTO lv_main_fdesc SEPARATED BY lc_pipe.

*---Filling the component table
lv_intlen = ls_ms_ddic-intlen.
lv_dec = ls_ms_ddic-decimals.
TRY.
CALL METHOD cl_abap_elemdescr=>get_by_kind
EXPORTING
p_type_kind = ls_ms_ddic-inttype
p_length = lv_intlen
p_decimals = lv_dec
RECEIVING
p_result = ls_main_comp-type.

ls_main_comp-name = ls_ms_ddic-fieldname.
CATCH cx_parameter_invalid_range.
ENDTRY.

APPEND ls_main_comp TO lt_main_comp.
CLEAR: ls_main_comp.
ENDIF.
ENDLOOP.

IF lt_main_comp IS INITIAL.
APPEND lw_ms_data TO lt_junk.
CONTINUE.
ENDIF.

*--Remove leading space
SHIFT lv_sel_mfields LEFT BY 1 PLACES.
SHIFT lv_key_fields LEFT BY 1 PLACES.
SHIFT lv_main_fdesc LEFT BY 1 PLACES.

*--- Create a New Type
TRY.
CLEAR: l_new_type.
CALL METHOD cl_abap_structdescr=>create
EXPORTING
p_components = lt_main_comp
RECEIVING
p_result = l_new_type.
CATCH cx_sy_struct_creation.
ENDTRY.

*--- New dynamic internal Table type
TRY.
CLEAR: l_new_tab.
CALL METHOD cl_abap_tabledescr=>create
EXPORTING
p_line_type = l_new_type
p_unique = abap_false
RECEIVING
p_result = l_new_tab.
CATCH cx_sy_table_creation.
ENDTRY.

*--data to handle the new table type
CLEAR: l_data.
CREATE DATA l_data TYPE HANDLE l_new_tab.

*--New internal table in the fieldsymbols
ASSIGN l_data->* TO <lt_main_dyntab>.
CLEAR: l_data.
*-------------------End Logic for Main data---------------------------

*-------------------Logic for Description data------------------------
IF lw_ms_data-desc_tab IS NOT INITIAL.
*--Separating the key fields
CLEAR: lt_ds_split, ls_split, lt_main_comp, lv_sel_dfields.
SPLIT lw_ms_data-desc_keys AT lc_pipe INTO TABLE lt_ds_split.
LOOP AT lt_ds_split INTO ls_split.
CLEAR: ls_ms_ddic, ls_main_comp, lv_intlen, lv_dec.
READ TABLE lt_ms_ddic INTO ls_ms_ddic WITH KEY
tabname = lw_ms_data-desc_tab
fieldname = ls_split
keyflag = abap_true
BINARY SEARCH.
IF sy-subrc = 0.

*--Concatenating fields for select query
CONCATENATE lv_sel_dfields ls_ms_ddic-fieldname
INTO lv_sel_dfields SEPARATED BY space.

*---Filling the component table
lv_intlen = ls_ms_ddic-intlen.
lv_dec = ls_ms_ddic-decimals.
TRY.
CALL METHOD cl_abap_elemdescr=>get_by_kind
EXPORTING
p_type_kind = ls_ms_ddic-inttype
p_length = lv_intlen
p_decimals = lv_dec
RECEIVING
p_result = ls_main_comp-type.

ls_main_comp-name = ls_ms_ddic-fieldname.
CATCH cx_parameter_invalid_range.
ENDTRY.

APPEND ls_main_comp TO lt_main_comp.
CLEAR: ls_main_comp.
ENDIF.
ENDLOOP.

IF lt_main_comp IS NOT INITIAL.
*--Remove leading space
SHIFT lv_sel_dfields LEFT BY 1 PLACES.

*---Populate Value Description Field
READ TABLE lt_ms_ddic INTO ls_ms_ddic WITH KEY
tabname = lw_ms_data-desc_tab
fieldname = lw_ms_data-desc_tx_field
BINARY SEARCH.
IF sy-subrc = 0.
IF lw_ms_data-desc_tx_field IS NOT INITIAL.
TRY.
lv_inttype = 'C'.
lv_intlen = 50.
CALL METHOD cl_abap_elemdescr=>get_by_kind
EXPORTING
p_type_kind = lv_inttype
p_length = lv_intlen
RECEIVING
p_result = ls_main_comp-type.

ls_main_comp-name = lw_ms_data-desc_tx_field.
CATCH cx_parameter_invalid_range.
ENDTRY.

APPEND ls_main_comp TO lt_main_comp.
CLEAR: ls_main_comp.

*--Concatenate desc field to select query
CONCATENATE lv_sel_dfields lw_ms_data-desc_tx_field INTO lv_sel_dfields
SEPARATED BY space.

ENDIF.
ELSE.
APPEND lw_ms_data TO lt_junk.
ENDIF.
*--- Create a New Type
TRY.
CLEAR: l_new_type.
CALL METHOD cl_abap_structdescr=>create
EXPORTING
p_components = lt_main_comp
RECEIVING
p_result = l_new_type.
CATCH cx_sy_struct_creation.
ENDTRY.

*--- New dynamic internal Table type
TRY.
CLEAR: l_new_tab.
CALL METHOD cl_abap_tabledescr=>create
EXPORTING
p_line_type = l_new_type
p_unique = abap_false
RECEIVING
p_result = l_new_tab.
CATCH cx_sy_table_creation.
ENDTRY.

*--data to handle the new table type
CLEAR: l_data.
CREATE DATA l_data TYPE HANDLE l_new_tab.

*--New internal table in the fieldsymbols
ASSIGN l_data->* TO <lt_desc_dyntab>.
CLEAR: l_data.

*--Fetching Description from Text/Main Tables dynamically
IF lw_ms_data-desc_tab IS NOT INITIAL.
SELECT (lv_sel_dfields)
INTO CORRESPONDING FIELDS OF
TABLE <lt_desc_dyntab> FROM (lw_ms_data-desc_tab).

*--Only capture upto 5 keys
CLEAR: lv_cnt,lv_key1,lv_key2,lv_key3,lv_key4,lv_key5,
lv_val1,lv_val2,lv_val3,lv_val4,lv_val5.
LOOP AT lt_ms_split INTO ls_split.
READ TABLE lt_ds_split TRANSPORTING NO FIELDS WITH KEY table_line = lc_spras.
IF sy-subrc = 0 AND lv_key1 IS INITIAL.
lv_key1 = lc_spras.
lv_val1 = sy-langu.
lv_cnt = 1.
ENDIF.

READ TABLE lt_ds_split INTO DATA(lw_sp) WITH KEY table_line = ls_split.
IF sy-subrc = 0 AND lw_sp <> lc_spras.
lv_cnt = lv_cnt + 1.
CASE lv_cnt.
WHEN '1'.
lv_key1 = lw_sp.
WHEN '2'.
lv_key2 = lw_sp.
WHEN '3'.
lv_key3 = lw_sp.
WHEN '4'.
lv_key4 = lw_sp.
WHEN OTHERS.
lv_key5 = lw_sp.
ENDCASE.
ENDIF.
ENDLOOP.
ENDIF.
ENDIF.
ENDIF.
*-------------------End Logic for Description data---------------------


*--Fetching Data from Config Tables dynamically
SELECT (lv_sel_mfields)
INTO CORRESPONDING FIELDS OF
TABLE <lt_main_dyntab> FROM (lw_ms_data-tabname).

LOOP AT <lt_main_dyntab> ASSIGNING <lw_main_data>.

IF lw_ms_data-desc_tab IS NOT INITIAL.
ASSIGN COMPONENT lv_key1 OF STRUCTURE <lw_main_data> TO <lv_field>.
IF sy-subrc = 0.
lv_val1 = <lv_field>.
ENDIF.

ASSIGN COMPONENT lv_key2 OF STRUCTURE <lw_main_data> TO <lv_field>.
IF sy-subrc = 0.
lv_val2 = <lv_field>.
ENDIF.

ASSIGN COMPONENT lv_key3 OF STRUCTURE <lw_main_data> TO <lv_field>.
IF sy-subrc = 0.
lv_val3 = <lv_field>.
ENDIF.

ASSIGN COMPONENT lv_key4 OF STRUCTURE <lw_main_data> TO <lv_field>.
IF sy-subrc = 0.
lv_val4 = <lv_field>.
ENDIF.

ASSIGN COMPONENT lv_key5 OF STRUCTURE <lw_main_data> TO <lv_field>.
IF sy-subrc = 0.
lv_val5 = <lv_field>.
ENDIF.

*--Get the value description
IF <lt_desc_dyntab> IS ASSIGNED.
READ TABLE <lt_desc_dyntab> ASSIGNING <lw_desc_data>
WITH KEY (lv_key1) = lv_val1
(lv_key2) = lv_val2
(lv_key3) = lv_val3
(lv_key4) = lv_val4
(lv_key5) = lv_val5.
IF sy-subrc = 0.
ASSIGN COMPONENT lw_ms_data-desc_tx_field OF STRUCTURE <lw_desc_data> TO <lv_field>.
IF sy-subrc = 0.
lv_val_desc = <lv_field>.
ENDIF.
ENDIF.
ENDIF.
ENDIF.


*--Concatenate the Values
DO.
ASSIGN COMPONENT sy-index
OF STRUCTURE <lw_main_data>
TO <lv_field>.
*--Capture only the field values and not the value description
IF sy-subrc EQ 0 AND sy-index <= lv_no_cols.
IF sy-index EQ 1.
lv_value = <lv_field>.
ELSE.
CONCATENATE lv_value <lv_field> INTO lv_value SEPARATED BY lc_pipe.
ENDIF.
ELSE.
EXIT.
ENDIF.
ENDDO.

lw_pre_data-tabname = lw_ms_data-tabname.
lw_pre_data-langu = lc_lang.
lw_pre_data-field = lv_key_fields.
lw_pre_data-fdesc = lv_main_fdesc.
lw_pre_data-value = lv_value.
lw_pre_data-val_desc = lv_val_desc.
lw_pre_data-desc_field = lw_ms_data-desc_tx_field.
APPEND lw_pre_data TO lt_pre_data.
CLEAR: lw_pre_data.
CLEAR: lv_value, lv_val_desc.
ENDLOOP.

IF <lt_main_dyntab> IS ASSIGNED.
UNASSIGN: <lt_main_dyntab>.
ENDIF.

IF <lt_desc_dyntab> IS ASSIGNED.
UNASSIGN: <lt_desc_dyntab>.
ENDIF.

CLEAR: lw_pre_data, lt_main_comp, l_new_tab, l_new_type, lv_no_cols.
ENDLOOP.


*--Comparing the data and preparing the final for Insert and Update records
SORT lt_pre_data BY langu tabname field value.
SORT lt_cfg_data BY langu tabname field value.
LOOP AT lt_pre_data ASSIGNING <lv_final>.
<lv_final>-sno = sy-tabix.
MOVE-CORRESPONDING <lv_final> TO lw_pre_data.
READ TABLE lt_cfg_data INTO DATA(lw_dt) WITH KEY langu = <lv_final>-langu
tabname = <lv_final>-tabname
field = <lv_final>-field
value = <lv_final>-value
BINARY SEARCH.

IF sy-subrc = 0.
*--For Arciving purpose
CLEAR: lw_final.
MOVE-CORRESPONDING lw_dt TO lw_final.
*--Data Exists
*--Compare the field descriptions and get the updated records
IF <lv_final>-desc_field IS NOT INITIAL.
IF <lv_final>-val_desc <> lw_dt-val_desc.
<lv_final>-chngind = lc_updt.
lw_pre_data-chngind = lc_updt.
<lv_final>-val_desc = <lv_final>-val_desc.
lw_pre_data-val_desc = <lv_final>-val_desc.
APPEND lw_pre_data TO lt_pre_final.
*--Capturing Archive record.
lw_final-archive = abap_true.
APPEND lw_final TO lt_final.
CLEAR: lw_final.
ENDIF.
ENDIF.
ELSE.
*--Data does not exists
*--Capture this data as new records
<lv_final>-chngind = lc_insr.
lw_pre_data-chngind = lc_insr.
APPEND lw_pre_data TO lt_pre_final.
ENDIF.
CLEAR: lw_pre_data, lw_dt.
ENDLOOP.

*--Comparing the data and preparing the final for delete records
SORT lt_pre_data BY langu tabname field value.
LOOP AT lt_cfg_data INTO lw_dt.
CLEAR: lw_pre_data.
READ TABLE lt_pre_data INTO lw_pre_data WITH KEY
langu = lw_dt-langu
tabname = lw_dt-tabname
field = lw_dt-field
value = lw_dt-value
BINARY SEARCH.
IF sy-subrc = '0'.
*--Data Exists Can be ignored
ELSE.
*--Data does not exists Capture the deleted records
MOVE-CORRESPONDING lw_dt TO lw_pre_data.
lw_pre_data-chngind = lc_dele.
APPEND lw_pre_data TO lt_pre_final.

*--Capturing Archive record.
MOVE-CORRESPONDING lw_dt TO lw_final.
lw_final-archive = abap_true.
APPEND lw_final TO lt_final.
CLEAR: lw_final.
ENDIF.
CLEAR: lw_dt.
ENDLOOP.

*--Preparing final table

lv_udate = sy-datum.
lv_utime = sy-timlo.

SORT lt_pre_final BY tabname.
SORT lt_cfg_data BY langu tabname ASCENDING sno DESCENDING.

LOOP AT lt_pre_final ASSIGNING <lv_final>.
AT NEW tabname.
CLEAR: lv_no_cols, lw_dt.
READ TABLE lt_cfg_data INTO lw_dt WITH KEY
langu = lc_lang
tabname = <lv_final>-tabname
BINARY SEARCH.
IF sy-subrc = 0.
lv_no_cols = lw_dt-sno + 1.
ELSE.
lv_no_cols = 1.
ENDIF.
ENDAT.
<lv_final>-sno = lv_no_cols.
lv_no_cols = lv_no_cols + 1.
<lv_final>-udate = lv_udate.
<lv_final>-utime = lv_utime.

MOVE-CORRESPONDING <lv_final> TO lw_final.
lw_final-mandt = sy-mandt.
APPEND lw_final TO lt_final.
CLEAR: lw_final.
ENDLOOP.

*--Inserting data into tabe
IF lt_final IS NOT INITIAL.
SORT lt_final BY langu tabname sno.
MODIFY zdata_table FROM TABLE lt_final.
ENDIF.

*-- Sending only the updated records.
DELETE lt_final WHERE archive IS NOT INITIAL.

IF <lv_final> IS ASSIGNED.
UNASSIGN <lv_final>.
ENDIF.

CLEAR: lv_utime, lv_udate, lt_pre_data, lt_pre_final,
lt_final.

LOOP AT lt_junk INTO lw_ms_data.
WRITE:/ lw_ms_data.
ENDLOOP.

d config data along with texts in the it_final table from the below code snippet.

Note: lt_junk will have the details related to the skipped tables.

 
3 Comments
Labels in this area