Documentos de cambio en ABAP: Protocolar cambios de datos

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

Los documentos de cambio protocolan automáticamente los cambios de datos en SAP. Permiten el seguimiento completo de cambios en objetos de negocio para auditoría y cumplimiento.

Conceptos de documentos de cambio

ConceptoDescripción
Objeto de documento de cambioDefinición de las tablas a protocolar
Documento de cambioEntrada de protocolo individual
CDHDRTabla de cabecera de documentos de cambio
CDPOSTabla de posiciones (cambios de campos)

Transacciones importantes

TransacciónDescripción
SCDOMantener objetos de documento de cambio
SCU3Mostrar documentos de cambio (genérico)
RSSCD100Evaluar documentos de cambio

Ejemplos básicos

Escribir documento de cambio

DATA: ls_order_old TYPE zorder,
ls_order_new TYPE zorder,
lv_objectid TYPE cdhdr-objectid.
" Leer datos antiguos (antes del cambio)
SELECT SINGLE * FROM zorder INTO ls_order_old
WHERE order_id = '1000000001'.
" Establecer nuevos datos
ls_order_new = ls_order_old.
ls_order_new-status = 'C'. " Modificado
ls_order_new-amount = 1500.
" Object-ID para documento de cambio
lv_objectid = ls_order_old-order_id.
" Abrir documento de cambio
CALL FUNCTION 'CHANGEDOCUMENT_OPEN'
EXPORTING
objectclass = 'ZORDER'
objectid = lv_objectid
planned_change_number = ' '
planned_or_real_changes = 'R'.
" Protocolar cambios
CALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE'
EXPORTING
tablename = 'ZORDER'
workarea_old = ls_order_old
workarea_new = ls_order_new
change_indicator = 'U'. " Update
" Cerrar y guardar documento de cambio
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.
" Cambiar datos realmente
UPDATE zorder FROM ls_order_new.
COMMIT WORK.
ENDIF.

Módulo de funciones generado

" Después de la definición SCDO se genera FB: 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.
" Cambio de registro individual
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 " Datos nuevos
yzorder = lt_yorder " Datos antiguos
EXCEPTIONS
OTHERS = 1.

Cambios para Insert (Creación)

DATA: ls_order_new TYPE zorder.
" Crear nuevo pedido
ls_order_new-order_id = '1000000002'.
ls_order_new-customer_id = 'CUST001'.
ls_order_new-status = 'O'.
ls_order_new-amount = 1000.
" Old-Record vacío indica 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.

Cambios para Delete (Eliminación)

DATA: ls_order_old TYPE zorder.
SELECT SINGLE * FROM zorder INTO ls_order_old
WHERE order_id = '1000000001'.
" New-Record vacío indica 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.

Protocolar múltiples tablas

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.
" Abrir documento de cambio
CALL FUNCTION 'CHANGEDOCUMENT_OPEN'
EXPORTING
objectclass = 'ZORDER'
objectid = ls_header_new-order_id.
" Protocolar datos de cabecera
CALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE'
EXPORTING
tablename = 'ZORDER_HEAD'
workarea_old = ls_header_old
workarea_new = ls_header_new
change_indicator = 'U'.
" Protocolar posiciones
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.
" Posición modificada
CALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE'
EXPORTING
tablename = 'ZORDER_ITEM'
workarea_old = ls_item_old
workarea_new = ls_item_new
change_indicator = 'U'.
ELSE.
" Nueva posición
CALL FUNCTION 'CHANGEDOCUMENT_SINGLE_CASE'
EXPORTING
tablename = 'ZORDER_ITEM'
workarea_old = VALUE zorder_item( )
workarea_new = ls_item_new
change_indicator = 'I'.
ENDIF.
ENDLOOP.
" Posiciones eliminadas
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.

Leer documentos de cambio

DATA: lt_cdhdr TYPE TABLE OF cdhdr,
lt_cdpos TYPE TABLE OF cdpos.
" Leer datos de cabecera
SELECT * FROM cdhdr INTO TABLE lt_cdhdr
WHERE objectclas = 'ZORDER'
AND objectid = '1000000001'
ORDER BY udate DESCENDING, utime DESCENDING.
" Leer datos de posición
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.
" Salida
LOOP AT lt_cdhdr INTO DATA(ls_cdhdr).
WRITE: / 'Cambio del:', ls_cdhdr-udate, ls_cdhdr-utime,
'por:', ls_cdhdr-username.
LOOP AT lt_cdpos INTO DATA(ls_cdpos)
WHERE changenr = ls_cdhdr-changenr.
WRITE: / ' Campo:', ls_cdpos-fname,
'Anterior:', ls_cdpos-value_old(20),
'Nuevo:', ls_cdpos-value_new(20).
ENDLOOP.
ENDLOOP.

Documentos de cambio con clase

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.

Mostrar documentos de cambio vía ALV

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.
" Mostrar ALV
cl_salv_table=>factory(
IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = lt_display ).
lo_alv->display( ).

Protocolar cambios de texto

" Para textos largos (ej. notas)
DATA: lv_text_old TYPE string VALUE 'Texto antiguo',
lv_text_new TYPE string VALUE 'Texto nuevo más largo'.
CALL FUNCTION 'CHANGEDOCUMENT_OPEN'
EXPORTING
objectclass = 'ZORDER'
objectid = lv_objectid.
" Protocolar cambio de texto
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.

Definir objeto de documento de cambio en SCDO

1. Llamar transacción SCDO
2. Ingresar nombre de objeto: ZORDER
3. Agregar tablas:
- ZORDER_HEAD (Tabla de cabecera)
- ZORDER_ITEM (Tabla de posiciones)
4. Marcar campos a protocolar
5. Generar módulo de funciones
6. Activar y transportar

Protocolización de cambios masivos

" Para cambios masivos
CALL FUNCTION 'CHANGEDOCUMENT_OPEN'
EXPORTING
objectclass = 'ZORDER'
objectid = '*MASS*'. " ID especial
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. " Por registro
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.

Indicadores de cambio

IndicadorDescripción
IInsert (Creación)
UUpdate (Modificación)
DDelete (Eliminación)
ECambio de campo individual

Estructura CDPOS

CampoDescripción
OBJECTCLASObjeto de documento de cambio
OBJECTIDID de objeto
CHANGENRNúmero de cambio
TABNAMENombre de tabla
FNAMENombre de campo
CHNGINDIndicador de cambio
VALUE_OLDValor anterior
VALUE_NEWValor nuevo

Mejores prácticas

  1. Campos relevantes: Solo protocolar campos relevantes para el negocio
  2. Rendimiento: Escribir documentos de cambio en update task
  3. Object-ID: Usar ID único y descriptivo
  4. Archivado: Archivar documentos de cambio antiguos (SARA)
  5. Permisos: Proteger acceso de lectura a documentos de cambio
  6. Cumplimiento: Observar requisitos de períodos de retención

Temas relacionados