Migration des champs système : Exemples pratiques avant/après pour ABAP Cloud

Catégorie
ABAP Cloud
Publié
Auteur
Johannes

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 Cloud
DATA(lv_user) = sy-uname.
ls_document-created_by = sy-uname.
WHERE created_by = @sy-uname
" ABAP Cloud - migration directe
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. Date système (SY-DATUM)

" ABAP classique
DATA(lv_today) = sy-datum.
IF ls_booking-flight_date < sy-datum.
" Expiré
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( ).
" Expiré
ENDIF.

3. Heure système (SY-UZEIT)

" ABAP classique
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. Horodatage UTC (GET TIME STAMP)

" ABAP classique
DATA: 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 classique
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. Fuseau horaire (SY-ZONLO)

" ABAP classique
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. Mandant (SY-MANDT)

" ABAP classique
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 système (SY-SYSID)

" ABAP classique
IF sy-sysid = 'PRD'.
" Système de production
ENDIF.
" ABAP Cloud
IF cl_abap_context_info=>get_system_id( ) = 'PRD'.
" Système de production
ENDIF.

9. Journalisation d’audit avec utilisateur et horodatage

" ABAP classique
ls_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 classique
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. Lien email avec URL système

" ABAP classique - URL système configurée manuellement
DATA(lv_url) = |https://mycompany.sap.com/sap/bc/ui5_ui5/...|.
" ABAP Cloud - URL système dynamique
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. Calcul de date avec la date actuelle

" ABAP classique
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. Horodatage pour une détermination RAP

" ABAP classique dans un contexte 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. Vérification de date d’expiration

" ABAP classique
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.

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_INFO
DATA(lo_ctx) = zcl_context_helper=>get_instance( ).
" Valeurs individuelles
DATA(lv_user) = lo_ctx->get_user( ).
DATA(lv_timestamp) = lo_ctx->get_timestamp( ).
" Contexte complet
DATA(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-uname
Remplacer: cl_abap_context_info=>get_user_technical_name( )
Rechercher: sy-datum
Remplacer: cl_abap_context_info=>get_system_date( )
Rechercher: sy-uzeit
Remplacer: 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és
ls_record-created_at = lv_timestamp.
ls_record-changed_at = lv_timestamp.

Pattern : Moderniser une classe de rapport

" Rapport classique avec champs SY
CLASS 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 Cloud
CLASS 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 fois
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( )
) ).
" Efficace : obtenir les valeurs une fois
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
) ).

Piège 2 : Fuseau horaire lors des comparaisons de dates

" Problème : Heure système vs heure locale
DATA(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 horaire
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.
" Maintenant lv_local_date est la date du point de vue de l'utilisateur

Piège 3 : SY-SUBRC après un appel API

" Fausse hypothèse : SY-SUBRC est défini par l'API
DATA(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 valeur
DATA(lv_user) = cl_abap_context_info=>get_user_technical_name( ).
" Pas de vérification nécessaire

Piège 4 : Mode batch (SY-BATCH)

" ABAP classique : Vérifier le mode batch
IF sy-batch = abap_true.
" Traitement en arrière-plan
ENDIF.
" 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ètres
CLASS 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 dur
IF cl_abap_context_info=>get_system_id( ) = 'PRD'.
" Code spécifique à la production
ENDIF.
" Mieux : Basé sur la configuration via customizing
" Dans ABAP Cloud : Utiliser Communication Scenario ou une table
SELECT 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 manuels

Conclusion

La migration des champs SY suit des patterns clairs :

  1. Remplacement direct pour la plupart des cas avec CL_ABAP_CONTEXT_INFO
  2. Classe utilitaire pour une gestion centralisée et la testabilité
  3. Mettre en cache les valeurs en cas d’utilisation multiple
  4. 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.