La migration des champs SY vers les APIs ABAP Cloud est l’une des étapes les plus fréquentes lors de la modernisation du code ABAP. Cet article présente des exemples concrets avant/après pour 15 cas d’utilisation courants, une classe utilitaire réutilisable et les pièges typiques à éviter.
Les fondamentaux sur les champs SY et leurs alternatives se trouvent dans l’article Champs système dans ABAP Cloud : Alternatives à SY-*.
Aperçu : La migration en un coup d’oeil
┌─────────────────────────────────────────────────────────────┐│ Migration des champs SY ││ ─────────────────────────────────────────────────────────── ││ ││ 1. Exécuter ATC → trouve tous les problèmes SY-* ││ 2. Créer une classe utilitaire → remplacement centralisé ││ 3. Rechercher et remplacer → migrer systématiquement ││ 4. Tests unitaires → valider le comportement ││ ││ SY-SUBRC, SY-TABIX, SY-DBCNT restent inchangés ! │└─────────────────────────────────────────────────────────────┘Partie 1 : 15 exemples avant/après
1. Nom d’utilisateur (SY-UNAME)
" ABAP classique - non compatible CloudDATA(lv_user) = sy-uname.ls_document-created_by = sy-uname.WHERE created_by = @sy-uname
" ABAP Cloud - migration directeDATA(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. Date système (SY-DATUM)
" ABAP classiqueDATA(lv_today) = sy-datum.IF ls_booking-flight_date < sy-datum. " ExpiréENDIF.
" ABAP CloudDATA(lv_today) = cl_abap_context_info=>get_system_date( ).IF ls_booking-flight_date < cl_abap_context_info=>get_system_date( ). " ExpiréENDIF.3. Heure système (SY-UZEIT)
" ABAP classiqueDATA(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. Horodatage UTC (GET TIME STAMP)
" ABAP classiqueDATA: lv_timestamp TYPE timestamp.GET TIME STAMP FIELD lv_timestamp.
" ABAP Cloud - plus lisible et typéDATA(lv_timestamp) = cl_abap_context_info=>get_system_timestamp( ).
" Note : Le type de retour est UTCLONG (recommandé pour la base de données)5. Langue de connexion (SY-LANGU)
" ABAP classiqueSELECT 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. Fuseau horaire (SY-ZONLO)
" ABAP classiqueDATA(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. Mandant (SY-MANDT)
" ABAP classiqueDATA(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 système (SY-SYSID)
" ABAP classiqueIF sy-sysid = 'PRD'. " Système de productionENDIF.
" ABAP CloudIF cl_abap_context_info=>get_system_id( ) = 'PRD'. " Système de productionENDIF.9. Journalisation d’audit avec utilisateur et horodatage
" ABAP classiquels_audit_log = VALUE #( user_name = sy-uname log_date = sy-datum log_time = sy-uzeit client = sy-mandt).
" ABAP Cloud - entièrement migré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. Nom d’utilisateur formaté pour l’interface
" ABAP classique - pas d'équivalent direct" SY-UNAME était souvent affiché directement : "JSMITH"
" ABAP Cloud - meilleure présentation possible" Nom formaté : "John Smith"DATA(lv_display_name) = cl_abap_context_info=>get_user_formatted_name( ).
" Ou alias utilisateur (souvent l'email)DATA(lv_alias) = cl_abap_context_info=>get_user_alias( ).11. Charger des textes multilingues
" ABAP classiqueSELECT 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. Lien email avec URL système
" ABAP classique - URL système configurée manuellementDATA(lv_url) = |https://mycompany.sap.com/sap/bc/ui5_ui5/...|.
" ABAP Cloud - URL système dynamiqueDATA(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. Calcul de date avec la date actuelle
" ABAP classiqueDATA(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. Horodatage pour une détermination RAP
" ABAP classique dans un contexte 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. Vérification de date d’expiration
" ABAP classiqueIF 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.Partie 2 : Classe utilitaire réutilisable
Une classe utilitaire centrale simplifie la migration et améliore la testabilité grâce à l’injection de dépendances.
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.
" Pattern singleton pour un accès simple CLASS-METHODS get_instance RETURNING VALUE(ro_instance) TYPE REF TO zcl_context_helper.
" Toutes les informations de contexte en une fois METHODS get_full_context RETURNING VALUE(rs_context) TYPE ty_context.
" Méthodes individuelles pour les accès fréquents 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.
" Pour les tests unitaires : surcharger les valeurs 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.
" Valeurs mock pour les tests unitaires 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. " Sortie de démonstration DATA(ls_context) = get_instance( )->get_full_context( ). out->write( |Utilisateur: { ls_context-user_name }| ). out->write( |Date: { ls_context-system_date }| ). out->write( |Heure: { ls_context-system_time }| ). out->write( |Horodatage: { ls_context-timestamp }| ). out->write( |Langue: { ls_context-language }| ). out->write( |ID système: { ls_context-system_id }| ). ENDMETHOD.
ENDCLASS.Utilisation de la classe utilitaire
" Au lieu d'appeler directement CL_ABAP_CONTEXT_INFODATA(lo_ctx) = zcl_context_helper=>get_instance( ).
" Valeurs individuellesDATA(lv_user) = lo_ctx->get_user( ).DATA(lv_timestamp) = lo_ctx->get_timestamp( ).
" Contexte completDATA(ls_context) = lo_ctx->get_full_context( ).Partie 3 : Migration de rapports existants
Migration pas à pas
Étape 1 : Exécuter la vérification ATC
Clic droit sur le paquet → Run As → ABAP Test Cockpit (ATC)→ Check Variant: ABAP_CLOUD_READINESS→ Tous les problèmes de champs SY sont listésÉtape 2 : Rechercher et remplacer dans ADT
Ctrl+H → Activer les expressions régulières
Rechercher: sy-unameRemplacer: cl_abap_context_info=>get_user_technical_name( )
Rechercher: sy-datumRemplacer: cl_abap_context_info=>get_system_date( )
Rechercher: sy-uzeitRemplacer: cl_abap_context_info=>get_system_time( )Étape 3 : Vérifier manuellement les cas complexes
" Stocker l'horodatage dans une variable (obtenir une fois)DATA(lv_timestamp) = cl_abap_context_info=>get_system_timestamp( ).
" Utiliser plusieurs fois sans appels API répétésls_record-created_at = lv_timestamp.ls_record-changed_at = lv_timestamp.Pattern : Moderniser une classe de rapport
" Rapport classique avec champs SYCLASS lcl_old_report DEFINITION. PUBLIC SECTION. METHODS execute. PRIVATE SECTION. DATA: mv_run_date TYPE d, mv_run_user TYPE syuname.ENDCLASS.
CLASS lcl_old_report IMPLEMENTATION. METHOD execute. mv_run_date = sy-datum. mv_run_user = sy-uname. " ... Logique ... ENDMETHOD.ENDCLASS.
" Rapport ABAP CloudCLASS zcl_new_report DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun. METHODS execute.
PRIVATE SECTION. DATA mo_context TYPE REF TO zcl_context_helper.
METHODS initialize.ENDCLASS.
CLASS zcl_new_report IMPLEMENTATION. METHOD if_oo_adt_classrun~main. initialize( ). execute( ). ENDMETHOD.
METHOD initialize. mo_context = zcl_context_helper=>get_instance( ). ENDMETHOD.
METHOD execute. DATA(lv_run_date) = mo_context->get_date( ). DATA(lv_run_user) = mo_context->get_user( ). " ... Logique ... ENDMETHOD.ENDCLASS.Partie 4 : Pièges courants et solutions
Piège 1 : Appels API multiples
" Inefficace : l'API est appelée 3 foisINSERT 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( )) ).
" Efficace : obtenir les valeurs une foisDATA(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) ).Piège 2 : Fuseau horaire lors des comparaisons de dates
" Problème : Heure système vs heure localeDATA(lv_system_date) = cl_abap_context_info=>get_system_date( )." Dans différents fuseaux horaires, cela peut être la mauvaise date !
" Solution : Prendre explicitement en compte le fuseau horaireDATA: 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.
" Maintenant lv_local_date est la date du point de vue de l'utilisateurPiège 3 : SY-SUBRC après un appel API
" Fausse hypothèse : SY-SUBRC est défini par l'APIDATA(lv_user) = cl_abap_context_info=>get_user_technical_name( ).IF sy-subrc <> 0. " Ne sera jamais exécuté - l'API ne définit pas SY-SUBRC !ENDIF.
" Correct : L'API n'a pas de cas d'erreur" CL_ABAP_CONTEXT_INFO retourne toujours une valeurDATA(lv_user) = cl_abap_context_info=>get_user_technical_name( )." Pas de vérification nécessairePiège 4 : Mode batch (SY-BATCH)
" ABAP classique : Vérifier le mode batchIF sy-batch = abap_true. " Traitement en arrière-planENDIF.
" ABAP Cloud : Pas de SY-BATCH" Dans ABAP Cloud, il n'y a pas de rapports classiques" Les Application Jobs ont leurs propres mécanismes
" Si nécessaire : Contrôler via vos propres paramètresCLASS zcl_my_job DEFINITION. PUBLIC SECTION. METHODS execute IMPORTING iv_is_background TYPE abap_bool DEFAULT abap_false.ENDCLASS.Piège 5 : Vérification système codée en dur
" Problématique : IDs système codés en durIF cl_abap_context_info=>get_system_id( ) = 'PRD'. " Code spécifique à la productionENDIF.
" Mieux : Basé sur la configuration via customizing" Dans ABAP Cloud : Utiliser Communication Scenario ou une tableSELECT SINGLE is_production FROM ztab_config WHERE system_id = @( cl_abap_context_info=>get_system_id( ) ) INTO @DATA(lv_is_production).Tests unitaires avec valeurs mock
La classe utilitaire permet des tests simples :
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 #( ).
" Définir les valeurs 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: Réservation pour demain DATA(ls_booking) = VALUE zif_booking=>ty_booking( flight_date = '20260302" ).
" When: Exécuter la validation DATA(lv_is_valid) = mo_cut->validate_booking( ls_booking ).
" Then: La réservation est valide (la date est dans le futur) cl_abap_unit_assert=>assert_true( lv_is_valid ). ENDMETHOD.ENDCLASS.Checklist pour la migration
## Checklist de migration des champs SY
### Préparation- [ ] Exécuter ATC avec ABAP_CLOUD_READINESS- [ ] Créer la classe utilitaire ZCL_CONTEXT_HELPER- [ ] Identifier tous les objets concernés
### Migration par objet- [ ] 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( )
### Assurance qualité- [ ] Tests unitaires adaptés (mocking)- [ ] Exécuter ATC à nouveau- [ ] Effectuer des tests manuelsConclusion
La migration des champs SY suit des patterns clairs :
- Remplacement direct pour la plupart des cas avec
CL_ABAP_CONTEXT_INFO - Classe utilitaire pour une gestion centralisée et la testabilité
- Mettre en cache les valeurs en cas d’utilisation multiple
- Tenir compte des fuseaux horaires lors des comparaisons de dates
La conversion est techniquement simple, mais nécessite une approche systématique. L’ATC aide à trouver tous les emplacements.
Pour un aperçu complet de tous les champs SY, voir Champs système dans ABAP Cloud. Plus d’informations sur la stratégie Clean Core dans Concept de niveaux Clean Core et pour la mise en oeuvre pratique dans la Checklist d’implémentation.