ABAP Änderungsbelege: Datenänderungen protokollieren

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

Änderungsbelege protokollieren Datenänderungen in SAP automatisch. Sie ermöglichen die lückenlose Nachverfolgung von Änderungen an Geschäftsobjekten für Audit und Compliance.

Änderungsbeleg-Konzepte

KonzeptBeschreibung
ÄnderungsbelegobjektDefinition der zu protokollierenden Tabellen
ÄnderungsbelegEinzelner Protokolleintrag
CDHDRKopftabelle der Änderungsbelege
CDPOSPositionstabelle (Feldänderungen)

Wichtige Transaktionen

TransaktionBeschreibung
SCDOÄnderungsbelegobjekte pflegen
SCU3Änderungsbelege anzeigen (generisch)
RSSCD100Änderungsbelege auswerten

Grundlegende Beispiele

Änderungsbeleg schreiben

DATA: ls_order_old TYPE zorder,
ls_order_new TYPE zorder,
lv_objectid TYPE cdhdr-objectid.
" Alte Daten lesen (vor Änderung)
SELECT SINGLE * FROM zorder INTO ls_order_old
WHERE order_id = '1000000001'.
" Neue Daten setzen
ls_order_new = ls_order_old.
ls_order_new-status = 'C'. " Geändert
ls_order_new-amount = 1500.
" Objekt-ID für Änderungsbeleg
lv_objectid = ls_order_old-order_id.
" Änderungsbeleg öffnen
CALL FUNCTION 'CHANGEDOCUMENT_OPEN'
EXPORTING
objectclass = 'ZORDER'
objectid = lv_objectid
planned_change_number = ' '
planned_or_real_changes = 'R'.
" Änderungen protokollieren
CALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE'
EXPORTING
tablename = 'ZORDER'
workarea_old = ls_order_old
workarea_new = ls_order_new
change_indicator = 'U'. " Update
" Änderungsbeleg schließen und speichern
CALL FUNCTION 'CHANGEDOCUMENT_CLOSE'
EXPORTING
objectclass = 'ZORDER'
objectid = lv_objectid
date_of_change = sy-datum
time_of_change = sy-uzeit
tcode = sy-tcode
username = sy-uname
EXCEPTIONS
no_position_found = 1
OTHERS = 2.
IF sy-subrc = 0.
" Daten tatsächlich ändern
UPDATE zorder FROM ls_order_new.
COMMIT WORK.
ENDIF.

Generierter Funktionsbaustein

" Nach SCDO-Definition wird FB generiert: ZORDER_WRITE_DOCUMENT
DATA: ls_order_old TYPE zorder,
ls_order_new TYPE zorder,
lt_xorder TYPE TABLE OF zorder,
lt_yorder TYPE TABLE OF zorder.
" Einzelsatz-Änderung
CALL FUNCTION 'ZORDER_WRITE_DOCUMENT'
EXPORTING
objectid = ls_order_new-order_id
tcode = sy-tcode
utime = sy-uzeit
udate = sy-datum
username = sy-uname
upd_zorder = 'U' " Update
TABLES
xzorder = lt_xorder " Neue Daten
yzorder = lt_yorder " Alte Daten
EXCEPTIONS
OTHERS = 1.

Änderungen für Insert (Neuanlage)

DATA: ls_order_new TYPE zorder.
" Neue Bestellung anlegen
ls_order_new-order_id = '1000000002'.
ls_order_new-customer_id = 'CUST001'.
ls_order_new-status = 'O'.
ls_order_new-amount = 1000.
" Leerer Old-Record signalisiert Insert
DATA(ls_order_old) = VALUE zorder( ).
CALL FUNCTION 'CHANGEDOCUMENT_OPEN'
EXPORTING
objectclass = 'ZORDER'
objectid = ls_order_new-order_id.
CALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE'
EXPORTING
tablename = 'ZORDER'
workarea_old = ls_order_old
workarea_new = ls_order_new
change_indicator = 'I'. " Insert
CALL FUNCTION 'CHANGEDOCUMENT_CLOSE'
EXPORTING
objectclass = 'ZORDER'
objectid = ls_order_new-order_id
date_of_change = sy-datum
time_of_change = sy-uzeit
tcode = sy-tcode
username = sy-uname.
INSERT zorder FROM ls_order_new.
COMMIT WORK.

Änderungen für Delete (Löschung)

DATA: ls_order_old TYPE zorder.
SELECT SINGLE * FROM zorder INTO ls_order_old
WHERE order_id = '1000000001'.
" Leerer New-Record signalisiert Delete
DATA(ls_order_new) = VALUE zorder( ).
CALL FUNCTION 'CHANGEDOCUMENT_OPEN'
EXPORTING
objectclass = 'ZORDER'
objectid = ls_order_old-order_id.
CALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE'
EXPORTING
tablename = 'ZORDER'
workarea_old = ls_order_old
workarea_new = ls_order_new
change_indicator = 'D'. " Delete
CALL FUNCTION 'CHANGEDOCUMENT_CLOSE'
EXPORTING
objectclass = 'ZORDER'
objectid = ls_order_old-order_id
date_of_change = sy-datum
time_of_change = sy-uzeit
tcode = sy-tcode
username = sy-uname.
DELETE FROM zorder WHERE order_id = ls_order_old-order_id.
COMMIT WORK.

Mehrere Tabellen protokollieren

DATA: ls_header_old TYPE zorder_head,
ls_header_new TYPE zorder_head,
lt_items_old TYPE TABLE OF zorder_item,
lt_items_new TYPE TABLE OF zorder_item.
" Änderungsbeleg öffnen
CALL FUNCTION 'CHANGEDOCUMENT_OPEN'
EXPORTING
objectclass = 'ZORDER'
objectid = ls_header_new-order_id.
" Kopfdaten protokollieren
CALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE'
EXPORTING
tablename = 'ZORDER_HEAD'
workarea_old = ls_header_old
workarea_new = ls_header_new
change_indicator = 'U'.
" Positionen protokollieren
LOOP AT lt_items_new INTO DATA(ls_item_new).
READ TABLE lt_items_old INTO DATA(ls_item_old)
WITH KEY item_id = ls_item_new-item_id.
IF sy-subrc = 0.
" Position geändert
CALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE'
EXPORTING
tablename = 'ZORDER_ITEM'
workarea_old = ls_item_old
workarea_new = ls_item_new
change_indicator = 'U'.
ELSE.
" Neue Position
CALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE'
EXPORTING
tablename = 'ZORDER_ITEM'
workarea_old = VALUE zorder_item( )
workarea_new = ls_item_new
change_indicator = 'I'.
ENDIF.
ENDLOOP.
" Gelöschte Positionen
LOOP AT lt_items_old INTO ls_item_old.
READ TABLE lt_items_new TRANSPORTING NO FIELDS
WITH KEY item_id = ls_item_old-item_id.
IF sy-subrc <> 0.
CALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE'
EXPORTING
tablename = 'ZORDER_ITEM'
workarea_old = ls_item_old
workarea_new = VALUE zorder_item( )
change_indicator = 'D'.
ENDIF.
ENDLOOP.
CALL FUNCTION 'CHANGEDOCUMENT_CLOSE'
EXPORTING
objectclass = 'ZORDER'
objectid = ls_header_new-order_id
date_of_change = sy-datum
time_of_change = sy-uzeit
tcode = sy-tcode
username = sy-uname.

Änderungsbelege lesen

DATA: lt_cdhdr TYPE TABLE OF cdhdr,
lt_cdpos TYPE TABLE OF cdpos.
" Kopfdaten lesen
SELECT * FROM cdhdr INTO TABLE lt_cdhdr
WHERE objectclas = 'ZORDER'
AND objectid = '1000000001'
ORDER BY udate DESCENDING, utime DESCENDING.
" Positionsdaten lesen
IF lt_cdhdr IS NOT INITIAL.
SELECT * FROM cdpos INTO TABLE lt_cdpos
FOR ALL ENTRIES IN lt_cdhdr
WHERE objectclas = lt_cdhdr-objectclas
AND objectid = lt_cdhdr-objectid
AND changenr = lt_cdhdr-changenr.
ENDIF.
" Ausgabe
LOOP AT lt_cdhdr INTO DATA(ls_cdhdr).
WRITE: / 'Änderung am:', ls_cdhdr-udate, ls_cdhdr-utime,
'von:', ls_cdhdr-username.
LOOP AT lt_cdpos INTO DATA(ls_cdpos)
WHERE changenr = ls_cdhdr-changenr.
WRITE: / ' Feld:', ls_cdpos-fname,
'Alt:', ls_cdpos-value_old(20),
'Neu:', ls_cdpos-value_new(20).
ENDLOOP.
ENDLOOP.

Änderungsbelege mit Klasse

CLASS zcl_change_document DEFINITION.
PUBLIC SECTION.
METHODS log_change
IMPORTING
iv_objectid TYPE cdhdr-objectid
is_old TYPE any
is_new TYPE any
iv_tablename TYPE tabname
iv_operation TYPE cdchngind. " I/U/D
ENDCLASS.
CLASS zcl_change_document IMPLEMENTATION.
METHOD log_change.
CALL FUNCTION 'CHANGEDOCUMENT_OPEN'
EXPORTING
objectclass = 'ZORDER'
objectid = iv_objectid.
CALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE'
EXPORTING
tablename = iv_tablename
workarea_old = is_old
workarea_new = is_new
change_indicator = iv_operation.
CALL FUNCTION 'CHANGEDOCUMENT_CLOSE'
EXPORTING
objectclass = 'ZORDER'
objectid = iv_objectid
date_of_change = sy-datum
time_of_change = sy-uzeit
tcode = sy-tcode
username = sy-uname
EXCEPTIONS
no_position_found = 1
OTHERS = 2.
ENDMETHOD.
ENDCLASS.

Änderungsbelege per ALV anzeigen

DATA: lt_display TYPE TABLE OF ty_change_display.
SELECT h~objectid, h~udate, h~utime, h~username, h~tcode,
p~tabname, p~fname, p~chngind,
p~value_old, p~value_new
FROM cdhdr AS h
INNER JOIN cdpos AS p ON h~objectclas = p~objectclas
AND h~objectid = p~objectid
AND h~changenr = p~changenr
WHERE h~objectclas = 'ZORDER'
AND h~udate BETWEEN @lv_from_date AND @lv_to_date
INTO CORRESPONDING FIELDS OF TABLE @lt_display
ORDER BY h~udate DESCENDING, h~utime DESCENDING.
" ALV ausgeben
cl_salv_table=>factory(
IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = lt_display ).
lo_alv->display( ).

Textänderungen protokollieren

" Für lange Texte (z.B. Notizen)
DATA: lv_text_old TYPE string VALUE 'Alter Text',
lv_text_new TYPE string VALUE 'Neuer längerer Text'.
CALL FUNCTION 'CHANGEDOCUMENT_OPEN'
EXPORTING
objectclass = 'ZORDER'
objectid = lv_objectid.
" Textänderung protokollieren
CALL FUNCTION 'CHANGEDOCUMENT_TEXT_CASE'
EXPORTING
textcase = 'X'
text_old = lv_text_old
text_new = lv_text_new
tablename = 'ZORDER'
fieldname = 'NOTES'
change_indicator = 'U'.
CALL FUNCTION 'CHANGEDOCUMENT_CLOSE'
EXPORTING
objectclass = 'ZORDER'
objectid = lv_objectid
date_of_change = sy-datum
time_of_change = sy-uzeit
tcode = sy-tcode
username = sy-uname.

Änderungsbelegobjekt in SCDO definieren

1. Transaktion SCDO aufrufen
2. Objektname eingeben: ZORDER
3. Tabellen hinzufügen:
- ZORDER_HEAD (Kopftabelle)
- ZORDER_ITEM (Positionstabelle)
4. Zu protokollierende Felder markieren
5. Funktionsbaustein generieren
6. Aktivieren und transportieren

Batch-Änderungsprotokollierung

" Für Massenänderungen
CALL FUNCTION 'CHANGEDOCUMENT_OPEN'
EXPORTING
objectclass = 'ZORDER'
objectid = '*MASS*'. " Spezielle ID
LOOP AT lt_changes INTO DATA(ls_change).
CALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE'
EXPORTING
tablename = 'ZORDER'
workarea_old = ls_change-old_data
workarea_new = ls_change-new_data
change_indicator = 'U'
objectid = ls_change-order_id. " Pro Satz
ENDLOOP.
CALL FUNCTION 'CHANGEDOCUMENT_CLOSE'
EXPORTING
objectclass = 'ZORDER'
objectid = '*MASS*'
date_of_change = sy-datum
time_of_change = sy-uzeit
tcode = sy-tcode
username = sy-uname.

Änderungsindikatoren

IndikatorBeschreibung
IInsert (Neuanlage)
UUpdate (Änderung)
DDelete (Löschung)
EEinzelfeld-Änderung

CDPOS-Struktur

FeldBeschreibung
OBJECTCLASÄnderungsbelegobjekt
OBJECTIDObjekt-ID
CHANGENRÄnderungsnummer
TABNAMETabellenname
FNAMEFeldname
CHNGINDÄnderungsindikator
VALUE_OLDAlter Wert
VALUE_NEWNeuer Wert

Best Practices

  1. Relevante Felder: Nur geschäftsrelevante Felder protokollieren
  2. Performance: Änderungsbelege im Update-Task schreiben
  3. Objekt-ID: Eindeutige, sprechende ID verwenden
  4. Archivierung: Alte Änderungsbelege archivieren (SARA)
  5. Berechtigungen: Lesezugriff auf Änderungsbelege schützen
  6. Compliance: Anforderungen an Aufbewahrungsfristen beachten

Verwandte Themen