Ä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
| Konzept | Beschreibung |
|---|---|
| Änderungsbelegobjekt | Definition der zu protokollierenden Tabellen |
| Änderungsbeleg | Einzelner Protokolleintrag |
| CDHDR | Kopftabelle der Änderungsbelege |
| CDPOS | Positionstabelle (Feldänderungen) |
Wichtige Transaktionen
| Transaktion | Beschreibung |
|---|---|
| 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 setzenls_order_new = ls_order_old.ls_order_new-status = 'C'. " Geändertls_order_new-amount = 1500.
" Objekt-ID für Änderungsbeleglv_objectid = ls_order_old-order_id.
" Änderungsbeleg öffnenCALL FUNCTION 'CHANGEDOCUMENT_OPEN' EXPORTING objectclass = 'ZORDER' objectid = lv_objectid planned_change_number = ' ' planned_or_real_changes = 'R'.
" Änderungen protokollierenCALL 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 speichernCALL 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-ÄnderungCALL 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 anlegenls_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 InsertDATA(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 DeleteDATA(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 öffnenCALL FUNCTION 'CHANGEDOCUMENT_OPEN' EXPORTING objectclass = 'ZORDER' objectid = ls_header_new-order_id.
" Kopfdaten protokollierenCALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE' EXPORTING tablename = 'ZORDER_HEAD' workarea_old = ls_header_old workarea_new = ls_header_new change_indicator = 'U'.
" Positionen protokollierenLOOP 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 PositionenLOOP 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 lesenSELECT * FROM cdhdr INTO TABLE lt_cdhdr WHERE objectclas = 'ZORDER' AND objectid = '1000000001' ORDER BY udate DESCENDING, utime DESCENDING.
" Positionsdaten lesenIF 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.
" AusgabeLOOP 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/DENDCLASS.
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 ausgebencl_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 protokollierenCALL 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 aufrufen2. Objektname eingeben: ZORDER3. Tabellen hinzufügen: - ZORDER_HEAD (Kopftabelle) - ZORDER_ITEM (Positionstabelle)4. Zu protokollierende Felder markieren5. Funktionsbaustein generieren6. Aktivieren und transportierenBatch-Änderungsprotokollierung
" Für MassenänderungenCALL 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 SatzENDLOOP.
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
| Indikator | Beschreibung |
|---|---|
| I | Insert (Neuanlage) |
| U | Update (Änderung) |
| D | Delete (Löschung) |
| E | Einzelfeld-Änderung |
CDPOS-Struktur
| Feld | Beschreibung |
|---|---|
| OBJECTCLAS | Änderungsbelegobjekt |
| OBJECTID | Objekt-ID |
| CHANGENR | Änderungsnummer |
| TABNAME | Tabellenname |
| FNAME | Feldname |
| CHNGIND | Änderungsindikator |
| VALUE_OLD | Alter Wert |
| VALUE_NEW | Neuer Wert |
Best Practices
- Relevante Felder: Nur geschäftsrelevante Felder protokollieren
- Performance: Änderungsbelege im Update-Task schreiben
- Objekt-ID: Eindeutige, sprechende ID verwenden
- Archivierung: Alte Änderungsbelege archivieren (SARA)
- Berechtigungen: Lesezugriff auf Änderungsbelege schützen
- Compliance: Anforderungen an Aufbewahrungsfristen beachten
Verwandte Themen
- Application Logging - Anwendungsprotokollierung
- Lock Objects - Sperrverwaltung
- Berechtigungsprüfungen - Zugriffskontrolle
- BAPI-Entwicklung - Belegverarbeitung