You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: rewrite draft handling page as a beginner-friendly guide
- add plain-language 'What Is a Draft?' intro with everyday analogy
- add a draft lifecycle diagram and 'Four Building Blocks' overview
- add a minimal first-app example before the full real-world app
- add a Common Pitfalls table for beginners
- keep the full working example and reference notes intact
Draft handling lets users save unfinished work — for example a half-filled form — without writing through to the active database state. In abap2UI5 apps you drive RAP draft-enabled business objects directly through EML, just like any other entity (see also [EML](./eml.md)).
6
+
## What Is a Draft?
7
7
8
-
All examples on this page use `I_BankTP`, a draft-enabled BO that ships with S/4HANA.
8
+
Imagine a user opens a form, fills in half of it, and then gets pulled into a meeting. With a normal save, they'd have to either commit half-finished (and possibly invalid) data, or lose everything. A **draft** is the third option: a private, work-in-progress copy that is parked safely on the server until the user is ready to finalize it.
9
9
10
-
### The EML Primitives
10
+
A helpful mental model:
11
11
12
-
#### Create a Draft
13
-
Use `MODIFY ENTITIES` with `%is_draft = if_abap_behv=>mk-on` to create a new draft instance:
12
+
| Concept | Everyday analogy |
13
+
|---|---|
14
+
|**Active record**| The published document everyone can read |
15
+
|**Draft**| Your private "unsaved changes" copy of that document |
16
+
|**Activate**| Hitting *Publish* — the draft replaces the active record |
17
+
|**Discard**| Hitting *Close without saving* — the draft is thrown away |
18
+
19
+
While a draft is open, the underlying record is **locked** so nobody else can edit it at the same time — but the lock is held by SAP's RAP framework, not by your app. That means your abap2UI5 app can stay **stateless**: the user can close the browser, come back tomorrow, and resume exactly where they left off.
20
+
21
+
In abap2UI5 you drive draft-enabled RAP business objects directly through **EML** (Entity Manipulation Language), exactly like any other entity — see also [EML](./eml.md).
22
+
23
+
::: tip You don't have to build anything
24
+
On S/4HANA and the BTP ABAP Environment (Steampunk), many business objects already ship as draft-enabled BOs. All examples on this page use **`I_BankTP`**, a draft-enabled BO that ships with S/4HANA. You don't create a BO, and you don't create a draft table — SAP provides both. Your app just calls the standard BO via EML.
25
+
:::
26
+
27
+
## The Draft Lifecycle
28
+
29
+
Every draft follows the same simple lifecycle. Get this picture in your head and the rest of the page is just code for each arrow:
30
+
31
+
```
32
+
Activate
33
+
┌────────────▶ ACTIVE record updated ✔
34
+
│ (draft saved to database)
35
+
Edit ┌───────────┴──┐
36
+
ACTIVE ─────▶│ DRAFT │
37
+
record │ (user edits) │
38
+
└───────────┬──┘
39
+
│
40
+
└────────────▶ Draft thrown away ✘
41
+
Discard (active record unchanged)
42
+
```
43
+
44
+
1.**Edit** — open a draft from the active record (acquires the lock).
45
+
2.**Change** — the user types; you save their input into the draft.
46
+
3.**Finish** — either **Activate** (write the draft to the database) or **Discard** (throw it away).
47
+
48
+
## The Four Building Blocks
49
+
50
+
Everything on this page is built from just four EML operations. Read these once — the full app below is simply these four, wired to buttons.
51
+
52
+
#### 1. Open a Draft — `Edit`
53
+
Open (acquire) a draft for an existing active record. This takes the lock:
Before the full-featured version, here is the **smallest app that actually works**. It does exactly three things: read the record, let the user edit, and save with one button. Start here — once this makes sense, the advanced version is just more buttons.
127
+
128
+
```abap
129
+
CLASS z2ui5_cl_sample_draft_min DEFINITION PUBLIC.
130
+
PUBLIC SECTION.
131
+
INTERFACES z2ui5_if_app.
132
+
DATA bank_country TYPE c LENGTH 3 VALUE `DE`.
133
+
DATA bank_internal_id TYPE c LENGTH 15 VALUE `50070010`.
134
+
DATA swift_code TYPE c LENGTH 11.
135
+
DATA long_bank_name TYPE string.
136
+
PROTECTED SECTION.
137
+
DATA client TYPE REF TO z2ui5_if_client.
138
+
METHODS view_display.
139
+
PRIVATE SECTION.
140
+
ENDCLASS.
141
+
142
+
CLASS z2ui5_cl_sample_draft_min IMPLEMENTATION.
143
+
METHOD z2ui5_if_app~main.
144
+
me->client = client.
145
+
146
+
IF client->check_on_init( ).
147
+
" 1. Open a draft so we can edit
148
+
MODIFY ENTITIES OF i_banktp ENTITY Bank EXECUTE Edit
149
+
FROM VALUE #( ( %key-BankCountry = bank_country
150
+
%key-BankInternalID = bank_internal_id ) )
151
+
FAILED DATA(f1) REPORTED DATA(r1).
152
+
COMMIT ENTITIES.
153
+
154
+
" 2. Read the draft values into our fields
155
+
READ ENTITIES OF i_banktp ENTITY Bank
156
+
FIELDS ( SWIFTCode LongBankName )
157
+
WITH VALUE #( ( %key-BankCountry = bank_country
158
+
%key-BankInternalID = bank_internal_id
159
+
%is_draft = if_abap_behv=>mk-on ) )
160
+
RESULT DATA(drafts).
161
+
IF drafts IS NOT INITIAL.
162
+
swift_code = drafts[ 1 ]-SWIFTCode.
163
+
long_bank_name = drafts[ 1 ]-LongBankName.
164
+
ENDIF.
165
+
view_display( ).
166
+
167
+
ELSEIF client->check_on_event( `SAVE` ).
168
+
" 3. Push the typed values into the draft, then activate (= save)
169
+
MODIFY ENTITIES OF i_banktp ENTITY Bank
170
+
UPDATE FIELDS ( SWIFTCode LongBankName )
171
+
WITH VALUE #( ( %key-BankCountry = bank_country
172
+
%key-BankInternalID = bank_internal_id
173
+
%is_draft = if_abap_behv=>mk-on
174
+
SWIFTCode = swift_code
175
+
LongBankName = long_bank_name
176
+
%control-SWIFTCode = if_abap_behv=>mk-on
177
+
%control-LongBankName = if_abap_behv=>mk-on ) )
178
+
FAILED DATA(f2) REPORTED DATA(r2).
179
+
180
+
MODIFY ENTITIES OF i_banktp ENTITY Bank EXECUTE Activate
181
+
FROM VALUE #( ( %key-BankCountry = bank_country
182
+
%key-BankInternalID = bank_internal_id ) )
183
+
FAILED DATA(f3) REPORTED DATA(r3).
184
+
COMMIT ENTITIES.
185
+
186
+
client->message_toast_display( `Saved!` ).
187
+
ENDIF.
188
+
ENDMETHOD.
189
+
190
+
METHOD view_display.
191
+
DATA(view) = z2ui5_cl_xml_view=>factory( ).
192
+
view->shell(
193
+
)->page( `Edit Bank (minimal draft)`
194
+
)->simple_form( editable = abap_true
195
+
)->content( `form`
196
+
)->label( `Bank Name`
197
+
)->input( client->_bind_edit( long_bank_name )
198
+
)->label( `SWIFT Code`
199
+
)->input( client->_bind_edit( swift_code )
200
+
)->button(
201
+
text = `Save`
202
+
type = `Emphasized`
203
+
press = client->_event( `SAVE` ) ).
204
+
client->view_display( view->stringify( ) ).
205
+
ENDMETHOD.
206
+
ENDCLASS.
207
+
```
208
+
209
+
That's a complete, working draft app. The three numbered comments map one-to-one onto the lifecycle diagram: **open → edit → activate**.
72
210
73
-
### Tutorial — Building a Bank Edit App
211
+
::: warning Why two steps to save?
212
+
Notice that *Save* does two things: `UPDATE FIELDS` (copy the user's typed values into the draft) **then**`Activate` (promote the draft to active). The fields are two-way bound with `_bind_edit`, so on each roundtrip the user's input lives in your ABAP variables — but it is **not** in the draft yet until you push it back with `UPDATE FIELDS`. Forgetting this step is the most common beginner mistake: the activate succeeds but saves the *old* values.
213
+
:::
74
214
75
-
With the primitives in hand, the rest of this page walks step by step through a full abap2UI5 app that edits a bank record through its standard BO draft.
215
+
## The Full App — A Real-World Edit Screen
76
216
77
-
On S/4HANA or BTP ABAP Environment (Steampunk), many business objects already ship as draft-enabled BOs — `I_BankTP` is one of them. You don't build a BO and you don't create a draft table — SAP provides both. Your abap2UI5 app just calls the standard BO via EML, which sidesteps the whole lock-during-think-time problem.
217
+
The minimal app always opens a draft on startup, which isn't ideal: it locks the record the moment anyone looks at it. A production app needs a few more behaviors:
78
218
79
-
The session can stay **stateless**: the draft survives between roundtrips in SAP's draft-shadow table, and the lock is held by the BO framework as long as the draft exists. Closing the browser without activating or discarding leaves the draft for the same user to resume later — no `set_session_stateful( )`, no `ENQUEUE_*`, no custom Z table.
219
+
-**Start read-only.** Show the active record first; only lock when the user clicks *Edit*.
220
+
-**Two modes.***VIEW* (read-only, no draft) and *EDIT* (draft open, inputs editable).
221
+
-**Handle an existing draft.** What if the user (or someone else) already has a draft open for this record?
222
+
-**Confirm before discarding.** Don't silently throw away work the user typed.
223
+
224
+
The rest of this page builds that app step by step. It uses two modes:
80
225
81
-
The app uses two modes:
82
226
-**VIEW mode** — read-only display of the active database record. No lock, no draft.
83
-
-**EDIT mode** — a draft is open; inputs are editable. The BO framework holds the lock.
227
+
-**EDIT mode** — a draft is open; inputs are editable. The RAP framework holds the lock.
84
228
85
-
Toggling between modes drives the entire draft lifecycle (acquire, resume, save, activate, discard).
229
+
Toggling between the modes drives the entire draft lifecycle (acquire, resume, save, activate, discard).
86
230
87
231
#### 1. App Startup — Always Begin in View Mode
88
-
On `on_init` the app reads the active record and renders it read-only. No draft is touched yet, so other users can still edit the same record.
232
+
On `on_init` the app reads the **active** record and renders it read-only. No draft is touched yet, so other users can still edit the same record.
89
233
```abap
90
234
METHOD on_init.
91
235
mode = `VIEW`.
@@ -109,7 +253,7 @@ METHOD read_active.
109
253
ENDIF.
110
254
ENDMETHOD.
111
255
```
112
-
`%is_draft = if_abap_behv=>mk-off` makes the read return the active row, not any open draft.
256
+
`%is_draft = if_abap_behv=>mk-off` makes the read return the **active** row, not any open draft.
113
257
114
258
#### 2. Entering Edit Mode — Check for an Existing Draft
115
259
When the user clicks **Switch to Edit Mode**, the app first looks in the BO's draft-shadow table (here `cabnk_bank_d`) joined with `sdraft_admin` to find out whether a draft already exists for this key — and who owns it.
@@ -154,7 +298,9 @@ Three branches:
154
298
-**same user** — show a popup so the user can resume or throw the draft away,
155
299
-**other user** — refuse and stay in VIEW with an explanatory status text.
156
300
157
-
The shadow-table name (`cabnk_bank_d`) is BO-specific; check `Draft Table` in the behavior definition for your own BO.
301
+
::: tip
302
+
The shadow-table name (`cabnk_bank_d`) is BO-specific. To find it for your own BO, check the `draft table` keyword in its behavior definition.
303
+
:::
158
304
159
305
#### 3. Acquiring a Fresh Draft
160
306
A new draft is created by the `Edit` action. `%param-preserve_changes = abap_true` tells RAP to keep any existing draft instead of overwriting it — defensive even though we already checked.
@@ -180,7 +326,7 @@ METHOD draft_acquire.
180
326
draft_open = abap_true.
181
327
ENDMETHOD.
182
328
```
183
-
After a successful `Edit`, the lock is held by the BO framework and a row exists in the draft-shadow table. `draft_open = abap_true` switches the view's inputs from read-only to editable.
329
+
After a successful `Edit`, the lock is held by the RAP framework and a row exists in the draft-shadow table. `draft_open = abap_true` switches the view's inputs from read-only to editable.
184
330
185
331
#### 4. Resuming an Existing Draft
186
332
If the user picks **Resume Draft**, the `Resume` action re-takes the lock on the existing draft without overwriting its contents.
@@ -805,6 +951,20 @@ ENDCLASS.
805
951
```
806
952
:::
807
953
954
+
## Common Pitfalls
955
+
956
+
A quick checklist of the mistakes beginners hit most often:
957
+
958
+
| Symptom | Cause | Fix |
959
+
|---|---|---|
960
+
| Activate saves the *old* values | Typed values were never pushed into the draft | Call `UPDATE FIELDS`**before**`Activate` (step 6) |
961
+
| Nothing is persisted at all | Missing `COMMIT ENTITIES`| EML stays in the buffer until you commit |
962
+
| Read returns the active record, not the draft | Wrong `%is_draft` flag | Use `mk-on` to read the draft, `mk-off` for the active record |
963
+
| "Record is locked" errors | A leftover draft from a previous session |`Resume` or `Discard` the existing draft (step 2) |
964
+
| Changes silently lost on exit | No save before leaving edit mode | Save (or prompt to keep) before switching back to VIEW |
965
+
966
+
For the full story on inspecting `FAILED` / `REPORTED` after EML calls, see the **Failure Handling** section in [EML](./eml.md).
967
+
808
968
::: tip
809
969
The field names (`BankCountry`, `BankInternalID`, `SWIFTCode`, `LongBankName`) and the draft-shadow table (`cabnk_bank_d`) match the released `I_BankTP` on current S/4HANA — on other releases the BO name, fields, or shadow table may differ. If no standard BO covers your object, define your own draft-enabled RAP BO (with its own `draft table z…_d`, `lock master`, etc.) and consume it the same way; see the [SAP RAP draft documentation](https://help.sap.com/docs/abap-cloud/abap-rap/draft). If you need locks for non-draft objects, see [Locks](../expert_more/lock.md).
0 commit comments