EML Part 3: IN LOCAL MODE — What It Bypasses and When It Is Safe
Understand exactly what IN LOCAL MODE skips — authorization and feature control — what it does not skip, why it is needed inside a behavior pool, and why using it to call other business objects is wrong.
Explanation
📌 KEY POINTS
✅ IN LOCAL MODE skips authorization and feature control.
✅ It does NOT skip determinations, validations, locks or draft.
✅ Allowed only for the BO of the behavior pool.
⚠️ Never use it (or direct writes) to bypass another BO's checks.
▶ WHERE WE ARE
Parts 1–2 used EML from outside the BO. Inside a behavior implementation you almost always write READ/MODIFY ENTITIES … IN LOCAL MODE. This part explains why.
━━━━━━━━━━━━━━━━━━━━
▶ THE PROBLEM IN LOCAL MODE SOLVES
The BO itself must sometimes do things its consumers are not allowed to do:
- An approve action sets Status — but Status is "field ( readonly )".
- A determination sets TotalAmount — also read-only.
- An action changes an approved request's status to closed — but feature control disables update for approved requests.
- Internal operations ("internal action recalcTotal") are only callable by the BO itself.
Without local mode, RAP would apply the same checks as for an external caller and reject these changes.
━━━━━━━━━━━━━━━━━━━━
▶ WHAT IN LOCAL MODE SKIPS
- Authorization checks (global and instance) for this EML statement.
- Feature control (static and instance) — including read-only fields and disabled operations.
- It also allows calling internal operations.
━━━━━━━━━━━━━━━━━━━━
▶ WHAT IN LOCAL MODE DOES NOT SKIP
- Determinations and validations still run as triggered — your own business rules still apply.
- Locks and ETag handling of the framework.
- Draft handling: %tky decides draft or active.
- The save sequence: the change is still only in the buffer until commit.
So local mode means "the BO trusts itself", not "anything goes".
━━━━━━━━━━━━━━━━━━━━
▶ WHERE IT MAY BE USED
- Only inside the behavior pool of the same BO (the syntax check enforces this).
- For reads inside the BO as well: READ ENTITIES … IN LOCAL MODE avoids needless authorization checks on data the BO already works with.
━━━━━━━━━━━━━━━━━━━━
▶ WHAT IT MUST NOT BE USED FOR
- Bypassing authorization for the user's own request. If a user triggers an action, the action itself is checked by RAP before your code runs; inside it, local mode is fine. But do not create "helper" actions that let users change fields they are not authorized to change.
- Calling other BOs. You cannot use IN LOCAL MODE for another BO. When your BO changes another BO (e.g. creating a follow-up document), the call goes through that BO's normal checks for the current user. If the other BO explicitly allows it, it can offer a "privileged" access path (declared with "with privileged mode" in its BDEF) that your code uses with the PRIVILEGED addition — a controlled, documented exception, not a workaround.
━━━━━━━━━━━━━━━━━━━━
▶ PRIVILEGED VS LOCAL — SHORT COMPARISON
- IN LOCAL MODE — own BO only, skips authorization and feature control.
- PRIVILEGED — another BO that declared privileged mode; skips the authorization checks that BO defines as skippable (typically related to access from other trusted BOs).
━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP
- Search your behavior pool: every READ/MODIFY on the own BO should have IN LOCAL MODE.
- Check that no local-mode code changes data the user should not be able to change through that action.
- For calls to other BOs, remove any attempt to bypass their checks; use their public or interface operations.
━━━━━━━━━━━━━━━━━━━━
▶ WHAT YOU HAVE AFTER PART 3
Correct use of local mode. Part 4 covers calling other BOs and controlling commits.
Code example
" ===== Inside ZBP_R_PURCHREQ: local mode is needed =====METHOD approve. " Status is field(readonly) and update may be disabled by feature control: " without IN LOCAL MODE this MODIFY would be rejected. MODIFY ENTITIES OF zr_purchreq IN LOCAL MODE ENTITY PurchaseRequest UPDATE FIELDS ( Status ) WITH VALUE #( FOR key IN keys ( %tky = key-%tky Status = 'A' ) ). " internal action - only callable from inside the BO MODIFY ENTITIES OF zr_purchreq IN LOCAL MODE ENTITY PurchaseRequest EXECUTE recalcTotal FROM CORRESPONDING #( keys ). READ ENTITIES OF zr_purchreq IN LOCAL MODE ENTITY PurchaseRequest ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(requests). result = VALUE #( FOR r IN requests ( %tky = r-%tky %param = r ) ).ENDMETHOD." Validations and determinations triggered by this change STILL run. " ===== WRONG: trying to use local mode on ANOTHER BO =====" MODIFY ENTITIES OF zr_journalentry IN LOCAL MODE ... -> syntax error:" local mode is only allowed for the BO of the behavior pool. " ===== RIGHT: call the other BO normally (its checks apply) =====MODIFY ENTITIES OF zr_budgetreservation ENTITY Reservation CREATE FIELDS ( RequestID Amount Currency ) WITH VALUE #( ( %cid = 'RES1' RequestID = request-RequestID Amount = request-TotalAmount Currency = request-CurrencyCode ) ) FAILED DATA(res_failed) REPORTED DATA(res_reported)." -> map res_failed / res_reported into this action's failed / reported " ===== Controlled exception: the other BO allows privileged access =====" Other BO's BDEF: ... with privileged mode disabling NoCheckWhenPrivileged; (release-dependent syntax)" Caller: MODIFY ENTITIES OF zr_budgetreservation PRIVILEGED ..." Use only when the owning team explicitly offers it, and document why.Real project scenario
A developer needed a purchase request action to create a budget reservation in a finance BO owned by another team. Because the user lacked the finance authorization, the call failed, and the developer tried to use IN LOCAL MODE on the finance BO. The syntax check rejected it, so he created a helper function that wrote the reservation table directly.
In the review, the direct write was removed: it bypassed all finance validations. The finance team offered a privileged path for exactly this reservation case, documented who may use it, and the purchase request action called it with PRIVILEGED. Everything else in the finance BO kept its normal authorization checks.
Common mistakes
- Forgetting IN LOCAL MODE inside the behavior pool, so the BO blocks its own changes.
- Thinking local mode skips validations and determinations.
- Using local-mode actions to let users change fields they are not authorized to change.
- Trying to use local mode on another BO, or bypassing it with direct table writes.
- Using PRIVILEGED without the owning team's agreement and documentation.
Best practices
- Use IN LOCAL MODE for all READ/MODIFY of your own BO inside the behavior pool.
- Keep authorization at the entry point: the action or operation the user calls is checked.
- Call other BOs through their normal or interface EML API and map their responses.
- Use privileged access only when explicitly provided and documented.
- Review local-mode code for unintended permission escalation.
Interview angle
Questions: "What does IN LOCAL MODE do?", "What does it not skip?", "Can you use it for other BOs?", "What is privileged mode?"
Strong answer: local mode lets the BO bypass its own authorization and feature control (e.g. changing read-only Status in an action, calling internal actions); determinations, validations, locks and the save sequence still apply. It is only allowed for the own BO. Other BOs are called with normal checks; a BO can explicitly offer privileged access for trusted callers. Pointing out the permission-escalation risk shows maturity.
Practice this lesson
Quick check
1. Inside a purchase request action, a developer writes MODIFY ENTITIES OF a finance BO IN LOCAL MODE. What happens?
2. Which checks does IN LOCAL MODE skip?
3. Where is IN LOCAL MODE allowed?
Interview questions
1. A purchase request action must create a budget reservation in a finance BO, but the user lacks finance authorization. A developer wants to use IN LOCAL MODE or write the table directly. What do you advise?
Answer it in your own words first, then compare.
2. What does IN LOCAL MODE skip, what does it not skip, and why is it needed inside a behavior pool?
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.