SAP ABAP自建表变更履历记录(动态更新)

需求:

平台开发时,需要记录对自建表某些字段的变更履历,即写入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.

以上。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeveloperMrMeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值