需求:
平台开发时,需要记录对自建表某些字段的变更履历,即写入CDHDR/CDPOS,正常方式是SCDO创建变更对象,然后调用自动生成的类方法完成记录写入,只是每次都需要自己写逻辑去获取对应的X结构(最新数据)和Y结构(历史数据),不想每次都写这段代码,故封装了一个动态方法,每次直接调用即可。
实现步骤:
以下表为测试用例:
1.对自建表启用日志更改:
2.对要记录的字段对应的数据元素启用更改文档:
3.SCDO创建变更文档对象:
4.创建动态更新工具类:
5.代码调用:
后续要记录其他自建表时,只需要将iv_class_name替换为生成的类名即可,it_table传要更新的表即可,iv_kz更新传M,删除传D即可。
测试效果:
工具类源码:
CLASS zcl_write_change_log DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
CLASS-METHODS dynamic_update
IMPORTING
VALUE(iv_class_name) TYPE seoclsname
VALUE(it_table) TYPE ANY TABLE
VALUE(iv_kz) TYPE cdchngind
RETURNING
VALUE(et_changenr) TYPE dfs_changenr_t
EXCEPTIONS
has_error .
CLASS-METHODS get_new_and_old
IMPORTING
VALUE(iv_kz) TYPE cdchngind
VALUE(is_table) TYPE any
EXPORTING
VALUE(ev_upd_cdchngindh) TYPE cdhdr-change_ind
VALUE(es_table_x) TYPE any
VALUE(es_table_y) TYPE any
VALUE(ev_objectid) TYPE cdhdr-objectid .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS ZCL_WRITE_CHANGE_LOG IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_WRITE_CHANGE_LOG=>DYNAMIC_UPDATE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_CLASS_NAME TYPE SEOCLSNAME
* | [--->] IT_TABLE TYPE ANY TABLE
* | [--->] IV_KZ TYPE CDCHNGIND
* | [<-()] ET_CHANGENR TYPE DFS_CHANGENR_T
* | [EXC!] HAS_ERROR
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD dynamic_update.
FIELD-SYMBOLS:
<ls_source_line_x> TYPE any,
<ls_source_line_y> TYPE any.
DATA:
lo_objectid TYPE REF TO data,
lo_tcode TYPE REF TO data,
lo_planned_change_number TYPE REF TO data,
lo_utime TYPE REF TO data,
lo_udate TYPE REF TO data,
lo_username TYPE REF TO data,
lo_cdoc_planned_or_real TYPE REF TO data,
lo_cdoc_upd_object TYPE REF TO data,
lo_cdoc_no_change_pointers TYPE REF TO data,
lo_cdchangenumber TYPE REF TO data,
lo_upd_cdchngindh TYPE REF TO data,
lo_changenumber TYPE REF TO data.
FIELD-SYMBOLS:
<lv_objectid> TYPE cdhdr-objectid,
<lv_tcode> TYPE cdhdr-tcode,
<lv_planned_change_number> TYPE cdhdr-planchngnr,
<lv_utime> TYPE cdhdr-utime,
<lv_udate> TYPE cdhdr-udate,
<lv_username> TYPE cdhdr-username,
<lv_cdoc_planned_or_real> TYPE cdhdr-change_ind,
<lv_cdoc_upd_object> TYPE cdhdr-change_ind,
<lv_cdoc_no_change_pointers> TYPE cdhdr-change_ind,
<lv_cdchangenumber> TYPE cdhdr-changenr,
<lv_upd_cdchngindh> TYPE if_chdo_object_tools_rel=>ty_cdchngindh,
<lv_changenumber> TYPE if_chdo_object_tools_rel=>ty_cdchangenr.
DATA:
lv_upd_cdchngindh TYPE if_chdo_object_tools_rel=>ty_cdchngindh,
lv_changenumber TYPE if_chdo_object_tools_rel=>ty_cdchangenr.
DATA:
lo_err_ref TYPE REF TO cx_chdo_write_error,
lv_err_action TYPE string.
DATA:
lo_source_typedescr TYPE REF TO cl_abap_typedescr,
lo_source_structdescr TYPE REF TO cl_abap_structdescr,
lo_source_structure_ref TYPE REF TO data,
lo_source_tabledescr TYPE REF TO cl_abap_tabledescr,
lo_source_line_ref_x TYPE REF TO data,
lo_source_line_ref_y TYPE REF TO data,
lo_class_objectdescr TYPE REF TO cl_abap_objectdescr.
DATA:
lt_parameter TYPE abap_parmbind_tab,
ls_parameter TYPE abap_parmbind,
lt_exception TYPE abap_excpbind_tab,
ls_exception TYPE abap_excpbind,
lt_methods TYPE abap_methdescr_tab,
ls_methods TYPE abap_methdescr,
ls_parameters TYPE abap_parmdescr,
ls_exceptions TYPE abap_excpdescr.
FIELD-SYMBOLS:
<ls_source_line> TYPE data.
CHECK it_table IS NOT INITIAL.
CREATE DATA lo_source_structure_ref LIKE LINE OF it_table.
CALL METHOD cl_abap_structdescr=>describe_by_data_ref
EXPORTING
p_data_ref = lo_source_structure_ref
RECEIVING
p_descr_ref = lo_source_typedescr.
lo_source_structdescr ?= lo_source_typedescr.
ASSIGN lo_source_structure_ref->* TO <ls_source_line>.
CREATE DATA lo_source_line_ref_x TYPE HANDLE lo_source_structdescr.
CREATE DATA lo_source_line_ref_y TYPE HANDLE lo_source_structdescr.
ASSIGN lo_source_line_ref_x->* TO <ls_source_line_x>.
ASSIGN lo_source_line_ref_y->* TO <ls_source_line_y>.
lo_class_objectdescr ?= cl_abap_objectdescr=>describe_by_name( iv_class_name ).
lt_methods = lo_class_objectdescr->methods.
CREATE DATA lo_objectid TYPE cdhdr-objectid.
ASSIGN lo_objectid->* TO <lv_objectid>.
CREATE DATA lo_tcode TYPE cdhdr-tcode.
ASSIGN lo_tcode->* TO <lv_tcode>.
CREATE DATA lo_planned_change_number TYPE cdhdr-planchngnr.
ASSIGN lo_planned_change_number->* TO <lv_planned_change_number>.
CREATE DATA lo_utime TYPE cdhdr-utime.
ASSIGN lo_utime->* TO <lv_utime>.
CREATE DATA lo_udate TYPE cdhdr-udate.
ASSIGN lo_udate->* TO <lv_udate>.
CREATE DATA lo_username TYPE cdhdr-username.
ASSIGN lo_username->* TO <lv_username>.
CREATE DATA lo_cdoc_planned_or_real TYPE cdhdr-change_ind.
ASSIGN lo_cdoc_planned_or_real->* TO <lv_cdoc_planned_or_real>.
CREATE DATA lo_cdoc_upd_object TYPE cdhdr-change_ind.
ASSIGN lo_cdoc_upd_object->* TO <lv_cdoc_upd_object>.
CREATE DATA lo_cdoc_no_change_pointers TYPE cdhdr-change_ind.
ASSIGN lo_cdoc_no_change_pointers->* TO <lv_cdoc_no_change_pointers>.
CREATE DATA lo_cdchangenumber TYPE cdhdr-changenr.
ASSIGN lo_cdchangenumber->* TO <lv_cdchangenumber>.
CREATE DATA lo_upd_cdchngindh TYPE if_chdo_object_tools_rel=>ty_cdchngindh.
ASSIGN lo_upd_cdchngindh->* TO <lv_upd_cdchngindh>.
CREATE DATA lo_changenumber TYPE if_chdo_object_tools_rel=>ty_cdchangenr.
ASSIGN lo_changenumber->* TO <lv_changenumber>.
LOOP AT it_table ASSIGNING <ls_source_line>.
CALL METHOD zcl_write_change_log=>get_new_and_old
EXPORTING
iv_kz = iv_kz
is_table = <ls_source_line>
IMPORTING
ev_upd_cdchngindh = <lv_upd_cdchngindh>
es_table_x = <ls_source_line_x>
es_table_y = <ls_source_line_y>
ev_objectid = <lv_objectid>.
<lv_utime> = sy-timlo.
<lv_udate> = sy-datlo.
<lv_username> = sy-uname.
<lv_tcode> = sy-tcode.
<lv_cdoc_upd_object> = <lv_upd_cdchngindh>.
READ TABLE lt_methods INTO ls_methods
WITH KEY name = 'WRITE'.
ls_parameter-name = 'OBJECTID'.
ls_parameter-kind = cl_abap_objectdescr=>exporting.
ls_parameter-value = lo_objectid.
INSERT ls_parameter INTO TABLE lt_parameter.
CLEAR ls_parameter.
ls_parameter-name = 'TCODE'.
ls_parameter-kind = cl_abap_objectdescr=>exporting.
ls_parameter-value = lo_tcode.
INSERT ls_parameter INTO TABLE lt_parameter.
CLEAR ls_parameter.
ls_parameter-name = 'UTIME'.
ls_parameter-kind = cl_abap_objectdescr=>exporting.
ls_parameter-value = lo_utime.
INSERT ls_parameter INTO TABLE lt_parameter.
CLEAR ls_parameter.
ls_parameter-name = 'UDATE'.
ls_parameter-kind = cl_abap_objectdescr=>exporting.
ls_parameter-value = lo_udate.
INSERT ls_parameter INTO TABLE lt_parameter.
CLEAR ls_parameter.
ls_parameter-name = 'USERNAME'.
ls_parameter-kind = cl_abap_objectdescr=>exporting.
ls_parameter-value = lo_username.
INSERT ls_parameter INTO TABLE lt_parameter.
CLEAR ls_parameter.
ls_parameter-name = 'PLANNED_CHANGE_NUMBER'.
ls_parameter-kind = cl_abap_objectdescr=>exporting.
ls_parameter-value = lo_planned_change_number.
INSERT ls_parameter INTO TABLE lt_parameter.
CLEAR ls_parameter.
ls_parameter-name = 'OBJECT_CHANGE_INDICATOR'.
ls_parameter-kind = cl_abap_objectdescr=>exporting.
ls_parameter-value = lo_cdoc_upd_object.
INSERT ls_parameter INTO TABLE lt_parameter.
CLEAR ls_parameter.
ls_parameter-name = 'PLANNED_OR_REAL_CHANGES'.
ls_parameter-kind = cl_abap_objectdescr=>exporting.
ls_parameter-value = lo_cdoc_planned_or_real.
INSERT ls_parameter INTO TABLE lt_parameter.
CLEAR ls_parameter.
ls_parameter-name = 'NO_CHANGE_POINTERS'.
ls_parameter-kind = cl_abap_objectdescr=>exporting.
ls_parameter-value = lo_cdoc_no_change_pointers.
INSERT ls_parameter INTO TABLE lt_parameter.
CLEAR ls_parameter.
LOOP AT ls_methods-parameters INTO ls_parameters.
IF ls_parameters-name+0(2) = 'O_'.
ls_parameter-name = ls_parameters-name.
ls_parameter-kind = cl_abap_objectdescr=>exporting.
ls_parameter-value = lo_source_line_ref_y.
INSERT ls_parameter INTO TABLE lt_parameter.
CLEAR ls_parameter.
ENDIF.
IF ls_parameters-name+0(2) = 'N_'.
ls_parameter-name = ls_parameters-name.
ls_parameter-kind = cl_abap_objectdescr=>exporting.
ls_parameter-value = lo_source_line_ref_x.
INSERT ls_parameter INTO TABLE lt_parameter.
CLEAR ls_parameter.
ENDIF.
IF ls_parameters-name+0(4) = 'UPD_'.
ls_parameter-name = ls_parameters-name.
ls_parameter-kind = cl_abap_objectdescr=>exporting.
ls_parameter-value = lo_upd_cdchngindh.
INSERT ls_parameter INTO TABLE lt_parameter.
CLEAR ls_parameter.
ENDIF.
ENDLOOP.
ls_parameter-name = 'CHANGENUMBER'.
ls_parameter-kind = cl_abap_objectdescr=>importing.
ls_parameter-value = lo_changenumber.
INSERT ls_parameter INTO TABLE lt_parameter.
CLEAR ls_parameter.
TRY.
* 写入变更记录
CALL METHOD (iv_class_name)=>write
PARAMETER-TABLE lt_parameter
EXCEPTION-TABLE lt_exception.
CATCH cx_chdo_write_error INTO lo_err_ref.
lv_err_action = lo_err_ref->get_text( ).
MESSAGE lv_err_action TYPE 'E' RAISING has_error.
ENDTRY.
IF <lv_changenumber> IS NOT INITIAL.
APPEND <lv_changenumber> TO et_changenr.
ENDIF.
CLEAR:
<lv_objectid>,
<lv_utime>,
<lv_udate>,
<lv_username>,
<lv_tcode>,
<lv_cdoc_upd_object>,
<lv_upd_cdchngindh>,
<ls_source_line_x>,
<ls_source_line_y>,
<lv_changenumber>.
ENDLOOP.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_WRITE_CHANGE_LOG=>GET_NEW_AND_OLD
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_KZ TYPE CDCHNGIND
* | [--->] IS_TABLE TYPE ANY
* | [<---] EV_UPD_CDCHNGINDH TYPE CDHDR-CHANGE_IND
* | [<---] ES_TABLE_X TYPE ANY
* | [<---] ES_TABLE_Y TYPE ANY
* | [<---] EV_OBJECTID TYPE CDHDR-OBJECTID
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD get_new_and_old.
DATA:
lo_source_typedescr TYPE REF TO cl_abap_typedescr,
lo_source_structdescr TYPE REF TO cl_abap_structdescr,
lt_source_tabledescr TYPE REF TO cl_abap_tabledescr,
lo_source_structure_ref TYPE REF TO data,
lo_source_table_ref TYPE REF TO data,
lt_source_components TYPE abap_component_tab,
ls_source_components TYPE abap_componentdescr,
lt_source_ddic_list TYPE ddfields,
ls_source_ddic_list TYPE dfies.
DATA:
lt_target_structdescr TYPE REF TO cl_abap_structdescr,
lt_target_tabledescr TYPE REF TO cl_abap_tabledescr,
lo_target_structure_ref_x TYPE REF TO data,
lo_target_table_ref_x TYPE REF TO data,
lo_target_structure_ref_y TYPE REF TO data,
lo_target_table_ref_y TYPE REF TO data.
DATA:
lv_index TYPE i,
lv_table_name TYPE tabname,
lv_sql_cond TYPE string.
DATA:
lt_key_tab TYPE STANDARD TABLE OF fieldname,
ls_key_tab TYPE fieldname.
DATA:
lt_sort_tab TYPE abap_sortorder_tab,
ls_sort_tab TYPE abap_sortorder.
FIELD-SYMBOLS:
<lt_source_tab> TYPE STANDARD TABLE,
<ls_source_line> TYPE data,
<ls_source_value> TYPE data,
<lt_target_tab_x> TYPE STANDARD TABLE,
<lt_target_tab_y> TYPE STANDARD TABLE,
<ls_target_line_x> TYPE data,
<ls_target_line_y> TYPE data,
<ls_target_value_x> TYPE data,
<ls_target_value_y> TYPE data,
<lv_mandt> TYPE data.
FIELD-SYMBOLS:
<ls_history_line> TYPE data.
ASSIGN is_table TO <ls_source_line>.
* 获取源对象结构类型
CALL METHOD cl_abap_structdescr=>describe_by_data
EXPORTING
p_data = is_table
RECEIVING
p_descr_ref = lo_source_typedescr.
lo_source_structdescr ?= lo_source_typedescr.
* 创建源对象引用
CREATE DATA lo_source_structure_ref TYPE HANDLE lo_source_structdescr.
ASSIGN lo_source_structure_ref->* TO <ls_history_line>.
* 获取源对象DDIC信息
CALL METHOD lo_source_structdescr->get_ddic_field_list
RECEIVING
p_field_list = lt_source_ddic_list
EXCEPTIONS
not_found = 1
no_ddic_type = 2
OTHERS = 3.
* 获取源对象DDIC表名
READ TABLE lt_source_ddic_list INTO ls_source_ddic_list INDEX 1.
lv_table_name = ls_source_ddic_list-tabname.
* 获取Key值
LOOP AT lt_source_ddic_list INTO ls_source_ddic_list
WHERE keyflag = 'X' AND fieldname <> 'MANDT'.
ls_key_tab = ls_source_ddic_list-fieldname.
APPEND ls_key_tab TO lt_key_tab.
CLEAR ls_key_tab.
ENDLOOP.
**********************************************************************
* 获取源对象组件信息
CALL METHOD lo_source_structdescr->get_components
RECEIVING
p_result = lt_source_components.
** 添加KZ字段
* ls_source_components-name = 'KZ'.
* ls_source_components-type = cl_abap_elemdescr=>get_c( 1 ).
* APPEND ls_source_components TO lt_source_components.
* 创建目标结构类型
lt_target_structdescr = cl_abap_structdescr=>create( lt_source_components ).
lt_target_tabledescr = cl_abap_tabledescr=>create( lt_target_structdescr ).
* 创建目标对象引用
CREATE DATA lo_target_table_ref_x TYPE HANDLE lt_target_tabledescr.
CREATE DATA lo_target_structure_ref_x TYPE HANDLE lt_target_structdescr.
CREATE DATA lo_target_table_ref_y TYPE HANDLE lt_target_tabledescr.
CREATE DATA lo_target_structure_ref_y TYPE HANDLE lt_target_structdescr.
* 分配目标对象指针
ASSIGN lo_target_table_ref_x->* TO <lt_target_tab_x>.
ASSIGN lo_target_structure_ref_x->* TO <ls_target_line_x>.
ASSIGN lo_target_table_ref_y->* TO <lt_target_tab_y>.
ASSIGN lo_target_structure_ref_y->* TO <ls_target_line_y>.
**********************************************************************
* 构建变更记录X/Y表
LOOP AT lt_key_tab INTO ls_key_tab.
ASSIGN COMPONENT ls_key_tab OF STRUCTURE <ls_source_line> TO <ls_source_value>.
lv_sql_cond = lv_sql_cond && ` ` && ls_key_tab && ` = '` && <ls_source_value> && `' AND`.
* 变更记录Key值
ev_objectid = ev_objectid && <ls_source_value>.
ENDLOOP.
lv_sql_cond = shift_right( val = lv_sql_cond sub = 'AND' ).
MOVE-CORRESPONDING <ls_source_line> TO <ls_target_line_x>.
* ASSIGN COMPONENT 'KZ' OF STRUCTURE <ls_target_line_x> TO <ls_target_value_x>.
* ASSIGN COMPONENT 'KZ' OF STRUCTURE <ls_target_line_y> TO <ls_target_value_y>.
* 获取历史数据
SELECT SINGLE * INTO <ls_history_line> FROM (lv_table_name) WHERE (lv_sql_cond).
IF sy-subrc = 0.
MOVE-CORRESPONDING <ls_history_line> TO <ls_target_line_y>.
ASSIGN COMPONENT 'MANDT' OF STRUCTURE <ls_target_line_y> TO <lv_mandt>.
CLEAR <lv_mandt>.
CASE iv_kz.
WHEN 'D'.
* <ls_target_value_x> = <ls_target_value_y> = 'D'.
ev_upd_cdchngindh = 'D'.
WHEN OTHERS.
* <ls_target_value_x> = <ls_target_value_y> = 'U'.
ev_upd_cdchngindh = 'U'.
ENDCASE.
* APPEND <ls_target_line_y> TO <lt_target_tab_y>.
ELSE.
* <ls_target_value_x> = 'I'.
ev_upd_cdchngindh = 'I'.
ENDIF.
* APPEND <ls_target_line_x> TO <lt_target_tab_x>.
*
* APPEND LINES OF <lt_target_tab_x> TO et_table_x.
* APPEND LINES OF <lt_target_tab_y> TO et_table_y.
es_table_x = <ls_target_line_x>.
es_table_y = <ls_target_line_y>.
ENDMETHOD.
ENDCLASS.
以上。