Actions et Functions sont les moyens dans RAP pour implementer votre propre logique metier au-dela des operations CRUD standard. Cet article explique les differences et montre quand utiliser quel concept.
Action vs. Function : La difference
| Aspect | Action | Function |
|---|---|---|
| But | Modifie les donnees (effets de bord) | Lit/calcule les donnees (pas d’effets de bord) |
| Methode HTTP | POST | GET |
| Transactionnel | Oui, fait partie de la transaction | Non, lecture seule |
| Idempotent | Non | Oui |
| Exemples | Annuler, Approuver, Copier | Calculer le prix, Verifier la disponibilite |
Quand Action, quand Function ?
- Action : Quand l’operation modifie l’etat de l’entite ou du systeme
- Function : Quand on calcule seulement une valeur ou on interroge une information, sans modifier de donnees
Bound vs. Unbound (Static)
| Aspect | Bound (Instance) | Unbound (Static) |
|---|---|---|
| Contexte | Lie a une instance | Sans contexte d’instance |
| Appel | Sur la ligne selectionnee | Via un bouton dans la barre d’outils |
| Cles | Cles de l’instance disponibles | Pas de cles automatiques |
| Cas d’utilisation | ”Annuler ce voyage" | "Approuver tous les voyages ouverts” |
Instance-bound Action
Une Instance-bound Action est executee sur une instance concrete :
Behavior Definition
managed implementation in class zbp_i_travel unique;strict ( 2 );
define behavior for ZI_Travel alias Travelpersistent table ztravellock masterauthorization master ( instance ){ // Instance-bound Action sans parametre action cancel result [1] $self;
// Instance-bound Action avec resultat action accept result [1] $self;
// Instance-bound Action qui retourne plusieurs instances action copyTravel result [0..*] $self;}Implementation
CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS cancel FOR MODIFY IMPORTING keys FOR ACTION Travel~cancel RESULT result.
METHODS accept FOR MODIFY IMPORTING keys FOR ACTION Travel~accept RESULT result.ENDCLASS.
CLASS lhc_travel IMPLEMENTATION. METHOD cancel. " Lire les donnees actuelles READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel FIELDS ( TravelID Status ) WITH CORRESPONDING #( keys ) RESULT DATA(travels).
" Seuls les voyages ouverts peuvent etre annules LOOP AT travels ASSIGNING FIELD-SYMBOL(<travel>) WHERE Status = 'O'.
" Definir le statut sur Cancelled MODIFY ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel UPDATE FIELDS ( Status ) WITH VALUE #( ( %tky = <travel>-%tky Status = 'X' " Cancelled ) ). ENDLOOP.
" Retourner le resultat READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(travels_result).
result = VALUE #( FOR travel IN travels_result ( %tky = travel-%tky %param = travel ) ). ENDMETHOD.
METHOD accept. " Definir le statut sur Accepted MODIFY ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel UPDATE FIELDS ( Status ) WITH VALUE #( FOR key IN keys ( %tky = key-%tky Status = 'A' " Accepted ) ).
" Retourner les donnees mises a jour READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(travels).
result = VALUE #( FOR travel IN travels ( %tky = travel-%tky %param = travel ) ). ENDMETHOD.ENDCLASS.Cardinalite du resultat
| Cardinalite | Signification |
|---|---|
result [1] $self | Exactement une instance en retour (la meme entite) |
result [0..1] $self | Optionnellement une instance |
result [0..*] $self | Nombre quelconque d’instances |
result [1] EntityName | Une instance d’une autre entite |
| (pas de result) | Pas de retour |
Static (Unbound) Action
Les Static Actions ne sont pas liees a une instance et sont typiquement utilisees pour les operations de masse :
Behavior Definition
define behavior for ZI_Travel alias Travel{ // Static Action sans parametre static action releaseAllOpen;
// Static Action avec resultat static action createFromTemplate result [1] $self;
// Static Action avec Global Feature Control static action ( features: global ) massUpdate;
// Pour Global Feature Control static features;}Implementation
CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS releaseAllOpen FOR MODIFY IMPORTING keys FOR ACTION Travel~releaseAllOpen.
METHODS createFromTemplate FOR MODIFY IMPORTING keys FOR ACTION Travel~createFromTemplate RESULT result.
METHODS get_global_features FOR GLOBAL FEATURES IMPORTING REQUEST requested_features FOR Travel RESULT result.ENDCLASS.
CLASS lhc_travel IMPLEMENTATION. METHOD releaseAllOpen. " Trouver tous les voyages ouverts SELECT * FROM ztravel WHERE status = 'O" INTO TABLE @DATA(open_travels).
" Definir le statut sur Released MODIFY ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel UPDATE FIELDS ( Status ) WITH VALUE #( FOR travel IN open_travels ( TravelUUID = travel-travel_uuid Status = 'R' " Released ) ). ENDMETHOD.
METHOD createFromTemplate. " Creer un nouveau voyage a partir du template DATA(template_id) = 'DEFAULT'.
" Charger le template (exemple simplifie) SELECT SINGLE * FROM ztravel_template WHERE template_id = @template_id INTO @DATA(template).
" Creer le nouveau voyage MODIFY ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel CREATE FIELDS ( CustomerID BeginDate EndDate Description ) WITH VALUE #( ( %cid = 'NEW_TRAVEL" CustomerID = template-customer_id BeginDate = cl_abap_context_info=>get_system_date( ) EndDate = cl_abap_context_info=>get_system_date( ) + 7 Description = 'Cree a partir du template" ) ) MAPPED DATA(mapped).
" Retourner l'entite creee READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel ALL FIELDS WITH VALUE #( ( %tky = mapped-travel[ 1 ]-%tky ) ) RESULT DATA(created_travel).
result = VALUE #( ( %cid_ref = 'NEW_TRAVEL" %param = created_travel[ 1 ] ) ). ENDMETHOD.
METHOD get_global_features. " Verifier si l'utilisateur a l'autorisation pour la mise a jour de masse DATA(has_auth) = check_mass_update_auth( ).
result = VALUE #( %action-massUpdate = COND #( WHEN has_auth = abap_true THEN if_abap_behv=>fc-o-enabled ELSE if_abap_behv=>fc-o-disabled ) ). ENDMETHOD.ENDCLASS.Function : Calculs sans effets de bord
Les Functions sont ideales pour les calculs et les requetes qui ne modifient pas de donnees :
Behavior Definition
define behavior for ZI_Travel alias Travel{ // Instance-bound Function function calculatePrice result [1] ZA_Price;
// Function avec plusieurs valeurs de retour function getAvailability result [1] ZA_Availability;
// Static Function static function getExchangeRates result [0..*] ZA_ExchangeRate;
// Function avec parametres function simulateDiscount parameter ZA_DiscountInput result [1] ZA_DiscountResult;}Abstract Entity pour les valeurs de retour
Les Functions necessitent souvent des entites abstraites pour les valeurs de retour structurees :
define abstract entity ZA_Price{ TravelUUID : sysuuid_x16; NetPrice : abap.dec( 15, 2 ); TaxAmount : abap.dec( 15, 2 ); TotalPrice : abap.dec( 15, 2 ); Currency : abap.cuky;}define abstract entity ZA_Availability{ TravelUUID : sysuuid_x16; IsAvailable : abap_boolean; SeatsLeft : abap.int4; NextAvailable: abap.dats;}Implementation
CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS calculatePrice FOR READ IMPORTING keys FOR FUNCTION Travel~calculatePrice RESULT result.
METHODS getAvailability FOR READ IMPORTING keys FOR FUNCTION Travel~getAvailability RESULT result.
METHODS getExchangeRates FOR READ IMPORTING keys FOR FUNCTION Travel~getExchangeRates RESULT result.ENDCLASS.
CLASS lhc_travel IMPLEMENTATION. METHOD calculatePrice. " Lire les donnees du voyage READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel FIELDS ( TotalPrice Currency ) WITH CORRESPONDING #( keys ) RESULT DATA(travels).
LOOP AT travels ASSIGNING FIELD-SYMBOL(<travel>). " Calculer le prix (simplifie) DATA(net_price) = <travel>-TotalPrice * '0.84'. DATA(tax_amount) = <travel>-TotalPrice * '0.16'.
APPEND VALUE #( %tky = <travel>-%tky %param = VALUE za_price( TravelUUID = <travel>-TravelUUID NetPrice = net_price TaxAmount = tax_amount TotalPrice = <travel>-TotalPrice Currency = <travel>-Currency ) ) TO result. ENDLOOP. ENDMETHOD.
METHOD getAvailability. READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel FIELDS ( BeginDate MaxSeats BookedSeats ) WITH CORRESPONDING #( keys ) RESULT DATA(travels).
LOOP AT travels ASSIGNING FIELD-SYMBOL(<travel>). DATA(seats_left) = <travel>-MaxSeats - <travel>-BookedSeats.
APPEND VALUE #( %tky = <travel>-%tky %param = VALUE za_availability( TravelUUID = <travel>-TravelUUID IsAvailable = COND #( WHEN seats_left > 0 THEN abap_true ELSE abap_false ) SeatsLeft = seats_left NextAvailable = COND #( WHEN seats_left = 0 THEN <travel>-BeginDate + 30 ELSE <travel>-BeginDate ) ) ) TO result. ENDLOOP. ENDMETHOD.
METHOD getExchangeRates. " Static Function - pas de keys disponibles " Lire les taux de change du systeme SELECT * FROM tcurr WHERE kurst = 'M" AND gdatu >= @( cl_abap_context_info=>get_system_date( ) ) INTO TABLE @DATA(rates) UP TO 100 ROWS.
result = VALUE #( FOR rate IN rates ( %param = VALUE za_exchangerate( SourceCurrency = rate-fcurr TargetCurrency = rate-tcurr ExchangeRate = rate-ukurs ValidFrom = rate-gdatu ) ) ). ENDMETHOD.ENDCLASS.Action avec parametres d’entree
Les Actions peuvent accepter des parametres d’entree structures :
Behavior Definition
define behavior for ZI_Travel alias Travel{ // Action avec parametre abstrait action rejectWithReason parameter ZA_RejectionReason result [1] $self;
// Action avec plusieurs parametres action reschedule parameter ZA_RescheduleInput result [1] $self;
// Action avec Deep Structure action addParticipants parameter ZA_ParticipantList result [1] $self;}Abstract Entity pour les parametres d’entree
define abstract entity ZA_RejectionReason{ ReasonCode : abap.char( 2 ); ReasonText : abap.string( 255 ); NotifyCustomer : abap_boolean;}define abstract entity ZA_RescheduleInput{ NewBeginDate : abap.dats; NewEndDate : abap.dats; UpdatePrice : abap_boolean;}Implementation
CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS rejectWithReason FOR MODIFY IMPORTING keys FOR ACTION Travel~rejectWithReason RESULT result.
METHODS reschedule FOR MODIFY IMPORTING keys FOR ACTION Travel~reschedule RESULT result.ENDCLASS.
CLASS lhc_travel IMPLEMENTATION. METHOD rejectWithReason. " Lire le parametre depuis keys LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>). " Raison du rejet depuis le parametre DATA(reason_code) = <key>-%param-ReasonCode. DATA(reason_text) = <key>-%param-ReasonText. DATA(notify) = <key>-%param-NotifyCustomer.
" Sauvegarder le statut et la justification MODIFY ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel UPDATE FIELDS ( Status RejectionReason ) WITH VALUE #( ( %tky = <key>-%tky Status = 'X' " Rejected RejectionReason = reason_text ) ).
" Optionnel : Notifier le client IF notify = abap_true. " Envoyer un email... send_rejection_notification( travel_key = <key>-%tky reason = reason_text ). ENDIF. ENDLOOP.
" Retourner les donnees mises a jour READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(travels).
result = VALUE #( FOR travel IN travels ( %tky = travel-%tky %param = travel ) ). ENDMETHOD.
METHOD reschedule. LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>). " Nouvelles dates depuis le parametre DATA(new_begin) = <key>-%param-NewBeginDate. DATA(new_end) = <key>-%param-NewEndDate. DATA(update_price) = <key>-%param-UpdatePrice.
" Mettre a jour les dates MODIFY ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel UPDATE FIELDS ( BeginDate EndDate ) WITH VALUE #( ( %tky = <key>-%tky BeginDate = new_begin EndDate = new_end ) ).
" Optionnel : Recalculer le prix IF update_price = abap_true. recalculate_price( <key>-%tky ). ENDIF. ENDLOOP.
" Retourner le resultat READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(travels).
result = VALUE #( FOR travel IN travels ( %tky = travel-%tky %param = travel ) ). ENDMETHOD.ENDCLASS.Actions dans la Projection
Les Actions doivent etre exposees dans la Projection :
projection implementation in class zbp_c_travel unique;strict ( 2 );
define behavior for ZC_Travel alias Travel{ use action cancel; use action accept; use action rejectWithReason; use action reschedule; use function calculatePrice; use function getAvailability;}Integration UI avec Fiori Elements
Placement des boutons
define root view entity ZC_Travel provider contract transactional_query as projection on ZI_Travel{ @UI.lineItem: [ { position: 10 }, { type: #FOR_ACTION, dataAction: 'cancel', label: 'Annuler' }, { type: #FOR_ACTION, dataAction: 'accept', label: 'Approuver' } ] @UI.identification: [ { position: 10 }, { type: #FOR_ACTION, dataAction: 'rejectWithReason', label: 'Rejeter' } ] key TravelUUID,
// Header Actions @UI.fieldGroup: [{ qualifier: 'Actions', type: #FOR_ACTION, dataAction: 'reschedule', label: 'Replanifier' }] Status}Action avec dialogue
Pour les Actions avec des parametres d’entree, un dialogue est automatiquement genere :
define abstract entity ZA_RejectionReason{ @UI.defaultValue: 'OT" @Consumption.valueHelpDefinition: [{ entity: { name: 'ZI_ReasonCode', element: 'Code' }}] ReasonCode : abap.char( 2 );
@UI.multiLineText: true ReasonText : abap.string( 255 );
@UI.defaultValue: 'true" NotifyCustomer : abap_boolean;}Factory Actions
Les Factory Actions creent de nouvelles instances basees sur des instances existantes :
define behavior for ZI_Travel alias Travel{ // Factory Action cree une nouvelle instance factory action copyTravel [1];
// Factory Action avec parametre factory action createFromBooking parameter ZA_BookingRef [1];}Implementation
METHOD copyTravel. " Lire les donnees originales READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(travels).
LOOP AT travels ASSIGNING FIELD-SYMBOL(<travel>). " Creer une copie avec un nouveau %cid MODIFY ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel CREATE FIELDS ( CustomerID BeginDate EndDate Description TotalPrice Currency ) WITH VALUE #( ( %cid = |COPY_{ sy-index }| CustomerID = <travel>-CustomerID BeginDate = <travel>-BeginDate EndDate = <travel>-EndDate Description = |Copie de { <travel>-TravelID }| TotalPrice = <travel>-TotalPrice Currency = <travel>-Currency ) ) MAPPED DATA(mapped).
" mapped contient %cid et %tky de la nouvelle instance APPEND VALUE #( %cid_ref = keys[ sy-index ]-%cid_ref %key = mapped-travel[ 1 ]-%key ) TO mapped-travel. ENDLOOP.ENDMETHOD.Bonnes pratiques
-
Choisir correctement Action vs. Function : Actions pour les modifications, Functions pour les calculs.
-
Definir le Result Type : Specifiez toujours
result [n] $selfpour que l’UI se mette a jour. -
Utiliser IN LOCAL MODE : Contourner les verifications d’autorisation pour les appels internes.
-
Error Handling : Retourner les erreurs via
FAILEDetREPORTED:
METHOD cancel. " Validation IF travel-Status <> 'O'. APPEND VALUE #( %tky = travel-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'Seuls les voyages ouverts peuvent etre annules" ) ) TO reported-travel.
APPEND VALUE #( %tky = travel-%tky ) TO failed-travel. RETURN. ENDIF.ENDMETHOD.-
Combiner Feature Control : Desactivez les Actions quand elles ne sont pas applicables.
-
Coherence transactionnelle : Toutes les modifications sont commitees seulement au SAVE.
Sujets connexes
- RAP Basics - Fondamentaux du RESTful ABAP Programming Model
- Feature Control & Side Effects - Pilotage dynamique de l’UI
- Draft Handling dans RAP - Sauvegarde intermediaire avec Drafts