SAP自建表更改记录

目录

1. 表技术设置-日志数据更改

2. SE16N-表数据更改日志

3. SCDO 文档更改对象

4. 自定义日志


1. 表技术设置-日志数据更改

        表技术设置中勾选如下标识-Log Data Changes

SAP自建表更改记录_第1张图片

        使用SM30维护表数据时会自动记录数据变更日志,日志查看路径如下(或使用程序RSVTPROT)

SAP自建表更改记录_第2张图片

        日志效果:

SAP自建表更改记录_第3张图片

SAP自建表更改记录_第4张图片

        前置:

        RZ11参数rec/client设置为all或者含当前client的值(默认关闭)

        日志数据对应表:DBTABLOG

        PS: 日志可以归档,删除操作,参考官方help文档地址help(Execute Customizing Project or Project Administration 是指 SPRO)

2. SE16N-表数据更改日志

        使用SE16N更改表数据时会记录更改日志。

        日志数据记录表 SE16N_CD_KEY、SE16N_CD_DATA

3. SCDO 文档更改对象

        使用SCDO文档更改对象记录数据变更在标准的业务功能中比较常见,比如采购订单等,文档更改对象可以关联工作流事件的触发。

        这里简单介绍自定义文档更改对象的创建及使用。

        创建自定义表,并勾选字段数据元素下的change document复选框

SAP自建表更改记录_第5张图片

SAP自建表更改记录_第6张图片

        进入SCDO,输入对象名,点击新建

SAP自建表更改记录_第7张图片

        输入表名,并点击生成(表名后的选项控制更新函数的生成,int.Table控制更新函数数据更新时是单条还是多条)

SAP自建表更改记录_第8张图片

        选择yes

SAP自建表更改记录_第9张图片

        填入Function group,如下图,Generate DATA for ABAP OO会影响生成的更新程序变量(OO中不允许*开头的变量)

SAP自建表更改记录_第10张图片

        Function group 可以自动生成,不用单独创建

SAP自建表更改记录_第11张图片

        生成的更新程序信息,include program 可以直接使用,已经包含了变量及function调用

SAP自建表更改记录_第12张图片

                生成的include程序如下

SAP自建表更改记录_第13张图片

SAP自建表更改记录_第14张图片

        使用时可以include生成的include程序,调用更新form,或者直接调用更新function,此处以在SM30中添加日志记录为例

        添加数据保存之前和之后事件对应的Routines

SAP自建表更改记录_第15张图片

        FORM 代码如下,该代码为通用代码,需要注意更新函数生成时的参数选择可能导致参数名不一样,需要对应调整代码。参考博客Change log for Z-Table Maintenance (via SCDO) | Zafer Onbaş

FORM before_save.
  TYPES: BEGIN OF ty_tcdrp,
           object     TYPE cdobjectcl,
           reportname TYPE cdreport,
         END OF   ty_tcdrp,
         BEGIN OF ty_view_tab,
           object  TYPE cdobjectcl,
           tabname TYPE cdtabname,
         END OF   ty_view_tab.
  DATA: lt_ptab      TYPE STANDARD TABLE OF string,
        lv_prog      TYPE string,
        lv_mess      TYPE string,
        lv_sid       TYPE string,
        lt_obj       TYPE STANDARD TABLE OF ty_view_tab,
        lt_tcdrp     TYPE STANDARD TABLE OF ty_tcdrp,
        lv_fugn      TYPE funct_pool,
        lv_table     TYPE cdtabname,
        lv_namesfunc TYPE namespace,
        lv_funcgroup TYPE progname,
        lv_namesprog TYPE namespace,
        lv_program   TYPE progname,
        lrt_tabname  TYPE RANGE OF tabname,
        lt_dd26v     TYPE TABLE OF dd26v,
        lv_object    TYPE cdobjectcl.
  " Get tabnames
  " DD: Interface for reading a view from the ABAP/4 Dictionary
  CALL FUNCTION 'DDIF_VIEW_GET'
    EXPORTING
      name          = vim_view_name
    TABLES
      dd26v_tab     = lt_dd26v
    EXCEPTIONS
      illegal_input = 1
      OTHERS        = 2.
  IF sy-subrc IS NOT INITIAL.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
            WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.
  IF lt_dd26v IS INITIAL.
    APPEND INITIAL LINE TO lrt_tabname ASSIGNING FIELD-SYMBOL().
    -sign   = 'I'.
    -option = 'EQ'.
    -low    = vim_view_name.
  ELSE.
    SORT lt_dd26v BY tabname.
    DELETE ADJACENT DUPLICATES FROM lt_dd26v COMPARING tabname.
    "*-
    LOOP AT lt_dd26v INTO DATA(ls_dd26v).
      APPEND INITIAL LINE TO lrt_tabname ASSIGNING .
      -sign   = 'I'.
      -option = 'EQ'.
      -low    = ls_dd26v-tabname.
    ENDLOOP.
  ENDIF.
  " Objects for change document creation
  SELECT object tabname
    FROM tcdob
    INTO TABLE lt_obj
    WHERE tabname IN lrt_tabname
    ##WARN_OK.
  IF sy-subrc IS NOT INITIAL.
    " No change document objects found
    MESSAGE i899(cd).
    RETURN.
  ENDIF.
  " Information on Include Reports Generated by RSSCD000
  SELECT object reportname
    FROM tcdrp
    INTO TABLE lt_tcdrp
    FOR ALL ENTRIES IN lt_obj
    WHERE object EQ lt_obj-object
    ##WARN_OK.
  IF sy-subrc IS NOT INITIAL.
    " Update program does not yet exist
    MESSAGE i446(m2).
    RETURN.
  ENDIF.
  " View Directory
  SELECT SINGLE area
    FROM tvdir
    INTO lv_fugn
    WHERE tabname EQ vim_view_name.
  "*-
  LOOP AT lt_obj ASSIGNING FIELD-SYMBOL().
    READ TABLE lt_tcdrp ASSIGNING FIELD-SYMBOL()
                        WITH KEY object = -object.
    IF sy-subrc IS NOT INITIAL.
      CONTINUE.
    ENDIF.
    " Split namespace
    CLEAR: lv_namesprog, lv_program.
    lv_program = -reportname.
    CALL FUNCTION 'RS_NAME_SPLIT_NAMESPACE'
      EXPORTING
        name_with_namespace    = lv_program
      IMPORTING
        namespace              = lv_namesprog
        name_without_namespace = lv_program
      EXCEPTIONS
        delimiter_error        = 1
        OTHERS                 = 2.
    IF sy-subrc <> 0.
      lv_program = -reportname.
    ENDIF.
    CLEAR: lv_namesfunc, lv_funcgroup.
    lv_funcgroup = lv_fugn.
    CALL FUNCTION 'RS_NAME_SPLIT_NAMESPACE'
      EXPORTING
        name_with_namespace    = lv_funcgroup
      IMPORTING
        namespace              = lv_namesfunc
        name_without_namespace = lv_funcgroup
      EXCEPTIONS
        delimiter_error        = 1
        OTHERS                 = 2.
    IF sy-subrc <> 0.
      lv_funcgroup = lv_fugn.
    ENDIF.
    " Namespace conversion
    lv_object = -object.
    IF lv_object(1) = '/'.
      SHIFT lv_object LEFT DELETING LEADING '/'.
      REPLACE FIRST OCCURRENCE OF '/' IN lv_object WITH '_'.
    ENDIF.
    lv_table = -tabname.
    IF lv_table(1) = '/'.
      SHIFT lv_table LEFT DELETING LEADING '/'.
      REPLACE FIRST OCCURRENCE OF '/' IN lv_table WITH '_'.
    ENDIF.
    " Subroutine pool
    APPEND ##NO_TEXT:
           `PROGRAM SUBPOOL.` TO lt_ptab,
           `  INCLUDE ` && lv_namesprog  && `F` && lv_program && `CDT.` TO lt_ptab,
           `  INCLUDE ` && lv_namesprog  && `F` && lv_program && `CDC.` TO lt_ptab,
           `  FORM f_process.` TO lt_ptab,
           `    TYPES:  BEGIN OF total.` TO lt_ptab,
           `            INCLUDE STRUCTURE ` && vim_view_name && `.` TO lt_ptab,
           `            INCLUDE STRUCTURE vimflagtab.` TO lt_ptab,
           `    TYPES:  END OF total.` TO lt_ptab,
           `    FIELD-SYMBOLS:        TYPE ANY TABLE,` TO lt_ptab,
           `                       TYPE total,` TO lt_ptab,
           `                       TYPE ANY TABLE,` TO lt_ptab,
           `                    TYPE vimnamtab,` TO lt_ptab,
           `                          TYPE any.` TO lt_ptab,
           `    DATA: lv_tabname(40) TYPE c VALUE '(` && lv_namesfunc && `SAPL` && lv_funcgroup && `)TOTAL[]',` TO lt_ptab,
           `          lv_cond_line  TYPE string,` TO lt_ptab,
           `          lv_cond_line2 TYPE string.` TO lt_ptab,
           `    ASSIGN (lv_tabname) TO .` TO lt_ptab,
           `    LOOP AT  ASSIGNING  CASTING.` TO lt_ptab,
           `      CASE -action.` TO lt_ptab,
           `        WHEN 'U'. " Update` TO lt_ptab,
           `          lv_tabname = '(` && lv_namesfunc && `SAPL` && lv_funcgroup && `)X_NAMTAB[]'.` TO lt_ptab,
           `          ASSIGN (lv_tabname) TO .` TO lt_ptab,
           `          lv_cond_line2 = |keyflag EQ 'X' AND viewfield NE 'MANDT'|.` TO lt_ptab,
           `          LOOP AT  ASSIGNING  WHERE (lv_cond_line2).` TO lt_ptab,
           `            ASSIGN COMPONENT -viewfield OF STRUCTURE  TO .` TO lt_ptab,
           `            IF sy-subrc IS INITIAL.` TO lt_ptab,
           `              lv_cond_line = lv_cond_line && |AND | && ` TO lt_ptab,
           `                             -viewfield && | EQ '| &&  && |' |.` TO lt_ptab,
           `              objectid = objectid && .` TO lt_ptab,
           `              UNASSIGN .` TO lt_ptab,
           `            ENDIF.` TO lt_ptab,
           `          ENDLOOP.` TO lt_ptab,
           `          IF sy-subrc IS INITIAL.` TO lt_ptab,
           `            SHIFT lv_cond_line LEFT BY 3 PLACES.` TO lt_ptab,
           `            SELECT SINGLE *` TO lt_ptab,
           `              FROM ` && -tabname TO lt_ptab,
           `              INTO *` && -tabname TO lt_ptab,
           `              WHERE (lv_cond_line).` TO lt_ptab,
           `            MOVE-CORRESPONDING  TO ` && -tabname && `.` TO lt_ptab,
           `            objectid        = objectid.` TO lt_ptab,
           `            tcode           = sy-tcode.` TO lt_ptab,
           `            udate           = sy-datum.` TO lt_ptab,
           `            utime           = sy-uzeit.` TO lt_ptab,
           `            username        = sy-uname.` TO lt_ptab,
           `            cdoc_upd_object = 'U'.` TO lt_ptab,
           `            upd_` && lv_table && ` = 'U'.` TO lt_ptab,
           `            PERFORM cd_call_` && lv_object && `.` TO lt_ptab,
           `          ENDIF.` TO lt_ptab,
           `        WHEN 'N'. " New` TO lt_ptab,
           `          lv_tabname = '(` && lv_namesfunc && `SAPL` && lv_funcgroup && `)X_NAMTAB[]'.` TO lt_ptab,
           `          ASSIGN (lv_tabname) TO .` TO lt_ptab,
           `          lv_cond_line2 = |keyflag EQ 'X' AND viewfield NE 'MANDT'|.` TO lt_ptab,
           `          LOOP AT  ASSIGNING  WHERE (lv_cond_line2).` TO lt_ptab,
           `            ASSIGN COMPONENT -viewfield OF STRUCTURE  TO .` TO lt_ptab,
           `            IF sy-subrc IS INITIAL.` TO lt_ptab,
           `              objectid = objectid && .` TO lt_ptab,
           `              UNASSIGN .` TO lt_ptab,
           `            ENDIF.` TO lt_ptab,
           `          ENDLOOP.` TO lt_ptab,
           `          IF sy-subrc IS INITIAL.` TO lt_ptab,
           `            MOVE-CORRESPONDING  TO ` && -tabname && `.` TO lt_ptab,
           `            objectid        = objectid.` TO lt_ptab,
           `            tcode           = sy-tcode.` TO lt_ptab,
           `            udate           = sy-datum.` TO lt_ptab,
           `            utime           = sy-uzeit.` TO lt_ptab,
           `            username        = sy-uname.` TO lt_ptab,
           `            cdoc_upd_object = 'I'.` TO lt_ptab,
           `            upd_` && lv_table && ` = 'I'.` TO lt_ptab,
           `            PERFORM cd_call_` && lv_object && `.` TO lt_ptab,
           `          ENDIF.` TO lt_ptab,
           `        WHEN 'D'. " Delete` TO lt_ptab,
           `          lv_tabname = '(` && lv_namesfunc && `SAPL` && lv_funcgroup && `)X_NAMTAB[]'.` TO lt_ptab,
           `          ASSIGN (lv_tabname) TO .` TO lt_ptab,
           `          lv_cond_line2 = |keyflag EQ 'X' AND viewfield NE 'MANDT'|.` TO lt_ptab,
           `          LOOP AT  ASSIGNING  WHERE (lv_cond_line2).` TO lt_ptab,
           `            ASSIGN COMPONENT -viewfield OF STRUCTURE  TO .` TO lt_ptab,
           `            IF sy-subrc IS INITIAL.` TO lt_ptab,
           `              objectid = objectid && .` TO lt_ptab,
           `              UNASSIGN .` TO lt_ptab,
           `            ENDIF.` TO lt_ptab,
           `          ENDLOOP.` TO lt_ptab,
           `          IF sy-subrc IS INITIAL.` TO lt_ptab,
           `            MOVE-CORRESPONDING  TO *` && -tabname && `.` TO lt_ptab,
           `            objectid        = objectid.` TO lt_ptab,
           `            tcode           = sy-tcode.` TO lt_ptab,
           `            udate           = sy-datum.` TO lt_ptab,
           `            utime           = sy-uzeit.` TO lt_ptab,
           `            username        = sy-uname.` TO lt_ptab,
           `            cdoc_upd_object = 'D'.` TO lt_ptab,
           `            upd_` && lv_table && ` = 'D'.` TO lt_ptab,
           `            PERFORM cd_call_` && lv_object && `.` TO lt_ptab,
           `          ENDIF.` TO lt_ptab,
           `      ENDCASE.` TO lt_ptab,
           `      CLEAR: lv_cond_line, lv_cond_line2, objectid, ` && -tabname && `, *` && -tabname && `.` TO lt_ptab,
           `    ENDLOOP.` TO lt_ptab,
           `  ENDFORM.` TO lt_ptab.
    "*-
    GENERATE SUBROUTINE POOL lt_ptab NAME lv_prog
             MESSAGE lv_mess
             SHORTDUMP-ID lv_sid.
    IF sy-subrc = 0.
      PERFORM ('F_PROCESS') IN PROGRAM (lv_prog) IF FOUND.
    ELSEIF sy-subrc = 4.
      MESSAGE lv_mess TYPE 'I'.
    ELSEIF sy-subrc = 8.
      MESSAGE lv_sid TYPE 'I'.
    ENDIF.
    CLEAR: lt_ptab.
  ENDLOOP.
ENDFORM.

FORM AFTER_SAVE.
  COMMIT WORK AND WAIT.
ENDFORM.

        使用程序CHANGEDOCU_READ 查看更改日志

        日志数据记录表 CDHDR、CDPOS

        注意:如果数据元素不勾选change document,则该字段数据变更不会记录

4. 自定义日志

        实际上自定义SCDO文档更改对象也属于自定义日志,只是结合了标准的日志记录功能,当然也可以结合SLG1标准日志来记录表数据更改。

        而完全自定义的更改日志记录,一般为了方便,只是简单使用自建表记录表数据的更改记录,搭建一个具有普适性的更改日志功能,还不如使用标准日志记录更方便。

        如果需求比较特殊,需要搭建完全自定义的日志功能,参考标准日志也是一种选择。

你可能感兴趣的:(功能,ABAP,abap)