Technical Articles
CRM – Identifying user changes on a decision table in BRF+
At CRM system, when you need to maintain the content of a decision table at Business Rule Framework plus, could be a requirement, during the activation action, to identify the changes done by the user.
For this, we need to compare the changes that the user has done on the table content at the decision table settings screen with the content stored on the database.
After press the “Activate” button, an object of the exit class registered on the application is instantiated. On this instance, we can check the differences, identifying the rows that have been modified by the user.
After implementation, when you change the table content of a decision table linked to the application where the exit class have been registered, the method ACTIVATION_VETO wil be triggered when the “Activate” button is pressed, on the decision table settings screen.
On this method, you can read the content of the screen and the content that is already saved on the database, making checks accordingly with your requirements.
Implementation
Create a class for the interface IF_FDT_APPLICATION_SETTINGS on T-CODE SE24.
Implement the method ACTIVATION_VETO.
Ps.: the method is only triggered whether the CONSTRUCTOR is implemented as static and attribute GV_ACTIVATION_VETO setted to ABAP_TRUE.
Take note of the application ID:
On the application settings we should link the created class to the application on BRFPLUS:
At T-CODE SE11, create:
Structure Z_S_DECISION_TABLE_RANGE
Table Type Z_TT_DECISION_TABLE_RANGE
Structure Z_S_DECISION_TABLE_DATA
Source code:
METHOD if_fdt_application_settings~activation_veto.
TYPES: BEGIN OF ty_decision_table_string,
decision_table_id TYPE <Decision table ID>,
ts_tange_low TYPE string,
r_value TYPE string,
END OF ty_decision_table_string.
DATA: lt_decision_table_str TYPE TABLE OF ty_decision_table_string,
ls_decision_table_str TYPE ty_decision_table_string,
lt_decision_table_str_old TYPE TABLE OF ty_decision_table_string,
ls_decision_table_str_old TYPE ty_decision_table_string,
lref_decision_table TYPE REF TO cl_fdt_decision_table,
lref_factory TYPE REF TO if_fdt_factory,
lr_data TYPE REF TO data,
lt_decision_table TYPE TABLE OF z_s_decision_table_data,
ls_decision_table LIKE LINE OF lt_decision_table,
lt_decision_table_old TYPE TABLE OF z_s_decision_table_data,
ls_decision_table_old LIKE LINE OF lt_decision_table,
lv_timestamp TYPE timestamp,
lv_col_no TYPE int4,
lt_decision_table_id TYPE TABLE OF <Decision table ID>.
FIELD-SYMBOLS: <fs_decision_table_id> TYPE any.
" GUID of application
CONSTANTS: co_app_id_ctt_product_rules TYPE if_fdt_types=>id VALUE '<Application ID>'.
*********************************************************************************************************************************
GET TIME STAMP FIELD lv_timestamp.
" Generic factory instance
lref_factory = cl_fdt_factory=>if_fdt_factory~get_instance( co_app_id_ctt_product_rules ).
" Get the expression instance of the decision table with the object ID
lref_decision_table ?= lref_factory->get_expression( iv_id = iv_id ).
" Get the decision table data based on above expression GUID
" it means that it will read the data including the user's changes before the activation
lref_decision_table->if_fdt_decision_table~get_table_data( IMPORTING ets_data = DATA(ets_data) ).
lt_decision_table = ets_data.
SORT lt_decision_table BY col_no row_no.
CHECK lt_decision_table IS NOT INITIAL.
" Transform the decision table on a string table to be possible to compare the content with the data that is stored on the database
DATA(lv_end_of_loop) = abap_false.
WHILE lv_end_of_loop = abap_false.
lv_col_no = lv_col_no + 1.
LOOP AT lt_decision_table INTO ls_decision_table WHERE col_no = lv_col_no.
DATA(ls_range) = COND #( WHEN line_exists( ls_decision_table-ts_range[ 1 ] )
THEN ls_decision_table-ts_range[ 1 ] ).
lr_data = ls_range-r_low_value.
ASSIGN lr_data->* TO FIELD-SYMBOL(<fs_ts_range_low>).
IF <fs_ts_range_low> IS ASSIGNED.
" The ROW_NO 1 stores the decision table ID
IF ls_decision_table-row_no = 1.
ls_decision_table_str-decision_table_id = <fs_ts_range_low>.
ELSE.
ls_decision_table_str-ts_tange_low = ls_decision_table_str-ts_tange_low && ls_range-option && <fs_ts_range_low>.
ENDIF.
UNASSIGN <fs_ts_range_low>.
ENDIF.
lr_data = ls_decision_table-r_value.
ASSIGN lr_data->* TO FIELD-SYMBOL(<fs_r_value>).
IF <fs_r_value> IS ASSIGNED.
ls_decision_table_str-r_value = ls_decision_table_str-r_value && <fs_r_value>.
UNASSIGN <fs_r_value>.
ENDIF.
ENDLOOP.
IF sy-subrc NE 0.
lv_end_of_loop = abap_true.
CLEAR lv_col_no.
ELSE.
APPEND ls_decision_table_str TO lt_decision_table_str.
CLEAR ls_decision_table_str.
ENDIF.
ENDWHILE.
" Get the decision table data based on above expression GUID and the current timestamp
" it means that it will read the data saved on the database
lref_decision_table->if_fdt_decision_table~get_table_data( EXPORTING iv_timestamp = lv_timestamp
IMPORTING ets_data = DATA(ets_data_old) ).
lt_decision_table_old = ets_data_old.
SORT lt_decision_table_old BY col_no row_no.
CHECK lt_decision_table_old IS NOT INITIAL.
" Transform the decision table on a string table to be possible to compare the content with the data with the user's changes before the activation
lv_end_of_loop = abap_false.
WHILE lv_end_of_loop = abap_false.
lv_col_no = lv_col_no + 1.
LOOP AT lt_decision_table_old INTO ls_decision_table_old WHERE col_no = lv_col_no.
DATA(ls_range_old) = COND #( WHEN line_exists( ls_decision_table_old-ts_range[ 1 ] )
THEN ls_decision_table_old-ts_range[ 1 ] ).
lr_data = ls_range_old-r_low_value.
ASSIGN lr_data->* TO <fs_ts_range_low>.
IF <fs_ts_range_low> IS ASSIGNED.
" The ROW_NO 1 stores the decision table ID
IF ls_decision_table_old-row_no = 1.
ls_decision_table_str_old-decision_table_id = <fs_ts_range_low>.
ELSE.
ls_decision_table_str_old-ts_tange_low = ls_decision_table_str_old-ts_tange_low && ls_range_old-option && <fs_ts_range_low>.
ENDIF.
UNASSIGN <fs_ts_range_low>.
ENDIF.
lr_data = ls_decision_table_old-r_value.
ASSIGN lr_data->* TO <fs_r_value>.
IF <fs_r_value> IS ASSIGNED.
ls_decision_table_str_old-r_value = ls_decision_table_str_old-r_value && <fs_r_value>.
UNASSIGN <fs_r_value>.
ENDIF.
ENDLOOP.
IF sy-subrc NE 0.
lv_end_of_loop = abap_true.
CLEAR lv_col_no.
ELSE.
APPEND ls_decision_table_str_old TO lt_decision_table_str_old.
CLEAR ls_decision_table_str_old.
ENDIF.
ENDWHILE.
*********************************************************************************************************************************
" Check differences between the internal tables
" and removes the content that have not been changed
LOOP AT lt_decision_table_str_old INTO ls_decision_table_str_old.
DATA(lv_tabix) = sy-tabix.
READ TABLE lt_decision_table_str WITH KEY decision_table_id = ls_decision_table_str_old-decision_table_id
r_value = ls_decision_table_str_old-r_value
ts_tange_low = ls_decision_table_str_old-ts_tange_low
INTO ls_decision_table_str.
IF sy-subrc = 0.
DELETE lt_decision_table_str INDEX sy-tabix.
DELETE lt_decision_table_str_old INDEX lv_tabix.
ENDIF.
ENDLOOP.
APPEND LINES OF lt_decision_table_str_old TO lt_decision_table_str.
SORT lt_decision_table_str BY decision_table_id ASCENDING.
DELETE ADJACENT DUPLICATES FROM lt_decision_table_str COMPARING decision_table_id.
" Fill table with the ID of the rows that have been modified
LOOP AT lt_decision_table_str INTO ls_decision_table_str.
APPEND ls_decision_table_str-decision_table_id TO lt_decision_table_id.
ENDLOOP.
ENDMETHOD.
Thank you for your post. If I understand well, you have implemented the method CLASS_CONSTRUCTOR with the single line GV_ACTIVATION_VETO = abap_true, so that SAP calls the method ACTIVATION_VETO at activation time.
Something you don’t mention is that if the implementation chooses to stop the activation process, it should return the parameters EV_VETO = abap_true along with one or more messages in parameter ET_MESSAGE.
Sandra
Thank you! You,re right.
I didn’t understand the line
What we need to pass here in the <Decision table ID>, i tried to pass the ID of decision table as type , but that doesn’t work for me. Can you please suggest.
eg:- decision_table_id type 00A3456BBBB12345FC772455E15CA ( gave me error here)