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-readyDATA(lv_user) = sy-uname.ls_document-created_by = sy-uname.WHERE created_by = @sy-uname
" ✅ ABAP Cloud - migración directaDATA(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 ABAPDATA(lv_today) = sy-datum.IF ls_booking-flight_date < sy-datum. " ExpiradoENDIF.
" ✅ ABAP CloudDATA(lv_today) = cl_abap_context_info=>get_system_date( ).IF ls_booking-flight_date < cl_abap_context_info=>get_system_date( ). " ExpiradoENDIF.3. Hora del sistema (SY-UZEIT)
" ❌ Classic ABAPDATA(lv_time) = sy-uzeit.ls_log-logged_at_time = sy-uzeit.
" ✅ ABAP CloudDATA(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 ABAPDATA: lv_timestamp TYPE timestamp.GET TIME STAMP FIELD lv_timestamp.
" ✅ ABAP Cloud - más legible y type-safeDATA(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 ABAPSELECT SINGLE text FROM ztab_texts WHERE langu = @sy-langu INTO @DATA(lv_text).
" ✅ ABAP CloudSELECT 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 ABAPDATA(lv_timezone) = sy-zonlo.CONVERT TIME STAMP lv_utc TIME ZONE sy-zonlo INTO DATE lv_local_date TIME lv_local_time.
" ✅ ABAP CloudDATA(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 ABAPDATA(lv_client) = sy-mandt.lv_table_key = |{ sy-mandt }{ lv_id }|.
" ✅ ABAP CloudDATA(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 ABAPIF sy-sysid = 'PRD'. " Sistema de producciónENDIF.
" ✅ ABAP CloudIF cl_abap_context_info=>get_system_id( ) = 'PRD'. " Sistema de producciónENDIF.9. Audit-Logging con usuario y timestamp
" ❌ Classic ABAPls_audit_log = VALUE #( user_name = sy-uname log_date = sy-datum log_time = sy-uzeit client = sy-mandt).
" ✅ ABAP Cloud - completamente migradols_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 ABAPSELECT text FROM ztab_translations WHERE text_id = @lv_text_id AND langu = @sy-langu INTO TABLE @DATA(lt_texts).
" ✅ ABAP CloudDATA(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 manualmenteDATA(lv_url) = |https://mycompany.sap.com/sap/bc/ui5_ui5/...|.
" ✅ ABAP Cloud - URL del sistema dinámicaDATA(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 ABAPDATA(lv_deadline) = sy-datum + 30.DATA(lv_last_month) = sy-datum - 30.
" ✅ ABAP CloudDATA(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 RAPMETHOD 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 CloudMETHOD 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 ABAPIF ls_license-valid_to < sy-datum. RAISE EXCEPTION TYPE zcx_license_expired.ENDIF.
" ✅ ABAP CloudIF 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_INFODATA(lo_ctx) = zcl_context_helper=>get_instance( ).
" Valores individualesDATA(lv_user) = lo_ctx->get_user( ).DATA(lv_timestamp) = lo_ctx->get_timestamp( ).
" Contexto completoDATA(ls_context) = lo_ctx->get_full_context( ).Parte 3: Errores comunes y soluciones
Error 1: Múltiples llamadas a API
" ❌ Ineficiente: API se llama 3xINSERT 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 vezDATA(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 localDATA(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ícitamenteDATA: 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 usuarioError 3: SY-SUBRC después de llamada a API
" ❌ Suposición incorrecta: SY-SUBRC se establece por APIDATA(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 valorDATA(lv_user) = cl_abap_context_info=>get_user_technical_name( )." No se necesita verificaciónError 4: Modo Batch (SY-BATCH)
" ❌ Classic ABAP: Verificar modo batchIF sy-batch = abap_true. " Procesamiento en segundo planoENDIF.
" ✅ 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 propiosCLASS 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 hardcodeadosIF cl_abap_context_info=>get_system_id( ) = 'PRD'. " Código específico de producciónENDIF.
" ✅ Mejor: Basado en configuración vía Customizing" En ABAP Cloud: Usar Communication Scenario o tablaSELECT 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 manualesConclusión
La migración de campos SY sigue patrones claros:
- Reemplazo directo para la mayoría de casos con
CL_ABAP_CONTEXT_INFO - Clase Utility para gestión centralizada y testabilidad
- Cachear valores en uso múltiple
- 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.