|
1 | | ---- |
2 | | -outline: [2, 4] |
3 | | ---- |
4 | | -# Locks |
5 | | - |
6 | | -Business logic often needs to prevent two users from editing the same object at the same time. Because abap2UI5 runs stateless by default, locks held with `ENQUEUE` / `DEQUEUE` are released at the end of each roundtrip — so locking needs a deliberate approach. |
7 | | - |
8 | | -### Lock with a Stateful Session |
9 | | -Switch the session to stateful and call the lock function module. The lock survives subsequent roundtrips as long as the session stays stateful: |
10 | | -```abap |
11 | | -CALL FUNCTION `ENQUEUE_E_TABLE` |
12 | | - EXPORTING |
13 | | - tabname = `ZTEST` |
14 | | - varkey = varkey |
15 | | - EXCEPTIONS |
16 | | - foreign_lock = 1 |
17 | | - system_failure = 2 |
18 | | - OTHERS = 3. |
19 | | -
|
20 | | -IF sy-subrc = 0. |
21 | | - client->set_session_stateful( ). |
22 | | -ELSE. |
23 | | - client->set_session_stateful( abap_false ). |
24 | | -ENDIF. |
| 1 | +# Locking Sales Orders in abap2UI5 — A Beginner's Guide |
| 2 | + |
| 3 | +This guide explains **how to lock business objects in abap2UI5 apps**, step by step, starting from the simplest case and adding one layer at a time. |
| 4 | + |
| 5 | +Every section points at a **complete, copy-paste-ready demo class** stored in this repository. To try a snippet: |
| 6 | + |
| 7 | +1. Open the linked file. |
| 8 | +2. Create a new ABAP class with the matching name (e.g. `z2ui5_test_lock_01`). |
| 9 | +3. Paste the file contents as the class source. |
| 10 | +4. Activate it. |
| 11 | +5. Launch via the abap2UI5 launchpad URL with `?app=<class_name>`. |
| 12 | + |
| 13 | +We use the **sales order header table `VBAK`** as the example throughout, because it ships with every SAP system and has a real, standard SAP enqueue object (`EVVBAK`). |
| 14 | + |
| 15 | +## Repository layout |
| 16 | + |
| 17 | +``` |
| 18 | +scenarios/ |
| 19 | + z2ui5_test_lock_01.clas.abap Scenario 1 — Naive editing (no locking) |
| 20 | + z2ui5_test_lock_02.clas.abap Scenario 2 — Enqueue at save |
| 21 | + z2ui5_test_lock_03.clas.abap Scenario 3 — Optimistic locking |
| 22 | + z2ui5_test_lock_04.clas.abap Scenario 4 — Enqueue + Optimistic (recommended) |
| 23 | + z2ui5_test_lock_05.clas.abap Scenario 5 — Stateful session |
| 24 | + z2ui5_test_lock_06.clas.abap Scenario 6 — Soft lock (advisory) |
| 25 | + z2ui5_test_lock_07.clas.abap Scenario 7 — Standard SAP BO draft via EML |
| 26 | + z2ui5_test_lock_08.clas.abap Scenario 8 — Platform lock manager (demo) |
| 27 | +
|
| 28 | +platform-lock-manager/ |
| 29 | + README.md DDIC setup + function module interface |
| 30 | + z_request_locking.fugr.abap Reusable lock-handling function module |
25 | 31 | ``` |
26 | 32 |
|
27 | | -Release the lock by ending the stateful session — for example on navigation away from the edit view: |
28 | | -```abap |
29 | | -client->set_session_stateful( abap_false ). |
30 | | -client->nav_app_leave( ). |
| 33 | +--- |
| 34 | + |
| 35 | +## 1. Why is locking on the web different? |
| 36 | + |
| 37 | +In classic SAP GUI, you open transaction `VA02`, the system calls `ENQUEUE_EVVBAK`, and **the lock lives as long as your dialog session lives**. You can think for ten minutes — the lock is still there. |
| 38 | + |
| 39 | +A web app is **stateless** by default. Every roundtrip (HTTP POST) is a fresh ABAP session. A lock set during one roundtrip is gone by the next one. abap2UI5 lets you opt into **stateful sessions**, but you have to think about *when* you want that. |
| 40 | + |
| 41 | +That difference creates the entire field of "web locking strategies." We will go through them one by one. |
| 42 | + |
| 43 | +### The two questions you must answer |
| 44 | + |
| 45 | +| Axis | Question | |
| 46 | +|---|---| |
| 47 | +| **A — Edit phase** | What happens while the user is *thinking and typing*? | |
| 48 | +| **B — Save phase** | What happens *the moment they hit save*? | |
| 49 | + |
| 50 | +Every realistic app picks one strategy per axis. They compose. |
| 51 | + |
| 52 | +--- |
| 53 | + |
| 54 | +## 2. Scenario 1 — Naive editing (no locking) |
| 55 | + |
| 56 | +**Source:** [`scenarios/z2ui5_test_lock_01.clas.abap`](scenarios/z2ui5_test_lock_01.clas.abap) |
| 57 | + |
| 58 | +The simplest possible starting point. The user can change a sales order and save. **There is no lock and no conflict check.** Last save wins, silently. |
| 59 | + |
| 60 | +This is rarely what you want in production, but it is the right place to *start* — every later scenario layers a single concept on top of this one, so you can see exactly what each layer buys you. |
| 61 | + |
| 62 | +**When to use this:** |
| 63 | +- Personal sandboxes, throwaway demos, internal tools where only one user ever touches a record |
| 64 | + |
| 65 | +**Key idea:** `client->_bind_edit( auart )` creates a two-way binding — what the user types lands back in `auart` before the SAVE event fires. There is no lock and no check. If two users edit the same order, the second save silently wipes the first one's changes. Every later scenario fixes a specific piece of this problem. |
| 66 | + |
| 67 | +--- |
| 68 | + |
| 69 | +## 3. Scenario 2 — Edit + Enqueue at save |
| 70 | + |
| 71 | +**Source:** [`scenarios/z2ui5_test_lock_02.clas.abap`](scenarios/z2ui5_test_lock_02.clas.abap) |
| 72 | + |
| 73 | +Now the user can change the sales order type. We do **not** hold a lock while they think. At the moment they press *Save*, we lock, write, commit, and release in one short roundtrip. |
| 74 | + |
| 75 | +**When to use this:** |
| 76 | +- Quick edits with low chance of two users hitting the same record |
| 77 | +- Default starting point for most stateless editing apps |
| 78 | + |
| 79 | +**Key idea:** the lock only exists for milliseconds, during the save event. Two users editing the same order in parallel will both succeed if neither saves at the literal same instant — the last save wins. That is a problem we fix in the next scenario. |
| 80 | + |
| 81 | +--- |
| 82 | + |
| 83 | +## 4. Scenario 3 — Optimistic locking (timestamp check) |
| 84 | + |
| 85 | +**Source:** [`scenarios/z2ui5_test_lock_03.clas.abap`](scenarios/z2ui5_test_lock_03.clas.abap) |
| 86 | + |
| 87 | +Now we add a *conflict check*. On read we remember the record's last-changed timestamp. On save, we re-read it and **reject** if it changed in the meantime. This is the same idea as HTTP ETag or OData's `@odata.etag`. |
| 88 | + |
| 89 | +**When to use this:** |
| 90 | +- Any time silent overwrites would be a problem |
| 91 | +- Combine with Scenario 2 — it costs almost nothing and catches real bugs |
| 92 | + |
| 93 | +**Key idea:** `token_aedat`/`token_aezet` travel with the app session. On save, we re-read the live values from the database and reject if they have shifted. No lock is held during the edit phase, so this scales to many concurrent users. |
| 94 | + |
| 95 | +--- |
| 96 | + |
| 97 | +## 5. Scenario 4 — Combined (the recommended default) |
| 98 | + |
| 99 | +**Source:** [`scenarios/z2ui5_test_lock_04.clas.abap`](scenarios/z2ui5_test_lock_04.clas.abap) |
| 100 | + |
| 101 | +Enqueue at save **and** the optimistic check. This is the safest stateless pattern and the one most production apps should default to. |
| 102 | + |
| 103 | +**Why both:** |
| 104 | +- The enqueue serializes concurrent writers so two saves cannot interleave. |
| 105 | +- The timestamp check catches anyone who slipped in via another path (SE16, batch job, classic SAP GUI) between your read and your write. |
| 106 | + |
| 107 | +**Key idea:** belt-and-suspenders. The enqueue prevents two parallel saves of *this* app from colliding. The timestamp check catches everything else. |
| 108 | + |
| 109 | +--- |
| 110 | + |
| 111 | +## 6. Scenario 5 — Stateful session with a persistent enqueue |
| 112 | + |
| 113 | +**Source:** [`scenarios/z2ui5_test_lock_05.clas.abap`](scenarios/z2ui5_test_lock_05.clas.abap) |
| 114 | + |
| 115 | +Sometimes you want classic SAP GUI behaviour: the moment the user opens the screen, the lock is held until they save or leave. With `client->set_session_stateful( )`, abap2UI5 keeps the session alive between roundtrips so a real `ENQUEUE_EVVBAK` survives. |
| 116 | + |
| 117 | +**When to use this:** |
| 118 | +- Internal back-office apps with few concurrent users |
| 119 | +- You want users to *immediately* see "locked by X" when opening |
| 120 | + |
| 121 | +**Cost:** each active user pins a work process. Do **not** use this for high-traffic apps. |
| 122 | + |
| 123 | +**Key idea:** while the app is open, an SM12 entry for `EVVBAK` / `0000004711` is visible. The user can navigate, type, wait — the lock stays. If they close the browser without pressing *Cancel*, the lock will eventually expire when the session times out. |
| 124 | + |
| 125 | +**Compare with `z2ui5_cl_demo_app_350`** in the abap2UI5 demos repo for a similar pattern using `ENQUEUE_E_TABLE`. |
| 126 | + |
| 127 | +--- |
| 128 | + |
| 129 | +## 7. Scenario 6 — Soft lock (advisory only) |
| 130 | + |
| 131 | +**Source:** [`scenarios/z2ui5_test_lock_06.clas.abap`](scenarios/z2ui5_test_lock_06.clas.abap) |
| 132 | + |
| 133 | +A soft lock is a row in a **custom Z table** marking *"user X is editing sales order Y."* It is **not** enforced by the SAP kernel — only your app code respects it. Use it for **UX feedback** ("locked by Alice since 09:32"), always layered on top of a real save-time guard. |
| 134 | + |
| 135 | +### 7.1 Create the Z table |
| 136 | + |
| 137 | +Create a small table in SE11 called `ZS_SO_LOCK`: |
| 138 | + |
| 139 | +| Field | Type | Description | |
| 140 | +|------------|-------------|-----------------------| |
| 141 | +| MANDT | MANDT | Client (key) | |
| 142 | +| VBELN | VBELN_VA | Sales order (key) | |
| 143 | +| USERNAME | SYUNAME | Editing user | |
| 144 | +| LOCKED_AT | TIMESTAMPL | When the lock started | |
| 145 | + |
| 146 | +Delivery class `A`. Activate. |
| 147 | + |
| 148 | +### 7.2 The app |
| 149 | + |
| 150 | +See [`scenarios/z2ui5_test_lock_06.clas.abap`](scenarios/z2ui5_test_lock_06.clas.abap). |
| 151 | + |
| 152 | +**Key idea:** the soft lock decides *who can edit in the UI*. The enqueue + timestamp check decides *whether the save is allowed*. They serve different jobs. |
| 153 | + |
| 154 | +**Gotcha:** if a user closes the browser without pressing *Release & Exit*, the row stays in `ZS_SO_LOCK` forever. In production you would add a small background job that deletes rows older than, say, 30 minutes. |
| 155 | + |
| 156 | +--- |
| 157 | + |
| 158 | + |
| 159 | +## 10. Side-by-side comparison |
| 160 | + |
| 161 | +| Scenario | SM12 entry during edit | Pins WP | Survives browser close | Conflict detection | Complexity | |
| 162 | +|---|---|---|---|---|---| |
| 163 | +| 1 Naive editing | — | — | — | — | trivial | |
| 164 | +| 2 Enqueue at save | only at save | no | — | at save (race possible) | low | |
| 165 | +| 3 Optimistic | — | no | — | at save (reliable) | low | |
| 166 | +| 4 **Enqueue + Optimistic** | only at save | no | — | at save (reliable) | low | |
| 167 | +| 5 Stateful session | yes, full duration | **yes** | dies on timeout | at open | medium | |
| 168 | +| 6 Soft lock + save guard | only at save | no | row lingers | UX at open + data at save | medium | |
| 169 | +| 7 **Standard BO draft via EML** | held while draft exists | no | **draft persists, user can resume** | framework handles it | low (no BO to build) | |
| 170 | +| 8 **Platform lock manager** | only at save | no | auto-expires via heartbeat | UX at open + data at save | low (if platform ships one) | |
| 171 | + |
| 172 | +--- |
| 173 | + |
| 174 | +## 11. Choosing a strategy |
| 175 | + |
| 176 | +Use this flow: |
| 177 | + |
| 178 | +``` |
| 179 | +Does the app edit data at all? |
| 180 | +├── No → render as read-only (every input with enabled = abap_false) |
| 181 | +└── Yes |
| 182 | + ├── On modern S/4 / Steampunk, and a released, draft-enabled SAP BO |
| 183 | + │ already covers this object? |
| 184 | + │ └── Scenario 7 (Standard BO draft via EML) |
| 185 | + ├── Does your platform ship a lock manager class? |
| 186 | + │ └── Scenario 8 (Platform lock manager) |
| 187 | + ├── Need "locked by X" feedback at open? |
| 188 | + │ ├── Few users, GUI-like feel → Scenario 5 |
| 189 | + │ └── Many users → Scenario 6 |
| 190 | + └── Default high-scale stateless editing |
| 191 | + └── Scenario 4 (Enqueue + Optimistic) |
31 | 192 | ``` |
32 | 193 |
|
33 | | -A complete example is in sample `Z2UI5_CL_DEMO_APP_350`. |
| 194 | +--- |
| 195 | + |
| 196 | +## 12. Common gotchas |
| 197 | + |
| 198 | +- **Don't hold an enqueue across roundtrips without `set_session_stateful( )`.** It will be silently released the moment the HTTP response is sent. |
| 199 | +- **Always release the enqueue on every code path** — both success and error. A leaked enqueue blocks future users until session timeout. |
| 200 | +- **Soft locks need cleanup.** Without a reaper job or `onbeforeunload` release, you will accumulate stale "edited by" rows. |
| 201 | +- **Optimistic timestamps require a field that always updates.** If anyone writes to `VBAK` bypassing `AEDAT/AEZET`, your check will miss real conflicts. Prefer a real timestamp column (`UPDATE_TMSTMP`) when available. |
| 202 | +- **Statefulness is contagious.** Once `set_session_stateful( )` is on, every roundtrip costs a work process slot until you turn it off again. Always pair it with an explicit `set_session_stateful( abap_false )` on exit. |
| 203 | +- **Always include the optimistic check** in non-draft scenarios. It is the only mechanism that catches conflicts originating *outside* your app (SE16, batch, RFC, another transaction). Draft-enabled BOs (Scenario 7) handle this for you via the framework's ETag. |
| 204 | + |
| 205 | +--- |
| 206 | + |
| 207 | +## 13. Summary |
| 208 | + |
| 209 | +| If you need... | Use | |
| 210 | +|---|---| |
| 211 | +| Personal sandbox or demo where conflicts are impossible | Scenario 1 | |
| 212 | +| Quick edits, low contention, no fanciness | Scenario 2 | |
| 213 | +| Stateless edits with reliable conflict detection | Scenario 3 | |
| 214 | +| Stateless edits, production-grade default | **Scenario 4** | |
| 215 | +| GUI-like "lock on open" for internal apps | Scenario 5 | |
| 216 | +| UX feedback "locked by Alice" via your own Z table | Scenario 6 | |
| 217 | +| A released, draft-enabled SAP BO already exists — just consume it | **Scenario 7** | |
| 218 | +| Platform already ships a lock manager — use the standard | **Scenario 8** | |
34 | 219 |
|
35 | | -### Other Options |
36 | | -Stateful sessions are only available on private and on-premise systems. For other deployments, infinite backend sessions can hold locks while the UI5 app keeps talking statelessly. See [Statefulness, Locks](../../advanced/stateful.md) for the alternatives and trade-offs. |
| 220 | +There is no single "best" lock strategy — only the one that fits your scenario. On a modern S/4 / Steampunk stack, the first question is "does a released SAP BO already cover this object?" — if yes, **Scenario 7** is almost always the right answer. Otherwise start with **Scenario 4** as the safe stateless default; if your platform ships a lock manager, prefer **Scenario 8**; reach for the others when the requirements push you there. |
0 commit comments