Modern ABAP Syntax Notes — Patterns and Pitfalls from Real Development
ABAP syntax actually used while developing our custom Z reports, organized by topic. Examples come from working sources, together with the pitfalls we actually hit.
Structure: syntax item → explanation → example code. Last updated: 2026-08-13
1. Data Declarations and Types
▷ Inline declarations DATA(...) / @DATA(...) — Declare and assign in one statement. SELECT result tables land directly in @DATA(lt_...) — the structure is derived from the SELECT list
SELECT matnr, maktx FROM makt INTO TABLE @DATA(lt_makt) WHERE spras = @sy-langu. DATA(lv_qty) = ls_stpo-menge * iv_losgr / lv_bmeng. LOOP AT lt_mat INTO DATA(ls_mat). " loop variable inline too ENDLOOP.
▷ FIELD-SYMBOLS / ASSIGNING — Modify internal table rows in place without copying. ASSIGN COMPONENT lets you pick the column name at runtime (used for dynamic logic such as auto-hiding empty columns)
LOOP AT gt_list ASSIGNING FIELD-SYMBOL(<ls_list>) WHERE typps = 'M'. <ls_list>-comp_txt = ls_makt-maktx. " the original row changes ENDLOOP. " access a column by name at runtime (empty-column check in ZCO0041) ASSIGN COMPONENT ls_comp-name OF STRUCTURE ls_row TO FIELD-SYMBOL(<lv_val>). IF <lv_val> IS ASSIGNED AND <lv_val> IS NOT INITIAL. ...
▷ TYPES / structure and table types — Declaring ty_list (one ALV row) and tt_... (table type) at the top of the report is the common skeleton of all our reports
TYPES: BEGIN OF ty_list,
matnr TYPE mara-matnr,
maktx TYPE makt-maktx,
dmbtr TYPE mseg-dmbtr,
waers TYPE t001-waers,
END OF ty_list,
tt_list TYPE STANDARD TABLE OF ty_list WITH DEFAULT KEY.
DATA gt_list TYPE tt_list.
2. Constructor Expressions (VALUE / COND / CONV)
▷ VALUE #( ... ) — Build a structure or table row in one statement and APPEND it — replaces multiple field-by-field assignments
APPEND VALUE ty_list(
bukrs = ls_ce1-bukrs
matnr = ls_ce1-artnr
dmbtr = ls_ce1-vv010 ) TO gt_list.
APPEND VALUE #( racct = ls_rpt-racct rfarea = ls_rpt-rfarea
rpt_amt = ls_rpt-amt ) TO gt_amt.
▷ COND — value from a condition — An IF/ELSE assignment as a single expression. Used for things like the movement type sign (+1/-1)
DATA(lv_sign) = COND i( WHEN ls_mseg-bwart = '262'
OR ls_mseg-bwart = '544'
THEN -1 ELSE 1 ).
DATA(lv_val) = COND ckis-wertn(
WHEN sy-subrc <> 0 OR ls_mbew-peinh = 0 THEN 0
WHEN ls_mbew-vprsv = 'S' THEN lv_qty * ls_mbew-stprs / ls_mbew-peinh
ELSE lv_qty * ls_mbew-verpr / ls_mbew-peinh ).
▷ CONV — explicit type conversion — Strictly-typed parameters (class methods etc.) need CONV up front — we actually hit this with i_node_text (lvc_value) of CL_GUI_ALV_TREE and with level arithmetic
" tree node text: type mismatch without CONV CALL METHOD go_tree->add_node EXPORTING i_node_text = CONV lvc_value( ls_item-descript ). " an arithmetic expression can't go straight into a comparison — " pre-compute it (hit in ZFI0031) DATA(lv_child_lvl) = CONV stufe_f02e( lv_rs + 1 ).
3. Internal Table Handling
▷ READ TABLE ... BINARY SEARCH — The basic pattern for mass matching such as text enrichment — always SORT first. Read everything with FOR ALL ENTRIES, then attach via BINARY SEARCH
SORT lt_makt BY matnr.
LOOP AT gt_list ASSIGNING FIELD-SYMBOL(<ls>).
READ TABLE lt_makt INTO DATA(ls_makt)
WITH KEY matnr = <ls>-matnr BINARY SEARCH.
IF sy-subrc = 0.
<ls>-maktx = ls_makt-maktx.
ENDIF.
ENDLOOP.
▷ SORT + DELETE ADJACENT DUPLICATES — When only existence matters in a FOR ALL ENTRIES result (a GROUP BY substitute) — used for the invoice-receipt check in ZMM0030
SORT lt_inv BY ebeln ebelp. DELETE ADJACENT DUPLICATES FROM lt_inv COMPARING ebeln ebelp.
▷ COLLECT — Automatically sums numeric fields of rows with the same key — used for per-component quantity/amount totals
LOOP AT lt_mseg INTO DATA(ls_mseg). CLEAR ls_amt. ls_amt-comp = ls_mseg-matnr. ls_amt-menge = ls_mseg-menge * lv_sign. ls_amt-dmbtr = ls_mseg-dmbtr * lv_sign. COLLECT ls_amt INTO ct_act. ENDLOOP.
4. String Processing
▷ String templates |...{ }...| — Variable interpolation and formatting options in one place. ALPHA = OUT strips leading zeros (account 0000004010 → 4010)
DATA(lv_comp) = CONV mara-matnr( |{ ls_frl-racct ALPHA = OUT }| ).
DATA(lv_pair) = |{ ls_key-field }:{ lv_txt }|.
" caution: passing |...| directly as a PERFORM argument is a syntax
" error ("Field \"|\" is unknown") — put it in a variable first
PERFORM add_line USING lv_pair. " OK
" PERFORM add_line USING |{ ... }|. " syntax error
▷ CONCATENATE vs && — Prefer && or string templates in new code — CONCATENATE only really earns its place when you need SEPARATED BY
lv_text = lv_a && ' / ' && lv_b. CONCATENATE lv_a lv_b INTO lv_text SEPARATED BY space.
5. Selection Screens
▷ PARAMETERS / SELECT-OPTIONS — SELECT-OPTIONS must be anchored on a structure that can appear in TABLES — CDS views and dynamic tables can't anchor, so use a similar table instead (ZFI0040 anchors on SKB1/BKPF, ZCO0030 on T001W)
TABLES: t001, mara. PARAMETERS: p_bukrs TYPE bukrs OBLIGATORY DEFAULT 'CC10', p_expand AS CHECKBOX DEFAULT 'X'. SELECT-OPTIONS: s_matnr FOR mara-matnr, s_werks FOR t001w-werks.
▷ MODIF ID + screen control — Hide fields and toggle them with a specific OK-code (=A) — the hidden option in ZCO0041. Inputs starting with '/' are intercepted by the kernel, so use a plain character (=A)
PARAMETERS p_exec AS CHECKBOX MODIF ID hid.
AT SELECTION-SCREEN OUTPUT.
LOOP AT SCREEN.
IF screen-group1 = 'HID' AND gv_exec_vis = abap_false.
screen-active = '0'.
MODIFY SCREEN.
ENDIF.
ENDLOOP.
AT SELECTION-SCREEN.
IF sy-ucomm = 'A'. " when =A is typed in the command field
gv_exec_vis = boolc( gv_exec_vis = abap_false ).
ENDIF.
▷ Custom F4 (possible entries) — At F4 time PAI has not run yet, so other fields' values must be read straight off the screen with DYNP_VALUES_READ — the cycle F4 in ZCO0041
AT SELECTION-SCREEN ON VALUE-REQUEST FOR s_cycle-low. PERFORM f4_cycle CHANGING s_cycle-low. " inside the FORM: show the list with F4IF_INT_TABLE_VALUE_REQUEST, " read the controlling-area input with DYNP_VALUES_READ and filter
6. Report Event Sequence
▷ Event blocks — LOAD-OF-PROGRAM (once at load) → INITIALIZATION (selection screen defaults) → AT SELECTION-SCREEN OUTPUT (before display, every time) → AT SELECTION-SCREEN (input validation / OK-codes) → START-OF-SELECTION (execution). Report globals reset on every run, so state across runs is kept with EXPORT/IMPORT MEMORY ID
LOAD-OF-PROGRAM.
IMPORT vis = gv_exec_vis FROM MEMORY ID gc_vis_memid.
INITIALIZATION.
p_spmon = sy-datum(6).
AT SELECTION-SCREEN.
IF sy-ucomm = 'A'.
gv_exec_vis = boolc( gv_exec_vis = abap_false ).
EXPORT vis = gv_exec_vis TO MEMORY ID gc_vis_memid.
ENDIF.
START-OF-SELECTION.
PERFORM build_list.
▷ Double-click navigation — SET PARAMETER + CALL TRANSACTION AND SKIP FIRST SCREEN — the user_command pattern of nearly every report
FORM user_command USING rv_ucomm TYPE sy-ucomm
rs_selfield TYPE slis_selfield.
READ TABLE gt_list INTO DATA(ls_row) INDEX rs_selfield-tabindex.
CASE rv_ucomm.
WHEN '&IC1'. " double-click
SET PARAMETER ID 'AUN' FIELD ls_row-vbeln.
CALL TRANSACTION 'VA03' AND SKIP FIRST SCREEN.
ENDCASE.
ENDFORM.
7. Syntax Traps We Actually Hit
▷ SELECT SINGLE + ORDER BY — Syntax error ("ORDER is invalid here") — for the latest single record use UP TO 1 ROWS + ORDER BY + ENDSELECT
SELECT kalnr, kadky FROM keko WHERE matnr = @iv_matnr AND freig = 'X' ORDER BY kadky DESCENDING INTO @DATA(ls_keko) UP TO 1 ROWS. ENDSELECT.
▷ String template as a PERFORM argument — PERFORM ... USING |...| is a syntax error — assign to a variable first (actually hit while deploying ZCO0040)
DATA(lv_pair) = |{ ls_key-field }:{ lv_val }|.
PERFORM add_etc USING lv_pair. " OK
" PERFORM add_etc USING |{ ls_key-field }:{ lv_val }|. " error
▷ Event parameter type mismatch — Receiving the OO ALV event's e_row-index in a FORM parameter typed TYPE i fails activation — receive as TYPE ANY or convert first (hit in ZJNC)
FORM on_double_click USING iv_row TYPE any
io_grid TYPE REF TO cl_gui_alv_grid.
DATA lv_index TYPE i.
lv_index = iv_row. " convert here
ENDFORM.
▷ FOR ALL ENTRIES implicit duplicate removal — Without a unique key in the SELECT list, identical rows collapse into one — always include the document key (MBLNR/ZEILE, ...). This is why subcontracting actuals once showed zero in ZCO0020
SELECT mblnr, mjahr, zeile, " unique key columns required
bwart, matnr, menge, dmbtr
FROM mseg
FOR ALL ENTRIES IN @lt_aufnr
WHERE aufnr = @lt_aufnr-aufnr
INTO TABLE @DATA(lt_mseg).
· How Sarah Joined a K-Dumpling Company and Became an SAP Genius — the business novel, 53 pages, $25
· The Dumpling Factory: Building a Complete SAP S/4HANA Company from Scratch — the build manual, 85 pages, $50
Comments
Post a Comment