Actions und Functions sind die Mittel in RAP, um eigene Geschäftslogik über die Standard-CRUD-Operationen hinaus zu implementieren. Dieser Artikel erklärt die Unterschiede und zeigt, wann welches Konzept zum Einsatz kommt.
Action vs. Function: Der Unterschied
| Aspekt | Action | Function |
|---|---|---|
| Zweck | Ändert Daten (Seiteneffekte) | Liest/berechnet Daten (keine Seiteneffekte) |
| HTTP-Methode | POST | GET |
| Transaktional | Ja, Teil der Transaction | Nein, nur lesend |
| Idempotent | Nein | Ja |
| Beispiele | Stornieren, Freigeben, Kopieren | Preis berechnen, Verfügbarkeit prüfen |
Wann Action, wann Function?
- Action: Wenn die Operation den Zustand der Entität oder des Systems ändert
- Function: Wenn nur ein Wert berechnet oder eine Information abgefragt wird, ohne Daten zu ändern
Bound vs. Unbound (Static)
| Aspekt | Bound (Instance) | Unbound (Static) |
|---|---|---|
| Kontext | An eine Instanz gebunden | Ohne Instanz-Kontext |
| Aufruf | Auf selektierter Zeile | Über Button in Toolbar |
| Keys | Keys der Instanz verfügbar | Keine automatischen Keys |
| Anwendungsfall | ”Diese Reise stornieren" | "Alle offenen Reisen freigeben” |
Instance-bound Action
Eine Instance-bound Action wird auf einer konkreten Instanz ausgeführt:
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 ohne Parameter action cancel result [1] $self;
// Instance-bound Action mit Ergebnis action accept result [1] $self;
// Instance-bound Action die mehrere Instanzen zurückgibt 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. " Aktuelle Daten lesen READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel FIELDS ( TravelID Status ) WITH CORRESPONDING #( keys ) RESULT DATA(travels).
" Nur offene Reisen können storniert werden LOOP AT travels ASSIGNING FIELD-SYMBOL(<travel>) WHERE Status = 'O'.
" Status auf Cancelled setzen MODIFY ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel UPDATE FIELDS ( Status ) WITH VALUE #( ( %tky = <travel>-%tky Status = 'X' " Cancelled ) ). ENDLOOP.
" Ergebnis zurückgeben 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. " Status auf Accepted setzen 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 ) ).
" Aktualisierte Daten zurückgeben 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.Result-Kardinalität
| Kardinalität | Bedeutung |
|---|---|
result [1] $self | Genau eine Instanz zurück (die gleiche Entität) |
result [0..1] $self | Optional eine Instanz |
result [0..*] $self | Beliebig viele Instanzen |
result [1] EntityName | Eine Instanz einer anderen Entität |
| (kein result) | Keine Rückgabe |
Static (Unbound) Action
Static Actions sind nicht an eine Instanz gebunden und werden typischerweise für Massenoperationen verwendet:
Behavior Definition
define behavior for ZI_Travel alias Travel{ // Static Action ohne Parameter static action releaseAllOpen;
// Static Action mit Ergebnis static action createFromTemplate result [1] $self;
// Static Action mit Global Feature Control static action ( features: global ) massUpdate;
// Für 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. " Alle offenen Reisen finden SELECT * FROM ztravel WHERE status = 'O' INTO TABLE @DATA(open_travels).
" Status auf Released setzen 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. " Neue Reise aus Template erstellen DATA(template_id) = 'DEFAULT'.
" Template laden (vereinfachtes Beispiel) SELECT SINGLE * FROM ztravel_template WHERE template_id = @template_id INTO @DATA(template).
" Neue Reise erstellen 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 = 'Erstellt aus Template' ) ) MAPPED DATA(mapped).
" Erstellte Entität zurückgeben 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. " Prüfen ob Benutzer Berechtigung für Massenupdate hat 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: Berechnungen ohne Seiteneffekte
Functions sind ideal für Berechnungen und Abfragen, die keine Daten ändern:
Behavior Definition
define behavior for ZI_Travel alias Travel{ // Instance-bound Function function calculatePrice result [1] ZA_Price;
// Function mit mehreren Rückgabewerten function getAvailability result [1] ZA_Availability;
// Static Function static function getExchangeRates result [0..*] ZA_ExchangeRate;
// Function mit Parametern function simulateDiscount parameter ZA_DiscountInput result [1] ZA_DiscountResult;}Abstract Entity für Rückgabewerte
Functions benötigen oft abstrakte Entitäten für strukturierte Rückgabewerte:
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. " Reisedaten lesen 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>). " Preis berechnen (vereinfacht) 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 - keine keys vorhanden " Wechselkurse aus System lesen 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 mit Input-Parametern
Actions können strukturierte Input-Parameter entgegennehmen:
Behavior Definition
define behavior for ZI_Travel alias Travel{ // Action mit abstraktem Parameter action rejectWithReason parameter ZA_RejectionReason result [1] $self;
// Action mit mehreren Parametern action reschedule parameter ZA_RescheduleInput result [1] $self;
// Action mit Deep Structure action addParticipants parameter ZA_ParticipantList result [1] $self;}Abstract Entity für Input-Parameter
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. " Parameter aus keys lesen LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>). " Ablehnungsgrund aus Parameter DATA(reason_code) = <key>-%param-ReasonCode. DATA(reason_text) = <key>-%param-ReasonText. DATA(notify) = <key>-%param-NotifyCustomer.
" Status und Begründung speichern 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 ) ).
" Optional: Kunde benachrichtigen IF notify = abap_true. " E-Mail senden... send_rejection_notification( travel_key = <key>-%tky reason = reason_text ). ENDIF. ENDLOOP.
" Aktualisierte Daten zurückgeben 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>). " Neue Termine aus Parameter DATA(new_begin) = <key>-%param-NewBeginDate. DATA(new_end) = <key>-%param-NewEndDate. DATA(update_price) = <key>-%param-UpdatePrice.
" Termine aktualisieren 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 ) ).
" Optional: Preis neu berechnen IF update_price = abap_true. recalculate_price( <key>-%tky ). ENDIF. ENDLOOP.
" Ergebnis zurückgeben 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 in der Projection
Actions müssen in der Projection exponiert werden:
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;}UI-Integration mit Fiori Elements
Button-Platzierung
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: 'Stornieren' }, { type: #FOR_ACTION, dataAction: 'accept', label: 'Freigeben' } ] @UI.identification: [ { position: 10 }, { type: #FOR_ACTION, dataAction: 'rejectWithReason', label: 'Ablehnen' } ] key TravelUUID,
// Header Actions @UI.fieldGroup: [{ qualifier: 'Actions', type: #FOR_ACTION, dataAction: 'reschedule', label: 'Umbuchen' }] Status}Action mit Dialog
Für Actions mit Input-Parametern wird automatisch ein Dialog generiert:
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
Factory Actions erstellen neue Instanzen basierend auf existierenden:
define behavior for ZI_Travel alias Travel{ // Factory Action erstellt neue Instanz factory action copyTravel [1];
// Factory Action mit Parameter factory action createFromBooking parameter ZA_BookingRef [1];}Implementation
METHOD copyTravel. " Originaldaten lesen 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>). " Kopie mit neuem %cid erstellen 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 = |Kopie von { <travel>-TravelID }| TotalPrice = <travel>-TotalPrice Currency = <travel>-Currency ) ) MAPPED DATA(mapped).
" mapped enthält %cid und %tky der neuen Instanz APPEND VALUE #( %cid_ref = keys[ sy-index ]-%cid_ref %key = mapped-travel[ 1 ]-%key ) TO mapped-travel. ENDLOOP.ENDMETHOD.Best Practices
-
Action vs. Function korrekt wählen: Actions für Änderungen, Functions für Berechnungen.
-
Result Type definieren: Gib immer
result [n] $selfan, damit die UI aktualisiert wird. -
IN LOCAL MODE verwenden: Bei internen Aufrufen Berechtigungsprüfungen umgehen.
-
Error Handling: Fehler über
FAILEDundREPORTEDzurückgeben:
METHOD cancel. " Validierung IF travel-Status <> 'O'. APPEND VALUE #( %tky = travel-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'Nur offene Reisen können storniert werden' ) ) TO reported-travel.
APPEND VALUE #( %tky = travel-%tky ) TO failed-travel. RETURN. ENDIF.ENDMETHOD.-
Feature Control kombinieren: Deaktiviere Actions wenn sie nicht anwendbar sind.
-
Transaktionale Konsistenz: Alle Änderungen werden erst beim SAVE committed.
Weiterführende Themen
- RAP Basics - Grundlagen des RESTful ABAP Programming Model
- Feature Control & Side Effects - Dynamische UI-Steuerung
- Draft Handling in RAP - Zwischenspeicherung mit Drafts