Migración de campos de sistema: Ejemplos prácticos antes/después para ABAP Cloud

Kategorie
ABAP Cloud
Veröffentlicht
Autor
Johannes

La migración de campos SY a APIs ABAP Cloud es uno de los pasos más frecuentes en la modernización de código ABAP. Este artículo muestra ejemplos concretos antes/después para 15 casos de uso comunes, una clase utility reutilizable y errores típicos a evitar.

Fundamentos sobre campos SY y sus alternativas los encontrarás en el artículo Campos de sistema en ABAP Cloud: Alternativas a SY-*.

Resumen: La migración de un vistazo

┌─────────────────────────────────────────────────────────────┐
│ Migración de campos SY │
│ ─────────────────────────────────────────────────────────── │
│ │
│ 1. Ejecutar ATC → encuentra todos los problemas SY-* │
│ 2. Crear clase Utility → reemplazo centralizado │
│ 3. Buscar y reemplazar → migrar sistemáticamente │
│ 4. Unit Tests → validar comportamiento │
│ │
│ ✅ ¡SY-SUBRC, SY-TABIX, SY-DBCNT permanecen sin cambios! │
└─────────────────────────────────────────────────────────────┘

Parte 1: 15 ejemplos antes/después

1. Nombre de usuario (SY-UNAME)

" ❌ Classic ABAP - no Cloud-ready
DATA(lv_user) = sy-uname.
ls_document-created_by = sy-uname.
WHERE created_by = @sy-uname
" ✅ ABAP Cloud - migración directa
DATA(lv_user) = cl_abap_context_info=>get_user_technical_name( ).
ls_document-created_by = cl_abap_context_info=>get_user_technical_name( ).
WHERE created_by = @( cl_abap_context_info=>get_user_technical_name( ) )

2. Fecha del sistema (SY-DATUM)

" ❌ Classic ABAP
DATA(lv_today) = sy-datum.
IF ls_booking-flight_date < sy-datum.
" Expirado
ENDIF.
" ✅ ABAP Cloud
DATA(lv_today) = cl_abap_context_info=>get_system_date( ).
IF ls_booking-flight_date < cl_abap_context_info=>get_system_date( ).
" Expirado
ENDIF.

3. Hora del sistema (SY-UZEIT)

" ❌ Classic ABAP
DATA(lv_time) = sy-uzeit.
ls_log-logged_at_time = sy-uzeit.
" ✅ ABAP Cloud
DATA(lv_time) = cl_abap_context_info=>get_system_time( ).
ls_log-logged_at_time = cl_abap_context_info=>get_system_time( ).

4. UTC Timestamp (GET TIME STAMP)

" ❌ Classic ABAP
DATA: lv_timestamp TYPE timestamp.
GET TIME STAMP FIELD lv_timestamp.
" ✅ ABAP Cloud - más legible y type-safe
DATA(lv_timestamp) = cl_abap_context_info=>get_system_timestamp( ).
" Nota: El tipo de retorno es UTCLONG (recomendado para base de datos)

5. Idioma de inicio de sesión (SY-LANGU)

" ❌ Classic ABAP
SELECT SINGLE text FROM ztab_texts
WHERE langu = @sy-langu
INTO @DATA(lv_text).
" ✅ ABAP Cloud
SELECT SINGLE text FROM ztab_texts
WHERE langu = @( cl_abap_context_info=>get_user_language_abap_format( ) )
INTO @DATA(lv_text).

6. Zona horaria (SY-ZONLO)

" ❌ Classic ABAP
DATA(lv_timezone) = sy-zonlo.
CONVERT TIME STAMP lv_utc TIME ZONE sy-zonlo
INTO DATE lv_local_date TIME lv_local_time.
" ✅ ABAP Cloud
DATA(lv_timezone) = cl_abap_context_info=>get_user_time_zone( ).
CONVERT TIME STAMP lv_utc TIME ZONE cl_abap_context_info=>get_user_time_zone( )
INTO DATE lv_local_date TIME lv_local_time.

7. Mandante (SY-MANDT)

" ❌ Classic ABAP
DATA(lv_client) = sy-mandt.
lv_table_key = |{ sy-mandt }{ lv_id }|.
" ✅ ABAP Cloud
DATA(lv_client) = cl_abap_context_info=>get_client( ).
lv_table_key = |{ cl_abap_context_info=>get_client( ) }{ lv_id }|.

8. ID del sistema (SY-SYSID)

" ❌ Classic ABAP
IF sy-sysid = 'PRD'.
" Sistema de producción
ENDIF.
" ✅ ABAP Cloud
IF cl_abap_context_info=>get_system_id( ) = 'PRD'.
" Sistema de producción
ENDIF.

9. Audit-Logging con usuario y timestamp

" ❌ Classic ABAP
ls_audit_log = VALUE #(
user_name = sy-uname
log_date = sy-datum
log_time = sy-uzeit
client = sy-mandt
).
" ✅ ABAP Cloud - completamente migrado
ls_audit_log = VALUE #(
user_name = cl_abap_context_info=>get_user_technical_name( )
timestamp = cl_abap_context_info=>get_system_timestamp( )
client = cl_abap_context_info=>get_client( )
).

10. Nombre de usuario formateado para UI

" ❌ Classic ABAP - sin equivalente directo
" A menudo se mostraba SY-UNAME directamente: "JSMITH"
" ✅ ABAP Cloud - mejor representación posible
" Nombre formateado: "John Smith"
DATA(lv_display_name) = cl_abap_context_info=>get_user_formatted_name( ).
" O alias de usuario (a menudo E-Mail)
DATA(lv_alias) = cl_abap_context_info=>get_user_alias( ).

11. Cargar textos multilingües

" ❌ Classic ABAP
SELECT text FROM ztab_translations
WHERE text_id = @lv_text_id
AND langu = @sy-langu
INTO TABLE @DATA(lt_texts).
" ✅ ABAP Cloud
DATA(lv_langu) = cl_abap_context_info=>get_user_language_abap_format( ).
SELECT text FROM ztab_translations
WHERE text_id = @lv_text_id
AND langu = @lv_langu
INTO TABLE @DATA(lt_texts).

12. Enlace de email con URL del sistema

" ❌ Classic ABAP - URL del sistema configurada manualmente
DATA(lv_url) = |https://mycompany.sap.com/sap/bc/ui5_ui5/...|.
" ✅ ABAP Cloud - URL del sistema dinámica
DATA(lv_base_url) = cl_abap_context_info=>get_system_url( ).
DATA(lv_deep_link) = |{ lv_base_url }/sap/bc/ui5_ui5/ui2/ushell/shells/abap/FioriLaunchpad.html#ZBooking-manage&/Booking/{ lv_booking_uuid }|.

13. Cálculo de fecha con fecha actual

" ❌ Classic ABAP
DATA(lv_deadline) = sy-datum + 30.
DATA(lv_last_month) = sy-datum - 30.
" ✅ ABAP Cloud
DATA(lv_today) = cl_abap_context_info=>get_system_date( ).
DATA(lv_deadline) = lv_today + 30.
DATA(lv_last_month) = lv_today - 30.

14. Timestamp para RAP Determination

" ❌ Classic ABAP en contexto RAP
METHOD set_timestamps.
DATA: lv_timestamp TYPE timestamp.
GET TIME STAMP FIELD lv_timestamp.
MODIFY ENTITIES OF zi_booking IN LOCAL MODE
ENTITY Booking
UPDATE FIELDS ( created_at last_changed_at )
WITH VALUE #( FOR key IN keys
( %tky = key-%tky
created_at = lv_timestamp
last_changed_at = lv_timestamp ) ).
ENDMETHOD.
" ✅ ABAP Cloud
METHOD set_timestamps.
DATA(lv_timestamp) = cl_abap_context_info=>get_system_timestamp( ).
MODIFY ENTITIES OF zi_booking IN LOCAL MODE
ENTITY Booking
UPDATE FIELDS ( created_at last_changed_at )
WITH VALUE #( FOR key IN keys
( %tky = key-%tky
created_at = lv_timestamp
last_changed_at = lv_timestamp ) ).
ENDMETHOD.

15. Verificación de fecha de expiración

" ❌ Classic ABAP
IF ls_license-valid_to < sy-datum.
RAISE EXCEPTION TYPE zcx_license_expired.
ENDIF.
" ✅ ABAP Cloud
IF ls_license-valid_to < cl_abap_context_info=>get_system_date( ).
RAISE EXCEPTION TYPE zcx_license_expired.
ENDIF.

Parte 2: Clase Utility reutilizable

Una clase Utility centralizada simplifica la migración y aumenta la testabilidad mediante Dependency Injection.

CLASS zcl_context_helper DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
TYPES: BEGIN OF ty_context,
user_name TYPE syuname,
user_formatted TYPE string,
user_alias TYPE string,
system_date TYPE d,
system_time TYPE t,
timestamp TYPE utclong,
time_zone TYPE timezone,
language TYPE sy-langu,
language_iso TYPE laiso,
system_id TYPE sy-sysid,
client TYPE sy-mandt,
system_url TYPE string,
END OF ty_context.
" Patrón Singleton para acceso fácil
CLASS-METHODS get_instance
RETURNING VALUE(ro_instance) TYPE REF TO zcl_context_helper.
" Toda la información de contexto de una vez
METHODS get_full_context
RETURNING VALUE(rs_context) TYPE ty_context.
" Métodos individuales para accesos frecuentes
METHODS get_user
RETURNING VALUE(rv_user) TYPE syuname.
METHODS get_timestamp
RETURNING VALUE(rv_timestamp) TYPE utclong.
METHODS get_date
RETURNING VALUE(rv_date) TYPE d.
METHODS get_time
RETURNING VALUE(rv_time) TYPE t.
METHODS get_language
RETURNING VALUE(rv_language) TYPE sy-langu.
METHODS get_timezone
RETURNING VALUE(rv_timezone) TYPE timezone.
" Para Unit Tests: Sobrescribir valores
METHODS set_mock_user
IMPORTING iv_user TYPE syuname.
METHODS set_mock_timestamp
IMPORTING iv_timestamp TYPE utclong.
METHODS set_mock_date
IMPORTING iv_date TYPE d.
METHODS clear_mocks.
PRIVATE SECTION.
CLASS-DATA go_instance TYPE REF TO zcl_context_helper.
" Valores mock para Unit Tests
DATA mv_mock_user TYPE syuname.
DATA mv_mock_timestamp TYPE utclong.
DATA mv_mock_date TYPE d.
DATA mv_use_mocks TYPE abap_bool.
ENDCLASS.
CLASS zcl_context_helper IMPLEMENTATION.
METHOD get_instance.
IF go_instance IS NOT BOUND.
go_instance = NEW #( ).
ENDIF.
ro_instance = go_instance.
ENDMETHOD.
METHOD get_full_context.
rs_context = VALUE #(
user_name = get_user( )
user_formatted = cl_abap_context_info=>get_user_formatted_name( )
user_alias = cl_abap_context_info=>get_user_alias( )
system_date = get_date( )
system_time = get_time( )
timestamp = get_timestamp( )
time_zone = get_timezone( )
language = get_language( )
language_iso = cl_abap_context_info=>get_user_language_iso_format( )
system_id = cl_abap_context_info=>get_system_id( )
client = cl_abap_context_info=>get_client( )
system_url = cl_abap_context_info=>get_system_url( )
).
ENDMETHOD.
METHOD get_user.
IF mv_use_mocks = abap_true AND mv_mock_user IS NOT INITIAL.
rv_user = mv_mock_user.
ELSE.
rv_user = cl_abap_context_info=>get_user_technical_name( ).
ENDIF.
ENDMETHOD.
METHOD get_timestamp.
IF mv_use_mocks = abap_true AND mv_mock_timestamp IS NOT INITIAL.
rv_timestamp = mv_mock_timestamp.
ELSE.
rv_timestamp = cl_abap_context_info=>get_system_timestamp( ).
ENDIF.
ENDMETHOD.
METHOD get_date.
IF mv_use_mocks = abap_true AND mv_mock_date IS NOT INITIAL.
rv_date = mv_mock_date.
ELSE.
rv_date = cl_abap_context_info=>get_system_date( ).
ENDIF.
ENDMETHOD.
METHOD get_time.
rv_time = cl_abap_context_info=>get_system_time( ).
ENDMETHOD.
METHOD get_language.
rv_language = cl_abap_context_info=>get_user_language_abap_format( ).
ENDMETHOD.
METHOD get_timezone.
rv_timezone = cl_abap_context_info=>get_user_time_zone( ).
ENDMETHOD.
METHOD set_mock_user.
mv_mock_user = iv_user.
mv_use_mocks = abap_true.
ENDMETHOD.
METHOD set_mock_timestamp.
mv_mock_timestamp = iv_timestamp.
mv_use_mocks = abap_true.
ENDMETHOD.
METHOD set_mock_date.
mv_mock_date = iv_date.
mv_use_mocks = abap_true.
ENDMETHOD.
METHOD clear_mocks.
CLEAR: mv_mock_user, mv_mock_timestamp, mv_mock_date.
mv_use_mocks = abap_false.
ENDMETHOD.
METHOD if_oo_adt_classrun~main.
" Demo output
DATA(ls_context) = get_instance( )->get_full_context( ).
out->write( |Usuario: { ls_context-user_name }| ).
out->write( |Fecha: { ls_context-system_date }| ).
out->write( |Hora: { ls_context-system_time }| ).
out->write( |Timestamp: { ls_context-timestamp }| ).
out->write( |Idioma: { ls_context-language }| ).
out->write( |System-ID: { ls_context-system_id }| ).
ENDMETHOD.
ENDCLASS.

Uso de la clase Utility

" En lugar de llamada directa a CL_ABAP_CONTEXT_INFO
DATA(lo_ctx) = zcl_context_helper=>get_instance( ).
" Valores individuales
DATA(lv_user) = lo_ctx->get_user( ).
DATA(lv_timestamp) = lo_ctx->get_timestamp( ).
" Contexto completo
DATA(ls_context) = lo_ctx->get_full_context( ).

Parte 3: Errores comunes y soluciones

Error 1: Múltiples llamadas a API

" ❌ Ineficiente: API se llama 3x
INSERT INTO ztab VALUES @( VALUE #(
created_by = cl_abap_context_info=>get_user_technical_name( )
created_at = cl_abap_context_info=>get_system_timestamp( )
changed_by = cl_abap_context_info=>get_user_technical_name( )
changed_at = cl_abap_context_info=>get_system_timestamp( )
) ).
" ✅ Eficiente: Obtener valores una vez
DATA(lv_user) = cl_abap_context_info=>get_user_technical_name( ).
DATA(lv_timestamp) = cl_abap_context_info=>get_system_timestamp( ).
INSERT INTO ztab VALUES @( VALUE #(
created_by = lv_user
created_at = lv_timestamp
changed_by = lv_user
changed_at = lv_timestamp
) ).

Error 2: Zona horaria en comparaciones de fecha

" ❌ Problema: Hora del sistema vs. hora local
DATA(lv_system_date) = cl_abap_context_info=>get_system_date( ).
" ¡En diferentes zonas horarias puede ser la fecha incorrecta!
" ✅ Solución: Considerar zona horaria explícitamente
DATA: lv_local_date TYPE d,
lv_local_time TYPE t.
CONVERT TIME STAMP cl_abap_context_info=>get_system_timestamp( )
TIME ZONE cl_abap_context_info=>get_user_time_zone( )
INTO DATE lv_local_date TIME lv_local_time.
" Ahora lv_local_date es la fecha desde la perspectiva del usuario

Error 3: SY-SUBRC después de llamada a API

" ❌ Suposición incorrecta: SY-SUBRC se establece por API
DATA(lv_user) = cl_abap_context_info=>get_user_technical_name( ).
IF sy-subrc <> 0.
" ¡Nunca se ejecuta - la API no establece SY-SUBRC!
ENDIF.
" ✅ Correcto: API no tiene caso de error
" CL_ABAP_CONTEXT_INFO siempre devuelve un valor
DATA(lv_user) = cl_abap_context_info=>get_user_technical_name( ).
" No se necesita verificación

Error 4: Modo Batch (SY-BATCH)

" ❌ Classic ABAP: Verificar modo batch
IF sy-batch = abap_true.
" Procesamiento en segundo plano
ENDIF.
" ✅ ABAP Cloud: No hay SY-BATCH
" En ABAP Cloud no hay reports clásicos
" Application Jobs tienen mecanismos propios
" Si es necesario: Controlar vía parámetros propios
CLASS zcl_my_job DEFINITION.
PUBLIC SECTION.
METHODS execute
IMPORTING iv_is_background TYPE abap_bool DEFAULT abap_false.
ENDCLASS.

Error 5: Verificación de sistema hardcodeada

" ❌ Problemático: IDs de sistema hardcodeados
IF cl_abap_context_info=>get_system_id( ) = 'PRD'.
" Código específico de producción
ENDIF.
" ✅ Mejor: Basado en configuración vía Customizing
" En ABAP Cloud: Usar Communication Scenario o tabla
SELECT SINGLE is_production FROM ztab_config
WHERE system_id = @( cl_abap_context_info=>get_system_id( ) )
INTO @DATA(lv_is_production).

Unit Tests con valores Mock

La clase Utility permite testing fácil:

CLASS ltc_booking_test DEFINITION FINAL FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO zcl_booking_handler.
DATA mo_context TYPE REF TO zcl_context_helper.
METHODS setup.
METHODS teardown.
METHODS test_booking_with_mocked_date FOR TESTING.
ENDCLASS.
CLASS ltc_booking_test IMPLEMENTATION.
METHOD setup.
mo_context = zcl_context_helper=>get_instance( ).
mo_cut = NEW #( ).
" Establecer valores mock
mo_context->set_mock_date( '20260301' ).
mo_context->set_mock_user( 'TESTUSER' ).
mo_context->set_mock_timestamp( CONV utclong( '2026-03-01 10:00:00' ) ).
ENDMETHOD.
METHOD teardown.
mo_context->clear_mocks( ).
ENDMETHOD.
METHOD test_booking_with_mocked_date.
" Given: Reserva para mañana
DATA(ls_booking) = VALUE zif_booking=>ty_booking(
flight_date = '20260302'
).
" When: Ejecutar validación
DATA(lv_is_valid) = mo_cut->validate_booking( ls_booking ).
" Then: Reserva es válida (fecha en el futuro)
cl_abap_unit_assert=>assert_true( lv_is_valid ).
ENDMETHOD.
ENDCLASS.

Checklist para la migración

## Checklist de migración de campos SY
### Preparación
- [ ] Ejecutar ATC con ABAP_CLOUD_READINESS
- [ ] Crear clase Utility ZCL_CONTEXT_HELPER
- [ ] Identificar todos los objetos afectados
### Migración por objeto
- [ ] SY-UNAME → get_user_technical_name( )
- [ ] SY-DATUM → get_system_date( )
- [ ] SY-UZEIT → get_system_time( )
- [ ] SY-LANGU → get_user_language_abap_format( )
- [ ] SY-ZONLO → get_user_time_zone( )
- [ ] SY-MANDT → get_client( )
- [ ] SY-SYSID → get_system_id( )
- [ ] GET TIME STAMP → get_system_timestamp( )
### Aseguramiento de calidad
- [ ] Unit Tests adaptados (Mocking)
- [ ] Ejecutar ATC nuevamente
- [ ] Realizar pruebas manuales

Conclusión

La migración de campos SY sigue patrones claros:

  1. Reemplazo directo para la mayoría de casos con CL_ABAP_CONTEXT_INFO
  2. Clase Utility para gestión centralizada y testabilidad
  3. Cachear valores en uso múltiple
  4. Considerar zonas horarias en comparaciones de fechas

El cambio es técnicamente simple, pero requiere un enfoque sistemático. ATC ayuda a encontrar todos los lugares.

Para una visión completa de todos los campos SY consulta Campos de sistema en ABAP Cloud. Más sobre la estrategia Clean Core en Concepto de niveles Clean Core y para implementación práctica en la Checklist de implementación.