@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.
Files changed (114) hide show
  1. package/README.md +384 -0
  2. package/dist/adt-client.js +364 -0
  3. package/dist/cli/activate.js +113 -0
  4. package/dist/cli/init.js +333 -0
  5. package/dist/cli/remove.js +80 -0
  6. package/dist/cli/status.js +229 -0
  7. package/dist/cli/systems.js +68 -0
  8. package/dist/cli.js +81 -0
  9. package/dist/index.js +1318 -0
  10. package/dist/knowledge/abap/abap-dictionary.md +199 -0
  11. package/dist/knowledge/abap/abap-sql.md +296 -0
  12. package/dist/knowledge/abap/amdp.md +273 -0
  13. package/dist/knowledge/abap/clean-code.md +293 -0
  14. package/dist/knowledge/abap/cloud-background-processing.md +250 -0
  15. package/dist/knowledge/abap/cloud-communication.md +265 -0
  16. package/dist/knowledge/abap/cloud-development.md +176 -0
  17. package/dist/knowledge/abap/cloud-extensibility.md +252 -0
  18. package/dist/knowledge/abap/cloud-released-apis.md +261 -0
  19. package/dist/knowledge/abap/constructor-expressions.md +289 -0
  20. package/dist/knowledge/abap/enhancements.md +232 -0
  21. package/dist/knowledge/abap/exceptions.md +271 -0
  22. package/dist/knowledge/abap/internal-tables.md +205 -0
  23. package/dist/knowledge/abap/object-orientation.md +298 -0
  24. package/dist/knowledge/abap/performance.md +216 -0
  25. package/dist/knowledge/abap/rap-abstract-entities.md +206 -0
  26. package/dist/knowledge/abap/rap-business-events.md +216 -0
  27. package/dist/knowledge/abap/rap-draft.md +191 -0
  28. package/dist/knowledge/abap/rap-eml.md +453 -0
  29. package/dist/knowledge/abap/rap-end-to-end.md +486 -0
  30. package/dist/knowledge/abap/rap-feature-control.md +185 -0
  31. package/dist/knowledge/abap/rap-numbering.md +280 -0
  32. package/dist/knowledge/abap/rap-service-exposure.md +163 -0
  33. package/dist/knowledge/abap/rap-unmanaged.md +468 -0
  34. package/dist/knowledge/abap/string-processing.md +180 -0
  35. package/dist/knowledge/abap/unit-testing.md +303 -0
  36. package/dist/knowledge/abap-cds/access-control.md +241 -0
  37. package/dist/knowledge/abap-cds/annotations.md +331 -0
  38. package/dist/knowledge/abap-cds/associations.md +254 -0
  39. package/dist/knowledge/abap-cds/expressions.md +230 -0
  40. package/dist/knowledge/abap-cds/functions.md +245 -0
  41. package/dist/knowledge/abap-cds/metadata-extensions.md +294 -0
  42. package/dist/knowledge/cap/authentication.md +278 -0
  43. package/dist/knowledge/cap/cdl-syntax.md +247 -0
  44. package/dist/knowledge/cap/cql-queries.md +266 -0
  45. package/dist/knowledge/cap/deployment.md +343 -0
  46. package/dist/knowledge/cap/event-handlers.md +287 -0
  47. package/dist/knowledge/cap/fiori-integration.md +303 -0
  48. package/dist/knowledge/cap/service-definitions.md +287 -0
  49. package/dist/knowledge/fiori/annotations.md +347 -0
  50. package/dist/knowledge/fiori/deployment.md +340 -0
  51. package/dist/knowledge/fiori/fiori-elements.md +332 -0
  52. package/dist/knowledge/fiori/fiori-side-effects.md +107 -0
  53. package/dist/knowledge/fiori/fiori-valuelist.md +144 -0
  54. package/dist/knowledge/fiori/ui5-controllers.md +358 -0
  55. package/dist/knowledge/fiori/ui5-data-binding.md +311 -0
  56. package/dist/knowledge/fiori/ui5-fragments-dialogs.md +330 -0
  57. package/dist/knowledge/fiori/ui5-manifest.md +411 -0
  58. package/dist/knowledge/fiori/ui5-routing.md +303 -0
  59. package/dist/knowledge/fiori/ui5-xml-views.md +294 -0
  60. package/dist/logger.js +114 -0
  61. package/dist/system-profile.js +207 -0
  62. package/dist/tools/abap-doc.js +72 -0
  63. package/dist/tools/abapgit.js +161 -0
  64. package/dist/tools/activate.js +68 -0
  65. package/dist/tools/atc-check.js +117 -0
  66. package/dist/tools/auth-object.js +56 -0
  67. package/dist/tools/breakpoints.js +76 -0
  68. package/dist/tools/call-hierarchy.js +84 -0
  69. package/dist/tools/cds-annotations.js +98 -0
  70. package/dist/tools/cds-dependencies.js +65 -0
  71. package/dist/tools/check.js +47 -0
  72. package/dist/tools/code-completion.js +70 -0
  73. package/dist/tools/code-coverage.js +111 -0
  74. package/dist/tools/create-amdp.js +111 -0
  75. package/dist/tools/create-dcl.js +81 -0
  76. package/dist/tools/create-transport.js +38 -0
  77. package/dist/tools/create.js +285 -0
  78. package/dist/tools/data-preview.js +37 -0
  79. package/dist/tools/delete.js +45 -0
  80. package/dist/tools/deploy-bsp.js +298 -0
  81. package/dist/tools/discovery.js +59 -0
  82. package/dist/tools/element-info.js +93 -0
  83. package/dist/tools/enhancements.js +186 -0
  84. package/dist/tools/extract-method.js +44 -0
  85. package/dist/tools/function-group.js +59 -0
  86. package/dist/tools/knowledge.js +275 -0
  87. package/dist/tools/lock-object.js +75 -0
  88. package/dist/tools/message-class.js +67 -0
  89. package/dist/tools/navigate.js +80 -0
  90. package/dist/tools/number-range.js +57 -0
  91. package/dist/tools/object-documentation.js +43 -0
  92. package/dist/tools/object-structure.js +78 -0
  93. package/dist/tools/object-versions.js +57 -0
  94. package/dist/tools/package-contents.js +60 -0
  95. package/dist/tools/pretty-printer.js +35 -0
  96. package/dist/tools/publish-binding.js +49 -0
  97. package/dist/tools/quick-fix.js +69 -0
  98. package/dist/tools/read.js +167 -0
  99. package/dist/tools/refactor-rename.js +60 -0
  100. package/dist/tools/release-transport.js +24 -0
  101. package/dist/tools/released-apis.js +51 -0
  102. package/dist/tools/repository-tree.js +90 -0
  103. package/dist/tools/scaffold-rap.js +642 -0
  104. package/dist/tools/search.js +73 -0
  105. package/dist/tools/shared/data-format.js +101 -0
  106. package/dist/tools/sql-console.js +17 -0
  107. package/dist/tools/system-info.js +270 -0
  108. package/dist/tools/traces.js +66 -0
  109. package/dist/tools/transport-contents.js +83 -0
  110. package/dist/tools/transports.js +67 -0
  111. package/dist/tools/unit-test.js +135 -0
  112. package/dist/tools/where-used.js +59 -0
  113. package/dist/tools/write.js +101 -0
  114. 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 )` |