Behavior Definitions Part 3: Actions, Functions, Determinations and Validations
Declare business logic in the BDEF correctly: instance, static, factory and internal actions, actions with parameters, functions, determinations with the right triggers, validations, determine actions and side effects.
Explanation
📌 KEY POINTS
✅ Button → action; read-only result → function.
✅ Automatic value → determination; block save → validation.
✅ List validations in draft determine action Prepare.
💡 Use side effects to refresh the UI instead of "recalculate" buttons.
▶ WHERE WE ARE
Part 2 covered operations and fields. This part covers declarations that carry business logic. Each becomes a method in the behavior pool (Behavior Implementations series).
━━━━━━━━━━━━━━━━━━━━
▶ 1. ACTIONS — EXPLICIT BUSINESS STEPS
- Instance action: "action approve result [1] $self;" — works on selected instances, returns them.
- Static action: "static action createFromTemplate result [1] $self;" — no instance needed.
- Factory action: "factory action copyRequest [1];" — creates new instances (e.g. copy); returns them via MAPPED.
- Static factory action: "static factory action createDefault [1];".
- Internal action: "internal action recalcTotal;" — only callable inside the BO.
- With parameter: "action reject parameter ZD_RejectReason result [1] $self;" — the parameter is an abstract CDS entity (define abstract entity ZD_RejectReason { Reason : abap.char(100); }); Fiori shows a dialog for it.
- Result options: [1] $self (same entity), [0..*] or [1] of another entity, or no result.
- Options: ( features : instance ), ( authorization : update ), ( precheck ).
━━━━━━━━━━━━━━━━━━━━
▶ 2. FUNCTIONS — READ-ONLY OPERATIONS
"function getApprovalPath result [0..*] ZD_ApprovalStep;" — returns calculated data without changing anything. Use a function when a consumer needs computed information that should not be a CDS field (e.g. expensive or dependent on buffer state).
━━━━━━━━━━━━━━━━━━━━
▶ 3. DETERMINATIONS — AUTOMATIC DERIVATIONS
Syntax: determination Name on modify | on save { trigger list; }
- Trigger operations: create; update; delete; (delete only for determinations on modify of child entities).
- Trigger fields: field Quantity, Price;
- on modify — runs right after the triggering change in the interaction phase; the user sees the result immediately (important with draft).
- on save — runs once in the save sequence (e.g. drawing numbers).
Determinations must be idempotent and may modify the BO via EML IN LOCAL MODE.
━━━━━━━━━━━━━━━━━━━━
▶ 4. VALIDATIONS — CHECKS THAT BLOCK SAVE
Syntax: validation Name on save { create; field Description; }
Validations always run on save, only when their triggers occurred. They cannot change data; they report FAILED and REPORTED. With draft, list them in "draft determine action Prepare { validation … ; }" so users see errors when clicking Save.
━━━━━━━━━━━━━━━━━━━━
▶ 5. DETERMINE ACTIONS — RUN CHECKS ON DEMAND
"determine action checkRequest { validation validateDescription; validation Item~validateQuantity; determination calculateTotal; }" — lets a consumer (or the UI) execute selected determinations and validations without saving. "Prepare" is the draft-specific determine action.
━━━━━━━━━━━━━━━━━━━━
▶ 6. SIDE EFFECTS — TELL THE UI WHAT TO RELOAD
"side effects { field Price affects field TotalAmount; action approve affects field Status; }" — metadata for Fiori Elements: after the user changes Price, the UI reloads TotalAmount; after approve, it reloads Status. Side effects do not execute logic; they only tell the UI what may have changed (available on current releases).
━━━━━━━━━━━━━━━━━━━━
▶ CHOOSING THE RIGHT ELEMENT
Button → action. Computed read-only result → function. Automatic value → determination. Block invalid save → validation. Run checks on demand → determine action. Keep UI in sync → side effects.
━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP
- From the requirements, list every piece of business behavior and classify it with the table above.
- Declare it in the BDEF; activate; generate methods with Ctrl+1.
- For parameters, create the abstract entity first.
- Add side effects for every determination the user should see immediately.
━━━━━━━━━━━━━━━━━━━━
▶ WHAT YOU HAVE AFTER PART 3
Correct declarations for all business logic. Part 4 covers associations, compositions and draft.
Code example
// ===== Abstract entity for an action parameter =====@EndUserText.label: 'Rejection reason'define abstract entity ZD_RejectReason{ Reason : abap.char( 100 );} // ===== BDEF: business logic declarations (root entity) =====define behavior for ZR_PurchReq alias PurchaseRequest/* entity header as in Part 1 */{ // actions action ( features : instance ) submit result [1] $self; action ( features : instance ) approve result [1] $self; action ( features : instance ) reject parameter ZD_RejectReason result [1] $self; factory action copyRequest [1]; // new instance from an existing one static action createFromTemplate result [1] $self; internal action recalcTotal; // only from inside the BO // function: read-only, computed result function getApprovalLevel result [1] ZD_ApprovalLevel; // ZD_ApprovalLevel = abstract entity // determinations determination setInitialStatus on modify { create; } determination setRequestID on save { create; } // validations validation validateDescription on save { create; field Description; } validation validateSupplier on save { create; field Supplier; } // run checks on demand (without saving) determine action checkRequest { validation validateDescription; validation validateSupplier; validation Item~validateQuantity; } // UI refresh hints for Fiori Elements side effects { action approve affects field Status; action reject affects field Status; }} define behavior for ZR_PurchReqItem alias Item/* entity header */{ determination calculateTotal on modify { create; delete; field Quantity, Price; } validation validateQuantity on save { create; field Quantity; }} " Generated method signatures (examples):" METHODS reject FOR MODIFY IMPORTING keys FOR ACTION PurchaseRequest~reject RESULT result." -> keys[ ... ]-%param-Reason holds the dialog input" METHODS copyRequest FOR MODIFY IMPORTING keys FOR ACTION PurchaseRequest~copyRequest." -> create via EML and fill MAPPED" METHODS getApprovalLevel FOR READ IMPORTING keys FOR FUNCTION PurchaseRequest~getApprovalLevel RESULT result.Real project scenario
A team exposed a technical action "forceRecalculate" on the UI because users sometimes saw outdated totals after changing item quantities. The root cause was not missing logic: the total determination already ran on modify, but Fiori did not know that TotalAmount had changed, so it did not reload it.
Replacing the action with a side effect ("field Quantity affects field TotalAmount" at the right level) made the total refresh immediately, and the extra button — which confused users and was sometimes pressed on approved requests — was removed. The rejection flow was also improved by turning a free-text field on the request into an action parameter shown in a dialog.
Common mistakes
- Implementing derivations as actions so they depend on someone clicking.
- Using an action for a pure read (use a function).
- Validations declared without trigger fields, so they run for every change or never.
- Forgetting to list validations in Prepare, so draft users see errors late.
- Adding "recalculate" buttons instead of side effects.
- Declaring a parameter action without creating the abstract entity.
Best practices
- Classify behavior: action, function, determination, validation, determine action, side effect.
- Keep determination and validation triggers specific to the fields that matter.
- Use internal actions for reusable logic called from other handlers.
- Use abstract entities for action parameters and results.
- Declare side effects for derived fields users must see immediately.
Interview angle
Questions: "What action types exist in RAP?", "Action vs function?", "What triggers can a determination have?", "What are side effects?"
Strong answer: instance, static, factory, internal and parameterized actions; functions are read-only and return computed data; determinations run on modify or on save with operation and field triggers; validations always on save; determine actions run checks on demand (Prepare for draft); side effects only tell Fiori what to reload. Choosing side effects over a recalculate button is a good practical example.
Practice this lesson
Quick check
1. A recalculation action must be callable by other handlers of the BO but never by UI or API consumers. How is it declared?
2. Totals must always be recalculated when item quantities change, whatever channel changes them. Which BDEF element fits?
3. In a RAP behavior definition, determinations can be triggered either 'on save' or 'on modify'. Which statement correctly characterizes the difference between these two trigger points in the RAP save sequence?
Interview questions
1. Which kinds of actions and functions can you declare in a BDEF? Give a use case for each.
Answer it in your own words first, then compare.
2. Users see outdated totals in a Fiori app after changing item quantities, and someone proposes a 'Recalculate' button. What would you do instead?
Answer it in your own words first, then compare.
Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.
More lessons in this topic
- Behavior Definitions Part 1: Reading a BDEF as the BO Contract
- Behavior Definitions Part 2: CRUD, Field Control, Numbering, Locking and Authorization
- Behavior Definitions Part 4: Associations, Compositions and Draft Declarations
- Behavior Definitions Part 5: Projection Behavior, Strict Mode and Stable Contracts