Options de traitement des Actions dans RAP : Choisir le bon pattern

Catégorie
RAP
Publié
Auteur
Johannes

Les Actions sont au coeur de la logique metier dans RAP. Mais quel type d’Action choisir et quand ? Cet article compare toutes les options de traitement et montre, a travers des exemples de reservation de vol, comment chaque type est implemente.

Vue d’ensemble des types d’Action

RAP propose differents types d’Actions pour differents cas d’utilisation :

Type d’ActionLiaisonObjectifExemple
Instance ActionLiee a l’entiteOperation sur l’instance selectionneeAnnuler une reservation
Static ActionPas de liaison d’instanceOperations en masse, creationLiberer toutes les reservations en attente
Factory ActionLiee a l’entiteCreer une nouvelle instance a partir d’une existanteCopier une reservation
Action avec parametreInstance ou StaticEntrees utilisateur avant executionAnnulation avec motif
Action avec resultatInstance ou StaticRetour de donneesRecalculer le prix

Instance Action : Operation sur une entite

Les Instance Actions operent sur une ou plusieurs instances selectionnees. C’est le type d’Action le plus courant.

Behavior Definition

managed implementation in class zbp_i_flightbooking unique;
strict ( 2 );
define behavior for ZI_FlightBooking alias FlightBooking
persistent table zflight_book
lock master
authorization master ( instance )
{
create;
update;
delete;
// Instance Action sans parametre
action confirmBooking result [1] $self;
// Instance Action avec retour de statut
action cancelBooking result [1] $self;
// Instance Action sans resultat
action markAsNoShow;
}

Implementation

CLASS lhc_flightbooking DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS confirmBooking FOR MODIFY
IMPORTING keys FOR ACTION FlightBooking~confirmBooking
RESULT result.
METHODS cancelBooking FOR MODIFY
IMPORTING keys FOR ACTION FlightBooking~cancelBooking
RESULT result.
METHODS markAsNoShow FOR MODIFY
IMPORTING keys FOR ACTION FlightBooking~markAsNoShow.
ENDCLASS.
CLASS lhc_flightbooking IMPLEMENTATION.
METHOD confirmBooking.
" Confirmer les reservations
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( booking_status )
WITH VALUE #( FOR key IN keys (
%tky = key-%tky
booking_status = 'C' " Confirmed
) ).
" Lire les donnees mises a jour
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(bookings).
" Retourner le resultat
result = VALUE #( FOR booking IN bookings (
%tky = booking-%tky
%param = booking
) ).
" Message de succes
APPEND VALUE #(
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-success
text = |{ lines( bookings ) } reservation(s) confirmee(s)| )
) TO reported-flightbooking.
ENDMETHOD.
METHOD cancelBooking.
" D'abord verifier si l'annulation est autorisee
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
FIELDS ( booking_status flight_date )
WITH CORRESPONDING #( keys )
RESULT DATA(bookings).
LOOP AT bookings INTO DATA(booking).
" Seules les reservations ouvertes ou confirmees peuvent etre annulees
IF booking-booking_status <> 'O' AND booking-booking_status <> 'C'.
APPEND VALUE #(
%tky = booking-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |La reservation au statut { booking-booking_status
} ne peut pas etre annulee| )
) TO reported-flightbooking.
APPEND VALUE #( %tky = booking-%tky ) TO failed-flightbooking.
CONTINUE.
ENDIF.
" Effectuer l'annulation
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( booking_status cancellation_date )
WITH VALUE #( (
%tky = booking-%tky
booking_status = 'X' " Cancelled
cancellation_date = cl_abap_context_info=>get_system_date( )
) ).
ENDLOOP.
" Lire et retourner le resultat
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(updated_bookings).
result = VALUE #( FOR bk IN updated_bookings (
%tky = bk-%tky
%param = bk
) ).
ENDMETHOD.
METHOD markAsNoShow.
" Action simple sans retour de resultat
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( booking_status no_show_date )
WITH VALUE #( FOR key IN keys (
%tky = key-%tky
booking_status = 'N' " No-Show
no_show_date = cl_abap_context_info=>get_system_date( )
) ).
" Seulement un message, pas de result
APPEND VALUE #(
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-success
text = 'Marque comme No-Show' )
) TO reported-flightbooking.
ENDMETHOD.
ENDCLASS.

Cardinalite du Result

SyntaxeSignificationUtilisation
result [1] $selfExactement une instance de la meme entiteStandard pour les changements de statut
result [0..1] $selfOptionnellement une instanceQuand le resultat n’est pas garanti
result [0..*] $selfN’importe quel nombre d’instancesOperations en masse
result [1] EntityNameUne autre entiteNavigation vers une entite associee
(pas de result)Pas de retourOperations fire-and-forget

Static Action : Operations en masse et creation

Les Static Actions ne sont pas liees a une instance. Elles sont typiquement utilisees pour des operations globales.

Behavior Definition

define behavior for ZI_FlightBooking alias FlightBooking
{
// Static Action pour operation en masse
static action releaseAllPending;
// Static Action avec resultat
static action createQuickBooking result [1] $self;
// Static Action avec Feature Control
static action ( features: global ) sendDailyReport;
// Pour le Global Feature Control
static features;
}

Implementation

CLASS lhc_flightbooking IMPLEMENTATION.
METHOD releaseAllPending.
" Trouver toutes les reservations avec statut 'Pending"
SELECT * FROM zflight_book
WHERE booking_status = 'P"
INTO TABLE @DATA(pending_bookings).
IF pending_bookings IS INITIAL.
APPEND VALUE #(
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-information
text = 'Aucune reservation en attente trouvee' )
) TO reported-flightbooking.
RETURN.
ENDIF.
" Mettre tous en 'Confirmed"
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( booking_status )
WITH VALUE #( FOR booking IN pending_bookings (
booking_id = booking-booking_id
booking_status = 'C' " Confirmed
) ).
APPEND VALUE #(
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-success
text = |{ lines( pending_bookings ) } reservations liberees| )
) TO reported-flightbooking.
ENDMETHOD.
METHOD createQuickBooking.
" Creer une reservation rapide avec valeurs par defaut
DATA(today) = cl_abap_context_info=>get_system_date( ).
DATA(user) = cl_abap_context_info=>get_user_technical_name( ).
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
CREATE FIELDS (
flight_date customer_id flight_id booking_status
flight_price currency_code created_by created_at
)
WITH VALUE #( (
%cid = 'QUICK_BOOKING"
flight_date = today + 14 " Dans 2 semaines
customer_id = get_default_customer( user )
flight_id = get_next_available_flight( today + 14 )
booking_status = 'P' " Pending
flight_price = 299
currency_code = 'EUR"
created_by = user
created_at = cl_abap_context_info=>get_system_time( )
) )
MAPPED DATA(mapped).
" Lire la reservation creee
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH VALUE #( ( %tky = mapped-flightbooking[ 1 ]-%tky ) )
RESULT DATA(created_bookings).
" Retourner le resultat avec %cid_ref
result = VALUE #( (
%cid_ref = 'QUICK_BOOKING"
%param = created_bookings[ 1 ]
) ).
APPEND VALUE #(
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-success
text = 'Reservation rapide creee' )
) TO reported-flightbooking.
ENDMETHOD.
METHOD get_global_features.
" Verifier si l'utilisateur a l'autorisation pour le rapport
DATA(has_report_auth) = check_report_authorization( ).
result = VALUE #(
%action-sendDailyReport = COND #(
WHEN has_report_auth = abap_true
THEN if_abap_behv=>fc-o-enabled
ELSE if_abap_behv=>fc-o-disabled )
).
ENDMETHOD.
ENDCLASS.

Factory Action : Nouvelle instance a partir d’une existante

Les Factory Actions creent une nouvelle instance basee sur une existante. Ideal pour les scenarios de copie et de modeles.

Behavior Definition

define behavior for ZI_FlightBooking alias FlightBooking
{
// Factory Action cree une copie
factory action copyBooking [1];
// Factory Action avec parametres
factory action rebookToDate parameter ZA_RebookParams [1];
}

Implementation

CLASS lhc_flightbooking IMPLEMENTATION.
METHOD copyBooking.
" Lire les reservations originales
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(bookings).
DATA: lt_mapped TYPE RESPONSE FOR MAPPED EARLY zi_flightbooking.
LOOP AT bookings INTO DATA(booking).
DATA(lv_cid) = |COPY_{ sy-tabix }|.
" Creer une copie avec nouveau statut
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
CREATE FIELDS (
flight_date customer_id flight_id
flight_price currency_code booking_status
created_by created_at
)
WITH VALUE #( (
%cid = lv_cid
flight_date = booking-flight_date
customer_id = booking-customer_id
flight_id = booking-flight_id
flight_price = booking-flight_price
currency_code = booking-currency_code
booking_status = 'P' " Nouvelle reservation en Pending
created_by = cl_abap_context_info=>get_user_technical_name( )
created_at = cl_abap_context_info=>get_system_time( )
) )
MAPPED DATA(mapped_single).
" Agreger mapped
APPEND VALUE #(
%cid_ref = keys[ sy-tabix ]-%cid_ref
%key = mapped_single-flightbooking[ 1 ]-%key
) TO mapped-flightbooking.
ENDLOOP.
APPEND VALUE #(
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-success
text = |{ lines( bookings ) } reservation(s) copiee(s)| )
) TO reported-flightbooking.
ENDMETHOD.
METHOD rebookToDate.
" Lire les reservations originales
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(bookings).
LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>).
READ TABLE bookings INTO DATA(booking)
WITH KEY booking_id = <key>-booking_id.
" Nouvelle date depuis le parametre
DATA(new_date) = <key>-%param-NewFlightDate.
" Validation
IF new_date < cl_abap_context_info=>get_system_date( ).
APPEND VALUE #(
%tky = <key>-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'La nouvelle date doit etre dans le futur' )
) TO reported-flightbooking.
APPEND VALUE #( %tky = <key>-%tky ) TO failed-flightbooking.
CONTINUE.
ENDIF.
" Creer une nouvelle reservation avec la nouvelle date
DATA(lv_cid) = |REBOOK_{ sy-tabix }|.
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
CREATE FIELDS (
flight_date customer_id flight_id
flight_price currency_code booking_status
original_booking_id
)
WITH VALUE #( (
%cid = lv_cid
flight_date = new_date
customer_id = booking-customer_id
flight_id = booking-flight_id
flight_price = booking-flight_price
currency_code = booking-currency_code
booking_status = 'P' " Pending
original_booking_id = booking-booking_id " Reference a la reservation originale
) )
MAPPED DATA(mapped_single).
" Mettre la reservation originale en 'Rebooked"
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( booking_status )
WITH VALUE #( (
%tky = <key>-%tky
booking_status = 'R' " Rebooked
) ).
APPEND VALUE #(
%cid_ref = <key>-%cid_ref
%key = mapped_single-flightbooking[ 1 ]-%key
) TO mapped-flightbooking.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Action avec parametre de resultat

Les Actions peuvent retourner des resultats structures qui vont au-dela de $self.

Abstract Entity pour le resultat

@EndUserText.label: 'Resultat de calcul de prix"
define abstract entity ZA_PriceCalculation
{
BookingId : abap.numc(10);
NetPrice : abap.curr(15,2);
TaxAmount : abap.curr(15,2);
TotalPrice : abap.curr(15,2);
Currency : abap.cuky;
DiscountPct : abap.dec(5,2);
DiscountText : abap.string(100);
}

Behavior Definition

define behavior for ZI_FlightBooking alias FlightBooking
{
// Action avec type de resultat propre
action recalculatePrice result [1] ZA_PriceCalculation;
// Action qui retourne une autre entite
action createInvoice result [1] ZI_Invoice;
}

Implementation

METHOD recalculatePrice.
" Lire les donnees de reservation
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
FIELDS ( booking_id flight_price currency_code customer_id )
WITH CORRESPONDING #( keys )
RESULT DATA(bookings).
LOOP AT bookings INTO DATA(booking).
" Determiner la remise client
DATA(discount_pct) = get_customer_discount( booking-customer_id ).
DATA(net_price) = booking-flight_price * ( 1 - discount_pct / 100 ).
DATA(tax_amount) = net_price * '0.19'.
DATA(total_price) = net_price + tax_amount.
" Mettre a jour le prix dans la reservation
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( flight_price )
WITH VALUE #( (
%tky = booking-%tky
flight_price = total_price
) ).
" Remplir la structure de resultat
APPEND VALUE #(
%tky = booking-%tky
%param = VALUE za_pricecalculation(
BookingId = booking-booking_id
NetPrice = net_price
TaxAmount = tax_amount
TotalPrice = total_price
Currency = booking-currency_code
DiscountPct = discount_pct
DiscountText = COND #(
WHEN discount_pct > 0
THEN |Remise fidelite : { discount_pct }%|
ELSE 'Pas de remise' )
)
) TO result.
ENDLOOP.
ENDMETHOD.

Gestion des erreurs dans les Actions

Les Actions robustes doivent gerer correctement les erreurs et les signaler a l’utilisateur.

Pattern : Validation avant execution

METHOD processBooking.
DATA: lt_failed TYPE TABLE FOR FAILED EARLY zi_flightbooking,
lt_reported TYPE TABLE FOR REPORTED EARLY zi_flightbooking.
" Lire les reservations
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(bookings).
LOOP AT bookings INTO DATA(booking).
" === PHASE DE VALIDATION ===
" Erreur 1 : Verifier le statut
IF booking-booking_status = 'X'.
APPEND VALUE #(
%tky = booking-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Les reservations annulees ne peuvent pas etre traitees' )
) TO lt_reported.
APPEND VALUE #( %tky = booking-%tky ) TO lt_failed.
CONTINUE.
ENDIF.
" Erreur 2 : Verifier la date
IF booking-flight_date < cl_abap_context_info=>get_system_date( ).
APPEND VALUE #(
%tky = booking-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'La date de vol est dans le passe' )
%element-flight_date = if_abap_behv=>mk-on
) TO lt_reported.
APPEND VALUE #( %tky = booking-%tky ) TO lt_failed.
CONTINUE.
ENDIF.
" Avertissement : Prix eleve
IF booking-flight_price > 2000.
APPEND VALUE #(
%tky = booking-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-warning
text = |Prix superieur a 2 000 { booking-currency_code }| )
%element-flight_price = if_abap_behv=>mk-on
) TO lt_reported.
" PAS d'entree failed pour Warning !
ENDIF.
" === PHASE D'EXECUTION ===
TRY.
" Executer la logique metier
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( booking_status processed_at )
WITH VALUE #( (
%tky = booking-%tky
booking_status = 'D' " Done
processed_at = cl_abap_context_info=>get_system_time( )
) ).
CATCH cx_root INTO DATA(lx_error).
" Erreur inattendue
APPEND VALUE #(
%tky = booking-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Erreur systeme : { lx_error->get_text( ) }| )
) TO lt_reported.
APPEND VALUE #( %tky = booking-%tky ) TO lt_failed.
ENDTRY.
ENDLOOP.
" Transmettre les erreurs et messages
failed-flightbooking = lt_failed.
reported-flightbooking = lt_reported.
" Resultat pour les reservations reussies
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(updated_bookings).
result = VALUE #( FOR bk IN updated_bookings
WHERE ( booking_status = 'D' ) (
%tky = bk-%tky
%param = bk
) ).
ENDMETHOD.

Pattern : Succes partiels

METHOD batchProcess.
DATA: lv_success_count TYPE i,
lv_error_count TYPE i.
READ ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(bookings).
LOOP AT bookings INTO DATA(booking).
IF can_process( booking ).
" Traiter
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE
ENTITY FlightBooking
UPDATE FIELDS ( booking_status )
WITH VALUE #( ( %tky = booking-%tky booking_status = 'D' ) ).
lv_success_count = lv_success_count + 1.
ELSE.
" Collecter les erreurs mais continuer
APPEND VALUE #(
%tky = booking-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Reservation { booking-booking_id } : Traitement impossible| )
) TO reported-flightbooking.
APPEND VALUE #( %tky = booking-%tky ) TO failed-flightbooking.
lv_error_count = lv_error_count + 1.
ENDIF.
ENDLOOP.
" Message recapitulatif
IF lv_success_count > 0 AND lv_error_count > 0.
APPEND VALUE #(
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-warning
text = |{ lv_success_count } traite(s), {
lv_error_count } echec(s)| )
) TO reported-flightbooking.
ELSEIF lv_success_count > 0.
APPEND VALUE #(
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-success
text = |{ lv_success_count } reservations traitees| )
) TO reported-flightbooking.
ENDIF.
ENDMETHOD.

Matrice de decision : Quelle Action quand ?

ExigenceType d’ActionExemple
Changement de statut d’une reservationInstance ActionconfirmBooking, cancelBooking
Liberer toutes les reservations ouvertesStatic ActionreleaseAllPending
Copier une reservationFactory ActioncopyBooking
Annulation avec motifInstance Action + ParametrecancelWithReason
Nouvelle reservation sans modeleStatic Action + ResultcreateQuickBooking
Report a une nouvelle dateFactory Action + ParametrerebookToDate
Recalculer et afficher le prixInstance Action + Result EntityrecalculatePrice
Creer une facture depuis une reservationInstance Action + autre entitecreateInvoice

Quand Instance vs. Static ?

┌─────────────────────────────────────────────────────────────────┐
│ L'Action a-t-elle besoin │
│ du contexte d'instance ? │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ │ │
│ ▼ ▼ │
│ [ OUI ] [ NON ] │
│ │ │ │
│ ┌──────────┴──────────┐ │ │
│ │ │ ▼ │
│ ▼ ▼ STATIC ACTION │
│ Cree une nouvelle Modifie une │
│ instance ? existante ? │
│ │ │ │
│ ▼ ▼ │
│ FACTORY INSTANCE │
│ ACTION ACTION │
└─────────────────────────────────────────────────────────────────┘

Quand avec ou sans parametre ?

ScenarioAvec parametreSans parametre
Entree utilisateur requiseDialogue popup
Confirmation sans detailsExecution directe
Infos supplementaires optionnellesParametre avec defauts
Action avec choixParametre avec Value Help

Quand utiliser Result ?

ScenarioType de Result
L’UI doit etre mise a jourresult [1] $self
Fire-and-ForgetPas de Result
Afficher le resultat du calculresult [1] ZA_ResultEntity
Operation en masse avec resultats individuelsresult [0..*] $self
Entite associee creeeresult [1] OtherEntity

Bonnes pratiques

1. Convention de nommage coherente

" Verbes pour les Actions
action confirmBooking ...
action cancelBooking ...
action recalculatePrice ...
" Eviter les substantifs
action confirmation ... " Pas clair ce qui se passe
action cancellation ...

2. Toujours specifier Result pour le rafraichissement UI

" L'UI est mise a jour apres l'Action
action confirmBooking result [1] $self;
" L'UI reste sur l'ancien etat
action confirmBooking; " Sans result

3. Detecter les erreurs tot

METHOD myAction.
" Validation D'ABORD, avant toute modification
READ ENTITIES ...
LOOP AT ...
IF NOT valid( ).
" Signaler l'erreur et CONTINUE
CONTINUE.
ENDIF.
ENDLOOP.
" Seulement apres la validation : effectuer les modifications
MODIFY ENTITIES ...
ENDMETHOD.

4. Utiliser IN LOCAL MODE

" Contourne la verification d'autorisation au sein de l'Action
READ ENTITIES OF zi_flightbooking IN LOCAL MODE ...
MODIFY ENTITIES OF zi_flightbooking IN LOCAL MODE ...

5. Combiner avec Feature Control

" Behavior Definition
action ( features: instance ) cancelBooking result [1] $self;
determination setFeatures on modify { field booking_status; }
" Implementation : cancelBooking active seulement pour les reservations ouvertes

Resume

Type d’ActionQuand l’utiliserKeys presentesCree une instance
InstanceOperation sur la selectionOuiNon
StaticOperation globaleNonOptionnel
FactoryCopie, modelesOuiOui

Le bon choix du type d’Action fait la difference entre une interface utilisateur intuitive et une confuse. Utilisez les Instance Actions pour les operations individuelles, les Static Actions pour les actions globales et les Factory Actions pour creer de nouvelles instances a partir d’existantes.

Articles complementaires : RAP Actions et Functions pour les fondamentaux, Actions avec Popup pour les Actions avec dialogue, Messages RAP pour la gestion des erreurs et Feature Control pour l’activation dynamique.