En Classic ABAP, les reports sont le fondement du developpement d’applications. REPORT, PARAMETERS, SELECT-OPTIONS et WRITE - c’est ainsi qu’on a developpe pendant des decennies. En ABAP Cloud, le monde est different : pas de reports classiques, pas de Dynpros, pas de Selection Screens. A la place : Executable Classes, Application Jobs et Apps Fiori.
Le probleme : Des reports en ABAP Cloud ?
Celui qui veut creer un report classique en ABAP Cloud sera decu :
" ❌ Pas possible en ABAP Cloud:REPORT zflight_report.PARAMETERS: p_carr TYPE s_carr_id.START-OF-SELECTION. SELECT * FROM sflight INTO TABLE @DATA(lt_flights) WHERE carrid = @p_carr. LOOP AT lt_flights INTO DATA(ls_flight). WRITE: / ls_flight-carrid, ls_flight-connid. ENDLOOP.Ce code sera rejete par le compilateur - REPORT, PARAMETERS et WRITE ne sont pas des instructions ABAP Cloud valides.
Les alternatives en apercu
ABAP Cloud offre trois approches principales pour l’execution de programmes :
┌─────────────────────────────────────────────────────────────────┐│ Programmes en ABAP Cloud │├─────────────────────────────────────────────────────────────────┤│ ││ 1. Executable Class (if_oo_adt_classrun) ││ → Tests rapides, developpement, debug ││ → Execution directement dans ADT ││ ││ 2. Application Job ││ → Traitement planifie en arriere-plan ││ → Parametres via catalogue de jobs ││ → Monitoring dans Fiori ││ ││ 3. App Fiori (basee sur RAP) ││ → Interface utilisateur interactive ││ → Filtres et actions ││ → UX complete ││ │└─────────────────────────────────────────────────────────────────┘Executable Classes avec IF_OO_ADT_CLASSRUN
L’interface IF_OO_ADT_CLASSRUN permet l’execution directe d’une classe dans les ABAP Development Tools (ADT). C’est le moyen le plus rapide d’executer et de tester du code.
Structure de base
CLASS zcl_flight_report DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_flight_report IMPLEMENTATION. METHOD if_oo_adt_classrun~main. " La logique du programme vient ici out->write( 'Report de reservations de vol' ). out->write( '===================' ).
" Charger les donnees SELECT FROM zflight_book FIELDS booking_id, flight_id, customer_id, booking_date, price INTO TABLE @DATA(lt_bookings).
" Sortie out->write( lt_bookings ). ENDMETHOD.ENDCLASS.Execution dans ADT
Clic droit sur la classe → Run As → ABAP Application (Console)→ La sortie apparait dans la vue ConsoleReport de reservations de vol : Exemple complet
CLASS zcl_booking_analysis DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
PRIVATE SECTION. TYPES: BEGIN OF ty_summary, carrier_id TYPE s_carr_id, total_bookings TYPE i, total_revenue TYPE p LENGTH 15 DECIMALS 2, avg_price TYPE p LENGTH 10 DECIMALS 2, END OF ty_summary, tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY.
METHODS analyze_bookings RETURNING VALUE(rt_summary) TYPE tt_summary.
METHODS get_top_routes RETURNING VALUE(rt_routes) TYPE string_table.
ENDCLASS.
CLASS zcl_booking_analysis IMPLEMENTATION. METHOD if_oo_adt_classrun~main. out->write( |Analyse des reservations de vol - { cl_abap_context_info=>get_system_date( ) }| ). out->write( |Cree par: { cl_abap_context_info=>get_user_technical_name( ) }| ). out->write( '=' && repeat( val = '=' occ = 50 ) ). out->write( `` ).
" Resume des reservations par transporteur out->write( '1. Reservations par compagnie aerienne:' ). out->write( '-' && repeat( val = '-' occ = 40 ) ).
DATA(lt_summary) = analyze_bookings( ). LOOP AT lt_summary INTO DATA(ls_summary). out->write( | { ls_summary-carrier_id }: { ls_summary-total_bookings } reservations, | && |CA: { ls_summary-total_revenue } EUR, | && |Moyenne: { ls_summary-avg_price } EUR| ). ENDLOOP. out->write( `` ).
" Top routes out->write( '2. Routes les plus populaires:' ). out->write( '-' && repeat( val = '-' occ = 40 ) ). DATA(lt_routes) = get_top_routes( ). LOOP AT lt_routes INTO DATA(lv_route). out->write( | { sy-tabix }. { lv_route }| ). ENDLOOP.
out->write( `` ). out->write( 'Analyse terminee.' ). ENDMETHOD.
METHOD analyze_bookings. SELECT FROM zflight_book AS b INNER JOIN zflight AS f ON b~flight_id = f~flight_id FIELDS f~carrier_id, COUNT( * ) AS total_bookings, SUM( b~price ) AS total_revenue, AVG( b~price ) AS avg_price GROUP BY f~carrier_id ORDER BY total_bookings DESCENDING INTO TABLE @rt_summary. ENDMETHOD.
METHOD get_top_routes. SELECT FROM zflight_book AS b INNER JOIN zflight AS f ON b~flight_id = f~flight_id FIELDS f~carrier_id, f~connection_id, f~departure_city, f~arrival_city, COUNT( * ) AS booking_count GROUP BY f~carrier_id, f~connection_id, f~departure_city, f~arrival_city ORDER BY booking_count DESCENDING UP TO 5 ROWS INTO TABLE @DATA(lt_routes).
LOOP AT lt_routes INTO DATA(ls_route). APPEND |{ ls_route-departure_city } → { ls_route-arrival_city } | && |({ ls_route-carrier_id }/{ ls_route-connection_id }): | && |{ ls_route-booking_count } reservations| TO rt_routes. ENDLOOP. ENDMETHOD.ENDCLASS.Comparaison : REPORT vs. Executable Class
| Aspect | REPORT classique | Executable Class |
|---|---|---|
| Creation | REPORT zreport. | Classe avec interface |
| Parametres | PARAMETERS, SELECT-OPTIONS | Aucun (code en dur ou table) |
| Sortie | WRITE, ALV, Dynpro | out->write( ) |
| Execution | SE38, SA38, Transaction | Console ADT |
| Planification | SM36, Background Job | Application Job |
| Tests | Manuel | Tests unitaires possibles |
Application Jobs : Traitement en arriere-plan
Pour les executions planifiees et le traitement en arriere-plan, ABAP Cloud offre les Application Jobs. Ceux-ci remplacent les Background Jobs classiques de SM36/SM37.
Composants d’un Application Job
┌─────────────────────────────────────────────────────────────────┐│ Application Job │├─────────────────────────────────────────────────────────────────┤│ ││ 1. Job Catalog Entry ││ → Enregistre le job dans le systeme ││ → Definit l'interface des parametres ││ ││ 2. Job Template (optionnel) ││ → Valeurs de parametres predefinies ││ → Reutilisable ││ ││ 3. Job Class ││ → Implemente IF_APJ_DT_EXEC_OBJECT ││ → Contient la logique metier ││ ││ 4. Planification ││ → App Fiori "Schedule Application Jobs" ││ → Unique, recurrent, base sur evenement ││ │└─────────────────────────────────────────────────────────────────┘Implementer une classe de job
CLASS zcl_booking_cleanup_job DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_apj_dt_exec_object.
PRIVATE SECTION. DATA mv_days_to_keep TYPE i. DATA mv_dry_run TYPE abap_bool.
METHODS delete_old_bookings RETURNING VALUE(rv_deleted) TYPE i.
METHODS log_result IMPORTING iv_deleted TYPE i.
ENDCLASS.
CLASS zcl_booking_cleanup_job IMPLEMENTATION.
METHOD if_apj_dt_exec_object~get_parameters. " Definir les parametres pour le catalogue de jobs et_parameter_def = VALUE #( ( selname = 'P_DAYS" kind = if_apj_dt_exec_object=>parameter datatype = 'INT4" length = 10 param_text = 'Duree de conservation (jours)" changeable_ind = abap_true mandatory_ind = abap_true )
( selname = 'P_DRYRUN" kind = if_apj_dt_exec_object=>parameter datatype = 'CHAR" length = 1 param_text = 'Simulation (X = oui)" changeable_ind = abap_true mandatory_ind = abap_false ) ).
" Valeurs par defaut et_parameter_val = VALUE #( ( selname = 'P_DAYS' low = '90' ) ( selname = 'P_DRYRUN' low = 'X' ) ). ENDMETHOD.
METHOD if_apj_dt_exec_object~execute. " Lire les parametres mv_days_to_keep = VALUE #( it_parameters[ selname = 'P_DAYS' ]-low DEFAULT 90 ). mv_dry_run = xsdbool( VALUE #( it_parameters[ selname = 'P_DRYRUN' ]-low DEFAULT '' ) = 'X' ).
" Execution DATA(lv_deleted) = delete_old_bookings( ).
" Logging log_result( lv_deleted ). ENDMETHOD.
METHOD delete_old_bookings. DATA(lv_cutoff_date) = cl_abap_context_info=>get_system_date( ) - mv_days_to_keep.
IF mv_dry_run = abap_true. " Compter seulement SELECT COUNT( * ) FROM zflight_book WHERE booking_status = 'C' " Annule AND booking_date < @lv_cutoff_date INTO @rv_deleted. ELSE. " Supprimer reellement DELETE FROM zflight_book WHERE booking_status = 'C" AND booking_date < @lv_cutoff_date. rv_deleted = sy-dbcnt. ENDIF. ENDMETHOD.
METHOD log_result. " Utiliser Application Log DATA(lo_log) = cl_bali_log=>create_with_header( header = cl_bali_header_setter=>create( object = 'ZFB_LOG" subobject = 'CLEANUP" ) ).
DATA(lv_msg) = COND #( WHEN mv_dry_run = abap_true THEN |Simulation: { iv_deleted } reservations seraient supprimees.| ELSE |{ iv_deleted } anciennes reservations ont ete supprimees.| ).
lo_log->add_item( cl_bali_free_text_setter=>create( text = lv_msg ) ). cl_bali_log_db=>get_instance( )->save_log( log = lo_log ). ENDMETHOD.
ENDCLASS.Creer une entree de catalogue de jobs
Le job doit etre enregistre dans le Job Catalog. Dans ADT :
Clic droit sur package → New → Other ABAP Repository Object→ Application Jobs → Application Job Catalog EntryNom: ZFB_BOOKING_CLEANUPDescription: Supprimer les anciennes reservations annuleesClasse de job: ZCL_BOOKING_CLEANUP_JOBCreer un template de job (optionnel)
Nom: ZFB_CLEANUP_WEEKLYEntree catalogue: ZFB_BOOKING_CLEANUPParametres: P_DAYS = 30 P_DRYRUN = (vide)Planifier et surveiller les jobs
La planification s’effectue via l’app Fiori Schedule Application Jobs :
| Fonction | App-ID | Description |
|---|---|---|
| Planifier | F2640 | Planifier des jobs |
| Surveiller | F2079 | Jobs en cours/termines |
| Logs | F0399 | Application Log |
Quand App Fiori vs. Application Job ?
Le choix entre App Fiori et Application Job depend du cas d’utilisation :
┌─────────────────────────────────────────────────────────────────┐│ Matrice de decision │├─────────────────────────────────────────────────────────────────┤│ ││ Interaction utilisateur necessaire ? ││ ├── Oui → App Fiori (basee sur RAP) ││ │ • Definir des filtres ││ │ • Modifier des donnees ││ │ • Feedback immediat ││ │ ││ └── Non → Application Job ││ • Execution planifiee ││ • Longue duree ││ • Traitement par lots ││ ││ Duree d'execution > 60 secondes ? ││ ├── Oui → Application Job ou bgPF ││ └── Non → Synchrone possible ││ │└─────────────────────────────────────────────────────────────────┘Exemples pour la bonne approche
| Exigence | Approche recommandee |
|---|---|
| Afficher l’apercu des reservations | Fiori Elements List Report |
| Saisir une nouvelle reservation | Fiori Object Page |
| Creer des statistiques quotidiennes | Application Job |
| Nettoyage mensuel | Application Job |
| Generer un rapport PDF | bgPF (action RAP asynchrone) |
| Analyse de donnees ad-hoc | Executable Class |
Exemple de reservation de vol : Programme complet
Voici un exemple complet d’un “programme” ABAP Cloud qui combine differentes approches :
1. Executable Class pour developpement/test
CLASS zcl_flight_util DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
" Methodes reutilisables METHODS get_bookings_by_carrier IMPORTING iv_carrier TYPE s_carr_id RETURNING VALUE(rt_bookings) TYPE ztt_flight_booking.
METHODS calculate_revenue IMPORTING it_bookings TYPE ztt_flight_booking RETURNING VALUE(rv_revenue) TYPE p LENGTH 15 DECIMALS 2.
ENDCLASS.
CLASS zcl_flight_util IMPLEMENTATION. METHOD if_oo_adt_classrun~main. " Execution de test out->write( 'Utilitaires de reservation de vol - Test' ).
DATA(lt_lh_bookings) = get_bookings_by_carrier( 'LH' ). out->write( |Lufthansa: { lines( lt_lh_bookings ) } reservations| ).
DATA(lv_revenue) = calculate_revenue( lt_lh_bookings ). out->write( |Chiffre d'affaires: { lv_revenue } EUR| ). ENDMETHOD.
METHOD get_bookings_by_carrier. SELECT FROM zflight_book AS b INNER JOIN zflight AS f ON b~flight_id = f~flight_id FIELDS b~booking_id, b~flight_id, b~customer_id, b~booking_date, b~price, b~currency_code WHERE f~carrier_id = @iv_carrier INTO TABLE @rt_bookings. ENDMETHOD.
METHOD calculate_revenue. rv_revenue = REDUCE #( INIT sum = CONV p( 0 ) FOR booking IN it_bookings NEXT sum = sum + booking-price ). ENDMETHOD.ENDCLASS.2. Application Job pour traitement par lots
CLASS zcl_flight_stats_job DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_apj_dt_exec_object.
ENDCLASS.
CLASS zcl_flight_stats_job IMPLEMENTATION. METHOD if_apj_dt_exec_object~get_parameters. et_parameter_def = VALUE #( ( selname = 'P_PERIOD" kind = if_apj_dt_exec_object=>parameter datatype = 'CHAR" length = 7 param_text = 'Periode (YYYYMM)" mandatory_ind = abap_true ) ).
" Default: Mois precedent DATA(lv_date) = cl_abap_context_info=>get_system_date( ) - 30. et_parameter_val = VALUE #( ( selname = 'P_PERIOD' low = |{ lv_date(6) }| ) ). ENDMETHOD.
METHOD if_apj_dt_exec_object~execute. DATA(lv_period) = VALUE #( it_parameters[ selname = 'P_PERIOD' ]-low DEFAULT '' ).
" Calculer et sauvegarder les statistiques DATA(lo_util) = NEW zcl_flight_util( ).
" ... Logique de traitement ... ENDMETHOD.ENDCLASS.3. Service RAP pour App Fiori
L’interface interactive est realisee via RAP :
" CDS View pour l'affichage des reservations@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Reservations de vol"define root view entity ZC_FlightBooking provider contract transactional_query as projection on ZI_FlightBooking{ @UI.facet: [{ type: #IDENTIFICATION_REFERENCE }]
@UI.lineItem: [{ position: 10 }] @UI.identification: [{ position: 10 }] key BookingId,
@UI.lineItem: [{ position: 20 }] FlightId,
@UI.lineItem: [{ position: 30 }] @UI.selectionField: [{ position: 10 }] CustomerId,
@UI.lineItem: [{ position: 40 }] @UI.selectionField: [{ position: 20 }] BookingDate,
@UI.lineItem: [{ position: 50 }] Price,
CurrencyCode, BookingStatus}Bonnes pratiques
1. Structurez votre code
" ✅ Bien: Logique dans des methodes separeesCLASS zcl_booking_report DEFINITION. PUBLIC SECTION. INTERFACES if_oo_adt_classrun. PRIVATE SECTION. METHODS fetch_data RETURNING VALUE(rt_data) TYPE tt_data. METHODS process_data IMPORTING it_data TYPE tt_data. METHODS output_results IMPORTING io_out TYPE REF TO if_oo_adt_classrun_out.ENDCLASS.
" ❌ Mauvais: Tout dans mainCLASS zcl_bad_example IMPLEMENTATION. METHOD if_oo_adt_classrun~main. " 500 lignes de code ici... ENDMETHOD.ENDCLASS.2. Utilisez les informations de contexte
" Au lieu de SY-UNAME, SY-DATUM etc.DATA(lv_user) = cl_abap_context_info=>get_user_technical_name( ).DATA(lv_date) = cl_abap_context_info=>get_system_date( ).DATA(lv_time) = cl_abap_context_info=>get_system_time( ).Details dans l’article Champs systeme en ABAP Cloud.
3. Utilisez Application Logging
" Au lieu de WRITE pour la sortie des jobsDATA(lo_log) = cl_bali_log=>create_with_header( header = cl_bali_header_setter=>create( object = 'ZFB_LOG" subobject = 'REPORTS" )).lo_log->add_item( cl_bali_free_text_setter=>create( text = 'Traitement demarre' ) ).cl_bali_log_db=>get_instance( )->save_log( log = lo_log ).4. Considerez la testabilite unitaire
" La classe avec if_oo_adt_classrun est aussi testable unitairement!CLASS ltc_flight_util DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. PRIVATE SECTION. METHODS test_calculate_revenue FOR TESTING.ENDCLASS.
CLASS ltc_flight_util IMPLEMENTATION. METHOD test_calculate_revenue. DATA(lo_cut) = NEW zcl_flight_util( ). DATA(lt_bookings) = VALUE ztt_flight_booking( ( price = 100 ) ( price = 200 ) ). cl_abap_unit_assert=>assert_equals( act = lo_cut->calculate_revenue( lt_bookings ) exp = 300 ). ENDMETHOD.ENDCLASS.Conclusion
Les programmes en ABAP Cloud necessitent un changement de mentalite :
| Classic ABAP | ABAP Cloud |
|---|---|
REPORT | Executable Class |
PARAMETERS | Parametres de job / Filtres Fiori |
WRITE | out->write( ) / Application Log |
| SM36/SM37 | Application Jobs (Fiori) |
| Transaction | App Fiori |
Le passage des reports aux classes favorise une meilleure conception : methodes reutilisables, testabilite unitaire et separation claire de la logique et de la sortie.
Pour le traitement asynchrone, voir RAP avec bgPF, pour le choix Fiori Elements vs. Freestyle voir Fiori Elements vs. Freestyle. Les bases de la migration se trouvent dans le Guide de migration ABAP Cloud.