@linkup-ai/abap-ai 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +384 -0
- package/dist/adt-client.js +364 -0
- package/dist/cli/activate.js +113 -0
- package/dist/cli/init.js +333 -0
- package/dist/cli/remove.js +80 -0
- package/dist/cli/status.js +229 -0
- package/dist/cli/systems.js +68 -0
- package/dist/cli.js +81 -0
- package/dist/index.js +1318 -0
- package/dist/knowledge/abap/abap-dictionary.md +199 -0
- package/dist/knowledge/abap/abap-sql.md +296 -0
- package/dist/knowledge/abap/amdp.md +273 -0
- package/dist/knowledge/abap/clean-code.md +293 -0
- package/dist/knowledge/abap/cloud-background-processing.md +250 -0
- package/dist/knowledge/abap/cloud-communication.md +265 -0
- package/dist/knowledge/abap/cloud-development.md +176 -0
- package/dist/knowledge/abap/cloud-extensibility.md +252 -0
- package/dist/knowledge/abap/cloud-released-apis.md +261 -0
- package/dist/knowledge/abap/constructor-expressions.md +289 -0
- package/dist/knowledge/abap/enhancements.md +232 -0
- package/dist/knowledge/abap/exceptions.md +271 -0
- package/dist/knowledge/abap/internal-tables.md +205 -0
- package/dist/knowledge/abap/object-orientation.md +298 -0
- package/dist/knowledge/abap/performance.md +216 -0
- package/dist/knowledge/abap/rap-abstract-entities.md +206 -0
- package/dist/knowledge/abap/rap-business-events.md +216 -0
- package/dist/knowledge/abap/rap-draft.md +191 -0
- package/dist/knowledge/abap/rap-eml.md +453 -0
- package/dist/knowledge/abap/rap-end-to-end.md +486 -0
- package/dist/knowledge/abap/rap-feature-control.md +185 -0
- package/dist/knowledge/abap/rap-numbering.md +280 -0
- package/dist/knowledge/abap/rap-service-exposure.md +163 -0
- package/dist/knowledge/abap/rap-unmanaged.md +468 -0
- package/dist/knowledge/abap/string-processing.md +180 -0
- package/dist/knowledge/abap/unit-testing.md +303 -0
- package/dist/knowledge/abap-cds/access-control.md +241 -0
- package/dist/knowledge/abap-cds/annotations.md +331 -0
- package/dist/knowledge/abap-cds/associations.md +254 -0
- package/dist/knowledge/abap-cds/expressions.md +230 -0
- package/dist/knowledge/abap-cds/functions.md +245 -0
- package/dist/knowledge/abap-cds/metadata-extensions.md +294 -0
- package/dist/knowledge/cap/authentication.md +278 -0
- package/dist/knowledge/cap/cdl-syntax.md +247 -0
- package/dist/knowledge/cap/cql-queries.md +266 -0
- package/dist/knowledge/cap/deployment.md +343 -0
- package/dist/knowledge/cap/event-handlers.md +287 -0
- package/dist/knowledge/cap/fiori-integration.md +303 -0
- package/dist/knowledge/cap/service-definitions.md +287 -0
- package/dist/knowledge/fiori/annotations.md +347 -0
- package/dist/knowledge/fiori/deployment.md +340 -0
- package/dist/knowledge/fiori/fiori-elements.md +332 -0
- package/dist/knowledge/fiori/fiori-side-effects.md +107 -0
- package/dist/knowledge/fiori/fiori-valuelist.md +144 -0
- package/dist/knowledge/fiori/ui5-controllers.md +358 -0
- package/dist/knowledge/fiori/ui5-data-binding.md +311 -0
- package/dist/knowledge/fiori/ui5-fragments-dialogs.md +330 -0
- package/dist/knowledge/fiori/ui5-manifest.md +411 -0
- package/dist/knowledge/fiori/ui5-routing.md +303 -0
- package/dist/knowledge/fiori/ui5-xml-views.md +294 -0
- package/dist/logger.js +114 -0
- package/dist/system-profile.js +207 -0
- package/dist/tools/abap-doc.js +72 -0
- package/dist/tools/abapgit.js +161 -0
- package/dist/tools/activate.js +68 -0
- package/dist/tools/atc-check.js +117 -0
- package/dist/tools/auth-object.js +56 -0
- package/dist/tools/breakpoints.js +76 -0
- package/dist/tools/call-hierarchy.js +84 -0
- package/dist/tools/cds-annotations.js +98 -0
- package/dist/tools/cds-dependencies.js +65 -0
- package/dist/tools/check.js +47 -0
- package/dist/tools/code-completion.js +70 -0
- package/dist/tools/code-coverage.js +111 -0
- package/dist/tools/create-amdp.js +111 -0
- package/dist/tools/create-dcl.js +81 -0
- package/dist/tools/create-transport.js +38 -0
- package/dist/tools/create.js +285 -0
- package/dist/tools/data-preview.js +37 -0
- package/dist/tools/delete.js +45 -0
- package/dist/tools/deploy-bsp.js +298 -0
- package/dist/tools/discovery.js +59 -0
- package/dist/tools/element-info.js +93 -0
- package/dist/tools/enhancements.js +186 -0
- package/dist/tools/extract-method.js +44 -0
- package/dist/tools/function-group.js +59 -0
- package/dist/tools/knowledge.js +275 -0
- package/dist/tools/lock-object.js +75 -0
- package/dist/tools/message-class.js +67 -0
- package/dist/tools/navigate.js +80 -0
- package/dist/tools/number-range.js +57 -0
- package/dist/tools/object-documentation.js +43 -0
- package/dist/tools/object-structure.js +78 -0
- package/dist/tools/object-versions.js +57 -0
- package/dist/tools/package-contents.js +60 -0
- package/dist/tools/pretty-printer.js +35 -0
- package/dist/tools/publish-binding.js +49 -0
- package/dist/tools/quick-fix.js +69 -0
- package/dist/tools/read.js +167 -0
- package/dist/tools/refactor-rename.js +60 -0
- package/dist/tools/release-transport.js +24 -0
- package/dist/tools/released-apis.js +51 -0
- package/dist/tools/repository-tree.js +90 -0
- package/dist/tools/scaffold-rap.js +642 -0
- package/dist/tools/search.js +73 -0
- package/dist/tools/shared/data-format.js +101 -0
- package/dist/tools/sql-console.js +17 -0
- package/dist/tools/system-info.js +270 -0
- package/dist/tools/traces.js +66 -0
- package/dist/tools/transport-contents.js +83 -0
- package/dist/tools/transports.js +67 -0
- package/dist/tools/unit-test.js +135 -0
- package/dist/tools/where-used.js +59 -0
- package/dist/tools/write.js +101 -0
- package/package.json +49 -0
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
# RAP Unmanaged BO — wrapping BAPIs, FMs, legacy code
|
|
2
|
+
|
|
3
|
+
## When to Use
|
|
4
|
+
|
|
5
|
+
- Wrapping existing BAPIs/FMs as RAP BO (legacy modernization)
|
|
6
|
+
- Complex save logic that managed BO cannot handle
|
|
7
|
+
- Custom persistence (non-standard DB operations)
|
|
8
|
+
- Integration with external systems during save
|
|
9
|
+
|
|
10
|
+
## BDEF — Unmanaged
|
|
11
|
+
|
|
12
|
+
```abap
|
|
13
|
+
unmanaged implementation in class ZBP_I_ORDER unique;
|
|
14
|
+
strict ( 2 );
|
|
15
|
+
|
|
16
|
+
define behavior for ZI_Order alias Order
|
|
17
|
+
lock master
|
|
18
|
+
authorization master ( instance )
|
|
19
|
+
etag master LocalLastChangedAt
|
|
20
|
+
{
|
|
21
|
+
create;
|
|
22
|
+
update;
|
|
23
|
+
delete;
|
|
24
|
+
|
|
25
|
+
field ( readonly ) OrderID;
|
|
26
|
+
field ( readonly ) CreatedBy, CreatedAt, LastChangedBy, LastChangedAt, LocalLastChangedAt;
|
|
27
|
+
field ( mandatory ) CustomerID;
|
|
28
|
+
|
|
29
|
+
action confirm result [1] $self;
|
|
30
|
+
|
|
31
|
+
validation validateCustomer on save { create; update; field CustomerID; }
|
|
32
|
+
determination setOrderID on modify { create; }
|
|
33
|
+
|
|
34
|
+
mapping for ztab_order corresponding;
|
|
35
|
+
association _Item { create; }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
define behavior for ZI_OrderItem alias Item
|
|
39
|
+
lock dependent by _Order
|
|
40
|
+
authorization dependent by _Order
|
|
41
|
+
{
|
|
42
|
+
update;
|
|
43
|
+
delete;
|
|
44
|
+
|
|
45
|
+
field ( readonly ) OrderID, ItemID;
|
|
46
|
+
|
|
47
|
+
mapping for ztab_order_item corresponding;
|
|
48
|
+
association _Order;
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Key difference from managed: `unmanaged` keyword. Developer must implement ALL CRUD operations.
|
|
53
|
+
|
|
54
|
+
## Handler Class — CREATE
|
|
55
|
+
|
|
56
|
+
```abap
|
|
57
|
+
CLASS lhc_order DEFINITION INHERITING FROM cl_abap_behavior_handler.
|
|
58
|
+
PRIVATE SECTION.
|
|
59
|
+
METHODS create FOR MODIFY IMPORTING entities FOR CREATE Order.
|
|
60
|
+
METHODS update FOR MODIFY IMPORTING entities FOR UPDATE Order.
|
|
61
|
+
METHODS delete FOR MODIFY IMPORTING keys FOR DELETE Order.
|
|
62
|
+
METHODS read FOR READ IMPORTING keys FOR READ Order RESULT result.
|
|
63
|
+
METHODS lock FOR LOCK IMPORTING keys FOR LOCK Order.
|
|
64
|
+
METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
|
|
65
|
+
IMPORTING keys REQUEST requested_authorizations FOR Order RESULT result.
|
|
66
|
+
ENDCLASS.
|
|
67
|
+
|
|
68
|
+
CLASS lhc_order IMPLEMENTATION.
|
|
69
|
+
|
|
70
|
+
METHOD create.
|
|
71
|
+
DATA lt_buffer TYPE STANDARD TABLE OF ztab_order.
|
|
72
|
+
|
|
73
|
+
LOOP AT entities ASSIGNING FIELD-SYMBOL(<entity>).
|
|
74
|
+
" Generate key
|
|
75
|
+
DATA(lv_id) = get_next_id( ).
|
|
76
|
+
|
|
77
|
+
" Prepare buffer record
|
|
78
|
+
DATA(ls_order) = CORRESPONDING ztab_order( <entity> MAPPING FROM ENTITY ).
|
|
79
|
+
ls_order-order_id = lv_id.
|
|
80
|
+
ls_order-created_by = cl_abap_context_info=>get_user_technical_name( ).
|
|
81
|
+
GET TIME STAMP FIELD ls_order-created_at.
|
|
82
|
+
ls_order-last_changed_by = ls_order-created_by.
|
|
83
|
+
GET TIME STAMP FIELD ls_order-last_changed_at.
|
|
84
|
+
GET TIME STAMP FIELD ls_order-local_last_changed_at.
|
|
85
|
+
|
|
86
|
+
APPEND ls_order TO lt_buffer.
|
|
87
|
+
|
|
88
|
+
" Return mapped key
|
|
89
|
+
INSERT VALUE #(
|
|
90
|
+
%cid = <entity>-%cid
|
|
91
|
+
order_id = lv_id
|
|
92
|
+
) INTO TABLE mapped-order.
|
|
93
|
+
ENDLOOP.
|
|
94
|
+
|
|
95
|
+
" Buffer — actual DB insert happens in saver class
|
|
96
|
+
INSERT ztab_order FROM TABLE @lt_buffer.
|
|
97
|
+
ENDMETHOD.
|
|
98
|
+
|
|
99
|
+
METHOD update.
|
|
100
|
+
DATA lt_buffer TYPE STANDARD TABLE OF ztab_order.
|
|
101
|
+
|
|
102
|
+
LOOP AT entities ASSIGNING FIELD-SYMBOL(<entity>).
|
|
103
|
+
" Read current data
|
|
104
|
+
SELECT SINGLE * FROM ztab_order
|
|
105
|
+
WHERE order_id = @<entity>-OrderID
|
|
106
|
+
INTO @DATA(ls_current).
|
|
107
|
+
|
|
108
|
+
" Apply only changed fields (using %control)
|
|
109
|
+
IF <entity>-%control-CustomerID = if_abap_behv=>mk-on.
|
|
110
|
+
ls_current-customer_id = <entity>-CustomerID.
|
|
111
|
+
ENDIF.
|
|
112
|
+
IF <entity>-%control-Description = if_abap_behv=>mk-on.
|
|
113
|
+
ls_current-description = <entity>-Description.
|
|
114
|
+
ENDIF.
|
|
115
|
+
IF <entity>-%control-Status = if_abap_behv=>mk-on.
|
|
116
|
+
ls_current-status = <entity>-Status.
|
|
117
|
+
ENDIF.
|
|
118
|
+
|
|
119
|
+
ls_current-last_changed_by = cl_abap_context_info=>get_user_technical_name( ).
|
|
120
|
+
GET TIME STAMP FIELD ls_current-last_changed_at.
|
|
121
|
+
GET TIME STAMP FIELD ls_current-local_last_changed_at.
|
|
122
|
+
|
|
123
|
+
UPDATE ztab_order FROM @ls_current.
|
|
124
|
+
ENDLOOP.
|
|
125
|
+
ENDMETHOD.
|
|
126
|
+
|
|
127
|
+
METHOD delete.
|
|
128
|
+
LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>).
|
|
129
|
+
DELETE FROM ztab_order WHERE order_id = @<key>-OrderID.
|
|
130
|
+
DELETE FROM ztab_order_item WHERE order_id = @<key>-OrderID.
|
|
131
|
+
ENDLOOP.
|
|
132
|
+
ENDMETHOD.
|
|
133
|
+
|
|
134
|
+
METHOD read.
|
|
135
|
+
SELECT * FROM ztab_order
|
|
136
|
+
FOR ALL ENTRIES IN @keys
|
|
137
|
+
WHERE order_id = @keys-OrderID
|
|
138
|
+
INTO TABLE @DATA(lt_orders).
|
|
139
|
+
|
|
140
|
+
result = CORRESPONDING #( lt_orders MAPPING TO ENTITY ).
|
|
141
|
+
ENDMETHOD.
|
|
142
|
+
|
|
143
|
+
METHOD lock.
|
|
144
|
+
" Enqueue lock
|
|
145
|
+
LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>).
|
|
146
|
+
TRY.
|
|
147
|
+
" Call lock object
|
|
148
|
+
NEW zcl_order_lock( )->lock( iv_order_id = <key>-OrderID ).
|
|
149
|
+
CATCH zcx_lock_error.
|
|
150
|
+
INSERT VALUE #( %tky = <key>-%tky ) INTO TABLE failed-order.
|
|
151
|
+
INSERT VALUE #( %tky = <key>-%tky
|
|
152
|
+
%msg = new_message_with_text(
|
|
153
|
+
severity = if_abap_behv_message=>severity-error
|
|
154
|
+
text = |Order { <key>-OrderID } is locked by another user| ) )
|
|
155
|
+
INTO TABLE reported-order.
|
|
156
|
+
ENDTRY.
|
|
157
|
+
ENDLOOP.
|
|
158
|
+
ENDMETHOD.
|
|
159
|
+
|
|
160
|
+
METHOD get_instance_authorizations.
|
|
161
|
+
" Check authorization per instance
|
|
162
|
+
LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>).
|
|
163
|
+
INSERT VALUE #(
|
|
164
|
+
%tky = <key>-%tky
|
|
165
|
+
%create = if_abap_behv=>auth-allowed
|
|
166
|
+
%update = if_abap_behv=>auth-allowed
|
|
167
|
+
%delete = if_abap_behv=>auth-allowed
|
|
168
|
+
) INTO TABLE result.
|
|
169
|
+
ENDLOOP.
|
|
170
|
+
ENDMETHOD.
|
|
171
|
+
|
|
172
|
+
ENDCLASS.
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Saver Class — Persist Changes
|
|
176
|
+
|
|
177
|
+
```abap
|
|
178
|
+
CLASS lsc_order DEFINITION INHERITING FROM cl_abap_behavior_saver.
|
|
179
|
+
PROTECTED SECTION.
|
|
180
|
+
METHODS finalize REDEFINITION.
|
|
181
|
+
METHODS check_before_save REDEFINITION.
|
|
182
|
+
METHODS save REDEFINITION.
|
|
183
|
+
METHODS cleanup REDEFINITION.
|
|
184
|
+
METHODS cleanup_finalize REDEFINITION.
|
|
185
|
+
ENDCLASS.
|
|
186
|
+
|
|
187
|
+
CLASS lsc_order IMPLEMENTATION.
|
|
188
|
+
METHOD finalize.
|
|
189
|
+
" Final calculations before save (optional)
|
|
190
|
+
ENDMETHOD.
|
|
191
|
+
|
|
192
|
+
METHOD check_before_save.
|
|
193
|
+
" Cross-entity consistency checks (optional)
|
|
194
|
+
" Set raised_for_messages = abap_true if there are errors to prevent save
|
|
195
|
+
ENDMETHOD.
|
|
196
|
+
|
|
197
|
+
METHOD save.
|
|
198
|
+
" Persist transactional buffer to database
|
|
199
|
+
" In simple cases, DB operations are already in handler methods
|
|
200
|
+
" In complex cases (BAPI wrapping), call BAPI here:
|
|
201
|
+
|
|
202
|
+
" Example: BAPI wrapping
|
|
203
|
+
" CALL FUNCTION 'BAPI_SALESORDER_CREATEFROMDAT2'
|
|
204
|
+
" EXPORTING ...
|
|
205
|
+
" IMPORTING ...
|
|
206
|
+
" TABLES ...
|
|
207
|
+
" CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'.
|
|
208
|
+
ENDMETHOD.
|
|
209
|
+
|
|
210
|
+
METHOD cleanup.
|
|
211
|
+
" Clear transactional buffer (called after save or on rollback)
|
|
212
|
+
ENDMETHOD.
|
|
213
|
+
|
|
214
|
+
METHOD cleanup_finalize.
|
|
215
|
+
" Cleanup after finalize phase (optional)
|
|
216
|
+
ENDMETHOD.
|
|
217
|
+
ENDCLASS.
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Save Sequence
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
1. finalize — final calculations
|
|
224
|
+
2. check_before_save — cross-entity checks
|
|
225
|
+
3. adjust_numbers — late numbering (if used)
|
|
226
|
+
4. save — persist to DB / call BAPI
|
|
227
|
+
5. cleanup — clear buffer
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Wrapping BAPI — Complete Pattern
|
|
231
|
+
|
|
232
|
+
```abap
|
|
233
|
+
" BDEF:
|
|
234
|
+
unmanaged implementation in class ZBP_I_BAPI_ORDER unique;
|
|
235
|
+
|
|
236
|
+
define behavior for ZI_BapiOrder alias Order
|
|
237
|
+
lock master
|
|
238
|
+
authorization master ( global )
|
|
239
|
+
{
|
|
240
|
+
create;
|
|
241
|
+
update;
|
|
242
|
+
field ( readonly ) OrderID;
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Buffer Pattern
|
|
247
|
+
|
|
248
|
+
```abap
|
|
249
|
+
" Global buffer in class (shared between handler and saver)
|
|
250
|
+
CLASS zbp_i_bapi_order DEFINITION PUBLIC ABSTRACT FINAL
|
|
251
|
+
FOR BEHAVIOR OF ZI_BapiOrder.
|
|
252
|
+
PUBLIC SECTION.
|
|
253
|
+
CLASS-DATA: gt_create TYPE STANDARD TABLE OF zs_bapi_order_create,
|
|
254
|
+
gt_update TYPE STANDARD TABLE OF zs_bapi_order_update.
|
|
255
|
+
ENDCLASS.
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Handler — Buffer Changes
|
|
259
|
+
|
|
260
|
+
```abap
|
|
261
|
+
METHOD create.
|
|
262
|
+
LOOP AT entities ASSIGNING FIELD-SYMBOL(<entity>).
|
|
263
|
+
DATA(ls_create) = VALUE zs_bapi_order_create(
|
|
264
|
+
cid = <entity>-%cid
|
|
265
|
+
customer_id = <entity>-CustomerID
|
|
266
|
+
material = <entity>-Material
|
|
267
|
+
quantity = <entity>-Quantity ).
|
|
268
|
+
APPEND ls_create TO zbp_i_bapi_order=>gt_create.
|
|
269
|
+
|
|
270
|
+
" Return mapped with temporary CID (real key assigned by BAPI)
|
|
271
|
+
INSERT VALUE #( %cid = <entity>-%cid ) INTO TABLE mapped-order.
|
|
272
|
+
ENDLOOP.
|
|
273
|
+
ENDMETHOD.
|
|
274
|
+
|
|
275
|
+
METHOD update.
|
|
276
|
+
LOOP AT entities ASSIGNING FIELD-SYMBOL(<entity>).
|
|
277
|
+
DATA(ls_update) = VALUE zs_bapi_order_update(
|
|
278
|
+
order_id = <entity>-OrderID ).
|
|
279
|
+
IF <entity>-%control-Quantity = if_abap_behv=>mk-on.
|
|
280
|
+
ls_update-quantity = <entity>-Quantity.
|
|
281
|
+
ls_update-quantity_changed = abap_true.
|
|
282
|
+
ENDIF.
|
|
283
|
+
APPEND ls_update TO zbp_i_bapi_order=>gt_update.
|
|
284
|
+
ENDLOOP.
|
|
285
|
+
ENDMETHOD.
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Saver — Call BAPI
|
|
289
|
+
|
|
290
|
+
```abap
|
|
291
|
+
METHOD save.
|
|
292
|
+
" Process creates
|
|
293
|
+
LOOP AT zbp_i_bapi_order=>gt_create INTO DATA(ls_create).
|
|
294
|
+
DATA ls_header TYPE bapisdhead.
|
|
295
|
+
DATA lt_items TYPE TABLE OF bapisditem.
|
|
296
|
+
DATA lt_return TYPE TABLE OF bapiret2.
|
|
297
|
+
|
|
298
|
+
ls_header-doc_type = 'TA'.
|
|
299
|
+
ls_header-sales_org = '1000'.
|
|
300
|
+
ls_header-distr_chan = '10'.
|
|
301
|
+
ls_header-division = '00'.
|
|
302
|
+
ls_header-sold_to = ls_create-customer_id.
|
|
303
|
+
|
|
304
|
+
APPEND VALUE #( material = ls_create-material
|
|
305
|
+
target_qty = ls_create-quantity )
|
|
306
|
+
TO lt_items.
|
|
307
|
+
|
|
308
|
+
CALL FUNCTION 'BAPI_SALESORDER_CREATEFROMDAT2'
|
|
309
|
+
EXPORTING order_header_in = ls_header
|
|
310
|
+
IMPORTING salesdocument = DATA(lv_vbeln)
|
|
311
|
+
TABLES order_items_in = lt_items
|
|
312
|
+
return = lt_return.
|
|
313
|
+
|
|
314
|
+
IF lv_vbeln IS NOT INITIAL.
|
|
315
|
+
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
|
|
316
|
+
EXPORTING wait = abap_true.
|
|
317
|
+
ELSE.
|
|
318
|
+
" Collect errors from lt_return
|
|
319
|
+
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
|
|
320
|
+
ENDIF.
|
|
321
|
+
ENDLOOP.
|
|
322
|
+
|
|
323
|
+
" Process updates similarly...
|
|
324
|
+
|
|
325
|
+
" Clear buffers
|
|
326
|
+
CLEAR: zbp_i_bapi_order=>gt_create,
|
|
327
|
+
zbp_i_bapi_order=>gt_update.
|
|
328
|
+
ENDMETHOD.
|
|
329
|
+
|
|
330
|
+
METHOD cleanup.
|
|
331
|
+
CLEAR: zbp_i_bapi_order=>gt_create,
|
|
332
|
+
zbp_i_bapi_order=>gt_update.
|
|
333
|
+
ENDMETHOD.
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Unmanaged with Additional Save (Hybrid)
|
|
337
|
+
|
|
338
|
+
```abap
|
|
339
|
+
" Managed BO with custom save logic for specific cases
|
|
340
|
+
managed implementation in class ZBP_I_ORDER unique;
|
|
341
|
+
with additional save;
|
|
342
|
+
|
|
343
|
+
define behavior for ZI_Order alias Order
|
|
344
|
+
persistent table ztab_order
|
|
345
|
+
lock master
|
|
346
|
+
{
|
|
347
|
+
create; update; delete;
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
```abap
|
|
352
|
+
" Saver class — additional save runs AFTER managed save
|
|
353
|
+
CLASS lsc_order DEFINITION INHERITING FROM cl_abap_behavior_saver.
|
|
354
|
+
PROTECTED SECTION.
|
|
355
|
+
METHODS save_modified REDEFINITION.
|
|
356
|
+
ENDCLASS.
|
|
357
|
+
|
|
358
|
+
CLASS lsc_order IMPLEMENTATION.
|
|
359
|
+
METHOD save_modified.
|
|
360
|
+
" Runs after standard managed save
|
|
361
|
+
" Use for: calling external systems, sending emails, custom logging
|
|
362
|
+
|
|
363
|
+
" Access what was saved:
|
|
364
|
+
IF create-order IS NOT INITIAL.
|
|
365
|
+
" New orders were created — notify external system
|
|
366
|
+
LOOP AT create-order INTO DATA(ls_created).
|
|
367
|
+
zcl_external_notifier=>notify_creation( ls_created-OrderID ).
|
|
368
|
+
ENDLOOP.
|
|
369
|
+
ENDIF.
|
|
370
|
+
|
|
371
|
+
IF update-order IS NOT INITIAL.
|
|
372
|
+
" Orders were updated
|
|
373
|
+
ENDIF.
|
|
374
|
+
|
|
375
|
+
IF delete-order IS NOT INITIAL.
|
|
376
|
+
" Orders were deleted
|
|
377
|
+
ENDIF.
|
|
378
|
+
ENDMETHOD.
|
|
379
|
+
ENDCLASS.
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Unmanaged Query (Custom CDS with IF_RAP_QUERY_PROVIDER)
|
|
383
|
+
|
|
384
|
+
```abap
|
|
385
|
+
" For CDS custom entities that don't map to a single DB table
|
|
386
|
+
@EndUserText.label: 'Custom Query Entity'
|
|
387
|
+
@ObjectModel.query.implementedBy: 'ABAP:ZCL_ORDER_QUERY'
|
|
388
|
+
define custom entity ZCE_OrderSummary
|
|
389
|
+
{
|
|
390
|
+
key OrderID : abap.numc(10);
|
|
391
|
+
CustomerName : abap.char(40);
|
|
392
|
+
TotalAmount : abap.curr(17,2);
|
|
393
|
+
Currency : abap.cuky(5);
|
|
394
|
+
ItemCount : abap.int4;
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
```abap
|
|
399
|
+
CLASS zcl_order_query DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
|
400
|
+
PUBLIC SECTION.
|
|
401
|
+
INTERFACES if_rap_query_provider.
|
|
402
|
+
ENDCLASS.
|
|
403
|
+
|
|
404
|
+
CLASS zcl_order_query IMPLEMENTATION.
|
|
405
|
+
METHOD if_rap_query_provider~select.
|
|
406
|
+
" Check what was requested
|
|
407
|
+
DATA(lv_top) = io_request->get_paging( )->get_page_size( ).
|
|
408
|
+
DATA(lv_skip) = io_request->get_paging( )->get_offset( ).
|
|
409
|
+
DATA(lt_filter) = io_request->get_filter( )->get_as_ranges( ).
|
|
410
|
+
DATA(lt_sort) = io_request->get_sort_elements( ).
|
|
411
|
+
DATA(lt_requested) = io_request->get_requested_elements( ).
|
|
412
|
+
|
|
413
|
+
" Build query based on request
|
|
414
|
+
SELECT o~order_id, c~name AS customer_name,
|
|
415
|
+
SUM( i~amount ) AS total_amount,
|
|
416
|
+
o~currency, COUNT(*) AS item_count
|
|
417
|
+
FROM ztab_order AS o
|
|
418
|
+
INNER JOIN ztab_customer AS c ON o~customer_id = c~customer_id
|
|
419
|
+
INNER JOIN ztab_order_item AS i ON o~order_id = i~order_id
|
|
420
|
+
GROUP BY o~order_id, c~name, o~currency
|
|
421
|
+
INTO TABLE @DATA(lt_result)
|
|
422
|
+
UP TO @lv_top ROWS
|
|
423
|
+
OFFSET @lv_skip.
|
|
424
|
+
|
|
425
|
+
" Return data
|
|
426
|
+
io_response->set_data( lt_result ).
|
|
427
|
+
|
|
428
|
+
" Return total count if requested
|
|
429
|
+
IF io_request->is_total_numb_of_rec_requested( ).
|
|
430
|
+
io_response->set_total_number_of_records( lines( lt_result ) ).
|
|
431
|
+
ENDIF.
|
|
432
|
+
ENDMETHOD.
|
|
433
|
+
ENDCLASS.
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
## Managed vs Unmanaged vs Unmanaged Query
|
|
437
|
+
|
|
438
|
+
| Aspect | Managed | Unmanaged | Unmanaged Query |
|
|
439
|
+
|--------|---------|-----------|-----------------|
|
|
440
|
+
| BDEF keyword | `managed` | `unmanaged` | (custom entity) |
|
|
441
|
+
| CRUD | Framework auto | Developer implements | READ only |
|
|
442
|
+
| DB persistence | `persistent table` | Developer manages | No persistence |
|
|
443
|
+
| Create/Update/Delete | Automatic | Handler methods | Not available |
|
|
444
|
+
| Use case | New development | Legacy wrapping | Complex read aggregations |
|
|
445
|
+
| Draft support | Full | Manual | Not available |
|
|
446
|
+
| Complexity | Low | High | Medium |
|
|
447
|
+
|
|
448
|
+
## Rules
|
|
449
|
+
- Unmanaged: developer MUST implement create, update, delete, read, lock methods
|
|
450
|
+
- Use `CORRESPONDING ... MAPPING FROM ENTITY` / `TO ENTITY` for BDEF↔DB mapping
|
|
451
|
+
- Buffer pattern: store changes in handler, persist in saver
|
|
452
|
+
- BAPI wrapping: BAPI calls go in saver `save` method (not handler)
|
|
453
|
+
- BAPI wrapping: always BAPI_TRANSACTION_COMMIT after successful BAPI call
|
|
454
|
+
- Additional save (`with additional save`): use for managed BO + external system calls
|
|
455
|
+
- Custom entity + `if_rap_query_provider`: for read-only aggregated data
|
|
456
|
+
- `%control` flags: always check before applying field updates
|
|
457
|
+
|
|
458
|
+
## Anti-Patterns
|
|
459
|
+
| Anti-Pattern | Correct |
|
|
460
|
+
|---|---|
|
|
461
|
+
| DB operations directly in handler without buffer | Buffer in handler, persist in saver |
|
|
462
|
+
| Missing BAPI_TRANSACTION_COMMIT after BAPI | Always COMMIT (or ROLLBACK on error) |
|
|
463
|
+
| BAPI call in handler (before save sequence) | BAPI in saver `save` method |
|
|
464
|
+
| Ignoring `%control` in update | Check each `%control-Field = if_abap_behv=>mk-on` |
|
|
465
|
+
| Missing lock implementation | Implement FOR LOCK method (or use lock object) |
|
|
466
|
+
| Unmanaged when managed would work | Use managed for new development |
|
|
467
|
+
| Static buffer without cleanup | Always CLEAR buffer in `cleanup` method |
|
|
468
|
+
| Custom entity for simple CRUD | Use managed/unmanaged BO instead |
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# String Processing — templates, concatenation, regex, conversion
|
|
2
|
+
|
|
3
|
+
## String Templates (Preferred)
|
|
4
|
+
|
|
5
|
+
```abap
|
|
6
|
+
" Basic interpolation
|
|
7
|
+
DATA(lv_msg) = |Order { lv_id } created for { lv_customer }|.
|
|
8
|
+
|
|
9
|
+
" Expressions inside { }
|
|
10
|
+
DATA(lv_calc) = |Total: { lv_qty * lv_price }|.
|
|
11
|
+
|
|
12
|
+
" Formatting: WIDTH, ALIGN, PAD
|
|
13
|
+
DATA(lv_padded) = |{ lv_id WIDTH = 10 ALIGN = RIGHT PAD = '0' }|. " '0000001234'
|
|
14
|
+
|
|
15
|
+
" Formatting: ALPHA conversion
|
|
16
|
+
DATA(lv_alpha_in) = |{ '1234' ALPHA = IN WIDTH = 10 }|. " '0000001234'
|
|
17
|
+
DATA(lv_alpha_out) = |{ '0000001234' ALPHA = OUT }|. " '1234'
|
|
18
|
+
|
|
19
|
+
" Formatting: DATE
|
|
20
|
+
DATA(lv_date) = |{ sy-datum DATE = USER }|. " '15.03.2026' (user format)
|
|
21
|
+
DATA(lv_iso) = |{ sy-datum DATE = ISO }|. " '2026-03-15'
|
|
22
|
+
DATA(lv_raw) = |{ sy-datum DATE = RAW }|. " '20260315'
|
|
23
|
+
|
|
24
|
+
" Formatting: TIME
|
|
25
|
+
DATA(lv_time) = |{ sy-uzeit TIME = ISO }|. " '14:30:00'
|
|
26
|
+
|
|
27
|
+
" Formatting: TIMESTAMP
|
|
28
|
+
DATA(lv_ts) = |{ lv_tstamp TIMESTAMP = ISO }|.
|
|
29
|
+
|
|
30
|
+
" Formatting: NUMBER
|
|
31
|
+
DATA(lv_num) = |{ lv_amount NUMBER = USER CURRENCY = 'EUR' }|.
|
|
32
|
+
|
|
33
|
+
" Formatting: CASE
|
|
34
|
+
DATA(lv_upper) = |{ lv_name CASE = UPPER }|.
|
|
35
|
+
DATA(lv_lower) = |{ lv_name CASE = LOWER }|.
|
|
36
|
+
|
|
37
|
+
" Newline, tab
|
|
38
|
+
DATA(lv_multi) = |Line 1{ cl_abap_char_utilities=>newline }Line 2|.
|
|
39
|
+
DATA(lv_tab) = |Col1{ cl_abap_char_utilities=>horizontal_tab }Col2|.
|
|
40
|
+
|
|
41
|
+
" Escape: \|, \{, \}, \\
|
|
42
|
+
DATA(lv_esc) = |Pipe: \| Brace: \{ Backslash: \\|.
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Concatenation
|
|
46
|
+
|
|
47
|
+
```abap
|
|
48
|
+
" && operator (preferred)
|
|
49
|
+
DATA(lv_full) = lv_first && ` ` && lv_last.
|
|
50
|
+
|
|
51
|
+
" CONCATENATE (legacy, still valid)
|
|
52
|
+
CONCATENATE lv_first lv_last INTO DATA(lv_name) SEPARATED BY ` `.
|
|
53
|
+
|
|
54
|
+
" Concatenate lines of internal table
|
|
55
|
+
CONCATENATE LINES OF lt_parts INTO DATA(lv_csv) SEPARATED BY ','.
|
|
56
|
+
|
|
57
|
+
" Using REDUCE
|
|
58
|
+
DATA(lv_joined) = REDUCE string( INIT s = ``
|
|
59
|
+
FOR wa IN lt_items
|
|
60
|
+
NEXT s = COND #( WHEN s IS INITIAL THEN wa-name
|
|
61
|
+
ELSE s && |, { wa-name }| ) ).
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## String Functions
|
|
65
|
+
|
|
66
|
+
```abap
|
|
67
|
+
" Length
|
|
68
|
+
DATA(lv_len) = strlen( lv_text ).
|
|
69
|
+
|
|
70
|
+
" Substring
|
|
71
|
+
DATA(lv_sub) = substring( val = lv_text off = 0 len = 5 ).
|
|
72
|
+
|
|
73
|
+
" Find position (0-based, -1 if not found)
|
|
74
|
+
DATA(lv_pos) = find( val = lv_text sub = '@' ).
|
|
75
|
+
|
|
76
|
+
" Count occurrences
|
|
77
|
+
DATA(lv_cnt) = count( val = lv_text sub = ',' ).
|
|
78
|
+
|
|
79
|
+
" Replace
|
|
80
|
+
DATA(lv_clean) = replace( val = lv_phone sub = '-' with = `` occ = 0 ). " all occurrences
|
|
81
|
+
|
|
82
|
+
" Shift / trim
|
|
83
|
+
DATA(lv_trimmed) = condense( lv_text ). " trim + collapse spaces
|
|
84
|
+
DATA(lv_ltrim) = shift_left( val = lv_text sub = ' ' ).
|
|
85
|
+
DATA(lv_rtrim) = shift_right( val = lv_text sub = ' ' ).
|
|
86
|
+
DATA(lv_cond) = condense( val = lv_text del = ` ` ). " remove all spaces
|
|
87
|
+
|
|
88
|
+
" Case conversion
|
|
89
|
+
DATA(lv_up) = to_upper( lv_text ).
|
|
90
|
+
DATA(lv_lo) = to_lower( lv_text ).
|
|
91
|
+
|
|
92
|
+
" Reverse
|
|
93
|
+
DATA(lv_rev) = reverse( lv_text ).
|
|
94
|
+
|
|
95
|
+
" Contains / matches
|
|
96
|
+
IF contains( val = lv_text sub = 'SAP' ).
|
|
97
|
+
IF matches( val = lv_email regex = '^\w+@\w+\.\w+$' ).
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Regex (PCRE)
|
|
101
|
+
|
|
102
|
+
```abap
|
|
103
|
+
" FIND with regex
|
|
104
|
+
FIND REGEX '(\d{4})-(\d{2})-(\d{2})' IN lv_text
|
|
105
|
+
SUBMATCHES DATA(lv_year) DATA(lv_month) DATA(lv_day).
|
|
106
|
+
|
|
107
|
+
" FIND ALL OCCURRENCES
|
|
108
|
+
FIND ALL OCCURRENCES OF REGEX '\d+' IN lv_text
|
|
109
|
+
RESULTS DATA(lt_matches).
|
|
110
|
+
|
|
111
|
+
" REPLACE with regex
|
|
112
|
+
REPLACE ALL OCCURRENCES OF REGEX '\s+' IN lv_text WITH ` `.
|
|
113
|
+
|
|
114
|
+
" Match function
|
|
115
|
+
IF matches( val = lv_phone regex = '^\+?\d{10,15}$' ).
|
|
116
|
+
" Valid phone number
|
|
117
|
+
ENDIF.
|
|
118
|
+
|
|
119
|
+
" Named groups (PCRE)
|
|
120
|
+
FIND REGEX '(?<year>\d{4})-(?<month>\d{2})' IN lv_text
|
|
121
|
+
SUBMATCHES DATA(lv_y) DATA(lv_m).
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Type Conversions
|
|
125
|
+
|
|
126
|
+
```abap
|
|
127
|
+
" String <-> Number
|
|
128
|
+
DATA(lv_str) = CONV string( 12345 ).
|
|
129
|
+
DATA(lv_int) = CONV i( '999' ).
|
|
130
|
+
DATA(lv_dec) = CONV decfloat34( '123.456' ).
|
|
131
|
+
|
|
132
|
+
" String <-> Date
|
|
133
|
+
DATA(lv_date_str) = |{ sy-datum DATE = ISO }|. " '2026-03-15'
|
|
134
|
+
" Parse date from string:
|
|
135
|
+
DATA lv_date TYPE sy-datum.
|
|
136
|
+
lv_date = |20260315|.
|
|
137
|
+
|
|
138
|
+
" NUMC <-> String (with ALPHA)
|
|
139
|
+
DATA(lv_numc) = |{ '1234' ALPHA = IN WIDTH = 10 }|. " '0000001234'
|
|
140
|
+
DATA(lv_clean2) = |{ lv_numc10 ALPHA = OUT }|. " '1234'
|
|
141
|
+
|
|
142
|
+
" xstring (hex) <-> string
|
|
143
|
+
DATA(lv_xstr) = cl_abap_codepage=>convert_to( lv_text ).
|
|
144
|
+
DATA(lv_back) = cl_abap_codepage=>convert_from( lv_xstr ).
|
|
145
|
+
|
|
146
|
+
" JSON serialization
|
|
147
|
+
DATA(lv_json) = /ui2/cl_json=>serialize( data = ls_structure ).
|
|
148
|
+
/ui2/cl_json=>deserialize( EXPORTING json = lv_json CHANGING data = ls_target ).
|
|
149
|
+
|
|
150
|
+
" XCO string (Cloud)
|
|
151
|
+
DATA(lv_upper2) = xco_cp=>string( lv_text )->to_upper_case( )->value.
|
|
152
|
+
DATA(lv_split) = xco_cp=>string( lv_csv )->split( ',' )->value.
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Split
|
|
156
|
+
|
|
157
|
+
```abap
|
|
158
|
+
SPLIT lv_csv AT ',' INTO TABLE DATA(lt_parts).
|
|
159
|
+
SPLIT lv_line AT cl_abap_char_utilities=>horizontal_tab INTO DATA(lv_f1) DATA(lv_f2) DATA(lv_f3).
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Rules
|
|
163
|
+
- String templates `| |` are preferred over CONCATENATE for readability
|
|
164
|
+
- `&&` concatenation for simple joins, `| |` for interpolation
|
|
165
|
+
- `occ = 0` in `replace()` means all occurrences (default is first only)
|
|
166
|
+
- `condense()` trims and collapses internal whitespace
|
|
167
|
+
- `find()` returns -1 if not found (check before using as offset)
|
|
168
|
+
- ALPHA IN/OUT via string templates is the cleanest conversion
|
|
169
|
+
- Use `matches()` for regex validation, `FIND REGEX` for extraction
|
|
170
|
+
|
|
171
|
+
## Anti-Patterns
|
|
172
|
+
| Anti-Pattern | Correct |
|
|
173
|
+
|---|---|
|
|
174
|
+
| `CONCATENATE a b INTO c SEPARATED BY space` | `c = \|{ a } { b }\|` |
|
|
175
|
+
| Manual padding with SHIFT/OVERLAY | `\|{ val WIDTH = 10 PAD = '0' }\|` |
|
|
176
|
+
| `WRITE lv_date TO lv_str` for date formatting | `\|{ lv_date DATE = ISO }\|` |
|
|
177
|
+
| `strlen( ) - 1` loop for char-by-char | Use `find()`, `replace()`, regex |
|
|
178
|
+
| `TRANSLATE text TO UPPER CASE` | `to_upper( text )` |
|
|
179
|
+
| `SEARCH text FOR pattern` (legacy) | `find( val = text sub = pattern )` |
|
|
180
|
+
| `cl_abap_regex` for simple matches | `matches( val = text regex = pattern )` |
|