Actions and Functions are the means in RAP to implement custom business logic beyond the standard CRUD operations. This article explains the differences and shows when to use which concept.
Action vs. Function: The Difference
| Aspect | Action | Function |
|---|---|---|
| Purpose | Modifies data (side effects) | Reads/calculates data (no side effects) |
| HTTP Method | POST | GET |
| Transactional | Yes, part of the transaction | No, read-only |
| Idempotent | No | Yes |
| Examples | Cancel, Release, Copy | Calculate price, Check availability |
When to Use Action vs. Function?
- Action: When the operation changes the state of the entity or system
- Function: When only a value is calculated or information is retrieved without changing data
Bound vs. Unbound (Static)
| Aspect | Bound (Instance) | Unbound (Static) |
|---|---|---|
| Context | Bound to an instance | Without instance context |
| Invocation | On selected row | Via button in toolbar |
| Keys | Instance keys available | No automatic keys |
| Use Case | ”Cancel this travel" | "Release all open travels” |
Instance-bound Action
An instance-bound action is executed on a specific instance:
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 without parameters action cancel result [1] $self;
// Instance-bound Action with result action accept result [1] $self;
// Instance-bound Action returning multiple 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. " Read current data READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel FIELDS ( TravelID Status ) WITH CORRESPONDING #( keys ) RESULT DATA(travels).
" Only open travels can be cancelled LOOP AT travels ASSIGNING FIELD-SYMBOL(<travel>) WHERE Status = 'O'.
" Set status to Cancelled MODIFY ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel UPDATE FIELDS ( Status ) WITH VALUE #( ( %tky = <travel>-%tky Status = 'X' " Cancelled ) ). ENDLOOP.
" Return result 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. " Set status to 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 ) ).
" Return updated data 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 Cardinality
| Cardinality | Meaning |
|---|---|
result [1] $self | Exactly one instance returned (the same entity) |
result [0..1] $self | Optionally one instance |
result [0..*] $self | Any number of instances |
result [1] EntityName | One instance of a different entity |
| (no result) | No return value |
Static (Unbound) Action
Static actions are not bound to an instance and are typically used for mass operations:
Behavior Definition
define behavior for ZI_Travel alias Travel{ // Static Action without parameters static action releaseAllOpen;
// Static Action with result static action createFromTemplate result [1] $self;
// Static Action with Global Feature Control static action ( features: global ) massUpdate;
// For 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. " Find all open travels SELECT * FROM ztravel WHERE status = 'O' INTO TABLE @DATA(open_travels).
" Set status to 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. " Create new travel from template DATA(template_id) = 'DEFAULT'.
" Load template (simplified example) SELECT SINGLE * FROM ztravel_template WHERE template_id = @template_id INTO @DATA(template).
" Create new travel 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 = 'Created from template' ) ) MAPPED DATA(mapped).
" Return created entity 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. " Check if user has authorization for mass update 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: Calculations Without Side Effects
Functions are ideal for calculations and queries that do not change data:
Behavior Definition
define behavior for ZI_Travel alias Travel{ // Instance-bound Function function calculatePrice result [1] ZA_Price;
// Function with multiple return values function getAvailability result [1] ZA_Availability;
// Static Function static function getExchangeRates result [0..*] ZA_ExchangeRate;
// Function with parameters function simulateDiscount parameter ZA_DiscountInput result [1] ZA_DiscountResult;}Abstract Entity for Return Values
Functions often require abstract entities for structured return values:
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. " Read travel data 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>). " Calculate price (simplified) 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 - no keys available " Read exchange rates from system 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 with Input Parameters
Actions can accept structured input parameters:
Behavior Definition
define behavior for ZI_Travel alias Travel{ // Action with abstract parameter action rejectWithReason parameter ZA_RejectionReason result [1] $self;
// Action with multiple parameters action reschedule parameter ZA_RescheduleInput result [1] $self;
// Action with Deep Structure action addParticipants parameter ZA_ParticipantList result [1] $self;}Abstract Entity for Input Parameters
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. " Read parameters from keys LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>). " Rejection reason from parameter DATA(reason_code) = <key>-%param-ReasonCode. DATA(reason_text) = <key>-%param-ReasonText. DATA(notify) = <key>-%param-NotifyCustomer.
" Save status and reason 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: Notify customer IF notify = abap_true. " Send email... send_rejection_notification( travel_key = <key>-%tky reason = reason_text ). ENDIF. ENDLOOP.
" Return updated data 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>). " New dates from parameter DATA(new_begin) = <key>-%param-NewBeginDate. DATA(new_end) = <key>-%param-NewEndDate. DATA(update_price) = <key>-%param-UpdatePrice.
" Update 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 ) ).
" Optional: Recalculate price IF update_price = abap_true. recalculate_price( <key>-%tky ). ENDIF. ENDLOOP.
" Return result 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 the Projection
Actions must be exposed in the 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;}UI Integration with Fiori Elements
Button Placement
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: 'Cancel' }, { type: #FOR_ACTION, dataAction: 'accept', label: 'Release' } ] @UI.identification: [ { position: 10 }, { type: #FOR_ACTION, dataAction: 'rejectWithReason', label: 'Reject' } ] key TravelUUID,
// Header Actions @UI.fieldGroup: [{ qualifier: 'Actions', type: #FOR_ACTION, dataAction: 'reschedule', label: 'Reschedule' }] Status}Action with Dialog
For actions with input parameters, a dialog is automatically generated:
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 create new instances based on existing ones:
define behavior for ZI_Travel alias Travel{ // Factory Action creates new instance factory action copyTravel [1];
// Factory Action with parameter factory action createFromBooking parameter ZA_BookingRef [1];}Implementation
METHOD copyTravel. " Read original data 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>). " Create copy with new %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 = |Copy of { <travel>-TravelID }| TotalPrice = <travel>-TotalPrice Currency = <travel>-Currency ) ) MAPPED DATA(mapped).
" mapped contains %cid and %tky of the new instance APPEND VALUE #( %cid_ref = keys[ sy-index ]-%cid_ref %key = mapped-travel[ 1 ]-%key ) TO mapped-travel. ENDLOOP.ENDMETHOD.Best Practices
-
Choose Action vs. Function correctly: Actions for modifications, Functions for calculations.
-
Define Result Type: Always specify
result [n] $selfso the UI gets updated. -
Use IN LOCAL MODE: Bypass authorization checks for internal calls.
-
Error Handling: Return errors via
FAILEDandREPORTED:
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 = 'Only open travels can be cancelled' ) ) TO reported-travel.
APPEND VALUE #( %tky = travel-%tky ) TO failed-travel. RETURN. ENDIF.ENDMETHOD.-
Combine with Feature Control: Disable actions when they are not applicable.
-
Transactional Consistency: All changes are committed only at SAVE.
Related Topics
- RAP Basics - Fundamentals of the RESTful ABAP Programming Model
- Feature Control & Side Effects - Dynamic UI control
- Draft Handling in RAP - Intermediate storage with Drafts