Generates ABAP source code from a process text file ({doc_name}_process.txt) produced by sap-docs-extract. Supports generating: - Dialog/Module Pool programs - ABAP Reports (帳票/バッチ) - Function Modules / RFC Uses variable naming conventions from sap-dev-core/shared/tables/abap_naming_rules.tsv (or custom override from {custom_url}). Prerequisites: Run /sap-docs-convert and /sap-docs-extract first.
Scanned 5/27/2026
Install via CLI
openskills install sapdev-ai/sap-dev---
name: sap-gen-abap
description: |
Generates ABAP source code from a process text file ({doc_name}_process.txt)
produced by sap-docs-extract. Supports generating:
- Dialog/Module Pool programs
- ABAP Reports (帳票/バッチ)
- Function Modules / RFC
Uses variable naming conventions from sap-dev-core/shared/tables/abap_naming_rules.tsv (or custom override from {custom_url}).
Prerequisites: Run /sap-docs-convert and /sap-docs-extract first.
argument-hint: "<path-to-process-txt> [--refresh-cache]"
---
# Generate ABAP Skill
You generate ABAP source code from a process text file produced by `/sap-docs-extract`.
Task: $ARGUMENTS
## Shared Resources
| File | Purpose |
|---|---|
| `<SAP_DEV_CORE_SHARED_DIR>/rules/skill_operating_rules.md` | Mandatory operating rules |
| `<SAP_DEV_CORE_SHARED_DIR>/rules/language_independence_rules.md` | GUI-scripting language independence — offline generator, but rule applies to downstream deploy skills the generated source feeds |
| `<SAP_DEV_CORE_SHARED_DIR>/rules/abap_code_quality_rules.md` | **Mandatory ABAP code-quality rules** — release-aware modern syntax, OOP scaffolds, exception classes, performance gates, authz hooks, ABAP Unit, dependency + traceability emission |
| `<SAP_DEV_CORE_SHARED_DIR>/templates/customer_brief.md` | One-page Project Profile read at Step 0a; drives release / OOP / perf decisions |
| `<SAP_DEV_CORE_SHARED_DIR>/tables/abap_naming_rules.tsv` | Variable naming prefixes (overridable via `{custom_url}`) |
| `<SAP_DEV_CORE_SHARED_DIR>/scripts/sap_log_helper.ps1` | Shared start/step/end wrapper around `sap_log_lib.ps1`. State file: `{WORK_TEMP}\sap_gen_abap_run.json`. Logging is best-effort. |
---
## Step 0 — Resolve Work Directory
**Settings reads/writes follow `<SAP_DEV_CORE_SHARED_DIR>/rules/settings_lookup.md`** — merge `settings.local.json` over `settings.json` per-key on the `.value` field; writes always go to `settings.local.json`. Resolve cross-plugin paths: 3 levels up from `<SKILL_DIR>`, then into `sap-dev-core\settings.json` and (if present) `sap-dev-core\settings.local.json`. Read `work_dir`, `custom_url`.
| Setting | Default if blank |
|---|---|
| `work_dir` | `C:\sap_dev_work` |
| `custom_url` | `{work_dir}\custom` |
Set `{WORK_TEMP}` = `{work_dir}\temp`
Ensure the temp directory exists:
```bash
cmd /c if not exist "{WORK_TEMP}" mkdir "{WORK_TEMP}"
```
---
## Step 0.5 — Start Logging
Start a structured log run. Best-effort: silently no-ops if disabled or the
lib can't load. `<SAP_DEV_CORE_SHARED_DIR>` resolves to
`plugins/sap-dev-core/shared/`. State file: `{WORK_TEMP}\sap_gen_abap_run.json`.
```bash
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action start -StateFile "{WORK_TEMP}\sap_gen_abap_run.json" -Skill sap-gen-abap -ParamsJson "{\"input\":\"<PROCESS_TXT_PATH>\"}"
```
---
## Step 0a — Read the Customer Project Brief
Read `{custom_url}\customer_brief.md` if it exists; otherwise fall back to
`<SAP_DEV_CORE_SHARED_DIR>/templates/customer_brief.md` (the empty default).
Extract these fields and use them to set generator MODE flags applied
end-to-end:
| Brief field | MODE flag set |
|---|---|
| ABAP release ≥ `7.40 SP08` | `MODE_MODERN_ABAP = TRUE` (use `DATA(...)`, `VALUE #(...)`, `@` host vars, `line_exists()`, `FOR/COND/REDUCE`) |
| OOP scaffolds = `yes` (or new program) | `MODE_OOP = TRUE` (emit `lcl_main` + `START-OF-SELECTION → run( )`) |
| ABAP Unit tests required | `MODE_UNIT_TESTS = TRUE` (emit `ltcl_main` with one `test_*` per golden I/O) |
| Change document logging required | `MODE_CHANGE_DOC = TRUE` |
| Volume band per object | `MODE_PERF_BAND = small/medium/large` (drives `SELECT SINGLE` / `INTO TABLE` / `PACKAGE SIZE`) |
| Authz objects per area | `MODE_AUTHZ_OBJECT = <name>` (drives `AUTHORITY-CHECK` placeholder) |
| Reusable utilities catalogue | `MODE_REUSE = <list>` (prefer these calls over scaffolding new helpers) |
| Project sub-prefix (e.g. `ZHK`) | applied to all generated names |
| Default message class | replaces hard-coded `MSGS` fallback |
| Max method length | `MODE_MAX_METHOD_LINES` (default 50; checker warns) |
If the brief is empty / missing, ask the user:
> "No Project Brief found. I can generate with safe defaults (classic ABAP, FORM
> routines, no unit tests, $TMP package). To get release-aware modern ABAP +
> OOP + ABAP Unit tests + traceability files, fill out
> `<SAP_DEV_CORE_SHARED_DIR>/templates/customer_brief.md` and save to
> `{custom_url}\customer_brief.md`. Continue with defaults?"
Apply `<SAP_DEV_CORE_SHARED_DIR>/rules/abap_code_quality_rules.md` end-to-end.
Emit the chosen mode as a one-line header comment in the generated `.abap`:
```abap
" Generated by sap-gen-abap: ABAP <release>, MODE_MODERN_ABAP=true, MODE_OOP=true, MODE_PERF_BAND=medium.
```
---
## Step 1 — Read the Process Text File
Extract the file path from `$ARGUMENTS`. The file should be a `_process.txt` file in a work folder.
If no path is given, ask the user:
> "Please provide the path to the _process.txt file (created by /sap-docs-extract)."
**Parse optional flags from `$ARGUMENTS`:**
| Flag | Meaning | Default |
|---|---|---|
| `--refresh-cache` | Bypass the FM signature cache for this run; force re-fetch every FM via RFC. Useful when you've recently modified a `Z*` FM and the 1-day TTL hasn't expired yet. | not set (use cache) |
Set `{REFRESH_CACHE}` = `"true"` if `--refresh-cache` is present in `$ARGUMENTS`, else `"false"`. This value is passed to Step 1.5 as the `%%REFRESH_CACHE%%` token.
Read the file using the Read tool.
Also look for and read `_PGM_summary.txt` in the same folder for program metadata (ID, name, type, package).
Determine the program type from the content:
- Dialog/Module Pool: 画面サービス設計書, 画面項目定義, MODULE POOL
- Report: 帳票設計書, バッチ設計書, 実行可プログラム, REPORT
- Function Module: インターフェース設計書, RFC, FUNCTION MODULE
---
## Step 1a — Read Optional Auxiliary Files
The canonical template (built by `tools/build_spec_template.py`) emits several
sibling files alongside `_process.txt`. Each one is OPTIONAL — the skill
works without them — but when present they sharpen the generated code.
For each file below, look in the SAME `{work_folder}` as `_process.txt`. If
a file exists, read it with the Read tool. If absent, continue silently.
| Sibling file | Purpose | Used by |
|---|---|---|
| `{doc_name}_selection_definition.txt` | TSV: one row per selection-screen field. Columns: `NO`, `LABEL`, `NAME_JA`, `NAME_EN`, `DTEL_NAME`, `DATATYPE`, `LENGTH`, `DECIMALS`, `IO_TYPE`, `DISPLAY_FORMAT`, `MANDATORY`, `DESCRIPTION`, `DEFAULT_VALUE`. | Step 2b (Reports) — drives `PARAMETERS` / `SELECT-OPTIONS` declarations AND `[SELECTION_TEXTS]` entries in the sibling `<NAME>.text_elements.txt` (LABEL → text per row) |
| `{doc_name}_selection_screen_layout.png` | **Image** of the WHOLE selection screen. Extracted from the spec workbook by `/sap-docs-extract`. Read as multimodal input. | Step 2b — informs `SELECTION-SCREEN BLOCK` / `WITH FRAME TITLE` / `COMMENT` / `POSITION` structure |
| `{doc_name}_textElements.txt` | TSV: explicit text-symbol overrides from the spec's Text Elements sheet (when present). Columns: `TEXT_ID`, `TEXT_VALUE`. A header-only file means "spec lists no explicit symbols" — that is **not** the same as missing; treat as "no entries, use defaults". | Step 3 — populates `[TEXT_SYMBOLS]` block in the sibling `<NAME>.text_elements.txt` |
| `{doc_name}_interface.txt` | TSV: Inputs / Outputs / Exceptions (anchored sub-sections). | Step 2c (Function Modules) — drives the `IMPORTING / EXPORTING / TABLES / EXCEPTIONS` block |
| `{doc_name}_file_mapping_in.txt` | TSV: file field → SAP table.field. Used for inbound interfaces. | Steps 2a/2b — drives the BAPI parameter assembly and the input-file parser |
| `{doc_name}_file_mapping_out.txt` | TSV: SAP table.field → file field. Outbound interfaces (V2 use). | Reserved — emit as a `" TODO" comment block when present and non-empty |
| `{doc_name}_supplement.txt` | **Free-form text** dump of the customer's Supplement sheet. Anything that didn't fit elsewhere — edge cases, business context, glossary, design rationale. | Read across all generation steps as **low-priority context**. See "Supplement handling" below. |
| `{doc_name}_golden.txt` | TSV: golden test scenarios. | Step 3 (when `MODE_UNIT_TESTS = TRUE`) — one `test_*` method per row |
| `{doc_name}_deps.txt` | TSV: declared dependencies (FMs, BAPIs, includes, classes). | Cross-reference against Step 1.5 FM signatures; emit into `Z<NAME>.deps.txt` |
### Supplement handling (`_supplement.txt`)
Supplement is intentionally unstructured — customers paste in whatever
they want: glossaries, decision logs, "why we chose option B over A",
known quirks of the source system, BAPI gotchas they discovered last
sprint. Treat it as **the lowest-priority context source**.
Rules:
- Read it (if present) at the same time as `_process.txt`. Hold it in
context across all of Step 2.
- The structured files (`_PGM_summary.txt`, `_selection_definition.txt`,
`_file_mapping_in.txt`, `_tables.txt`, `_dataElements.txt`, etc.) are
ALWAYS authoritative. If supplement contradicts a structured file,
the structured file wins.
- Use supplement to inform: ABAP comments above generated blocks
(paste relevant context as `" Note: ...`), variable naming when the
structured spec is ambiguous, decisions about which optional path to
take (e.g. "use BAPI_MATERIAL_SAVEDATA, not BAPI_MATERIAL_SAVE-
REPLICA — supplement says the latter is deprecated in this client").
- DO NOT infer new parameters, fields, or tables from supplement alone.
If supplement implies a field that isn't in the structured spec,
flag it: `" TODO: supplement mentions <field>; not in DDIC. Confirm.`
- Empty `_supplement.txt` (zero bytes or whitespace only) → treat as
absent. No effect on generation.
### Hard rule for selection-screen generation: TSV is canonical, image is a hint
When BOTH `_selection_definition.txt` and `_selection_screen_layout.png`
are present:
| Concern | Source of truth |
|---|---|
| List of parameters / select-options that exist | **Definition TSV** — emit exactly those, no more, no less |
| Each parameter's name, type, length, mandatory flag, default | **Definition TSV** |
| `SELECTION-SCREEN BLOCK` boundaries and frame titles | **Image** (Definition has no grouping data) |
| `SELECTION-SCREEN COMMENT` text and positioning | **Image** |
| Visual grouping cues (which params share a frame) | **Image** |
| Order of parameters on the screen | **Image** if present, else Definition row order |
Disagreement handling:
- If the image **suggests a parameter** that isn't in the Definition →
flag and ask the user. Do **not** invent a `PARAMETERS` line.
- If the Definition has a parameter **not visible in the image** → emit it
anyway. Definition wins; the image may have been cropped or stale.
- If the Definition is **empty / missing** but the image is present →
refuse to generate selection-screen code from the image alone. Ask the
user to fill the Definition sheet first. (Image-only generation is
non-deterministic and can't be validated.)
When neither file is present, fall back to Step 2's existing behaviour
(extract from `_process.txt` field-definitions section).
---
## Step 1.5 — Pre-fetch FM Signatures (Optional, RFC-mode only)
**Purpose:** Eliminate the most common ABAP-generation error class
(hallucinated parameter names on standard BAPIs and customer Z-FMs) by
fetching real FM signatures from the live SAP system **before** generation.
The generator then has ground truth instead of relying on AI training data
for things like `BAPI_MATERIAL_SAVEDATA`'s exact parameter names.
This step is **optional** and skipped automatically when:
- `userConfig.fm_cache_enabled` is `false`, OR
- `userConfig.sap_dev_mode` is not capable of RFC (no NCo 3.1 in GAC), OR
- No FMs are mentioned in the spec, OR
- All requested FMs are already in cache and within TTL.
### 1.5a — Scan for FM mentions
From `{doc_name}_process.txt`, extract every FM name referenced. Patterns to
match:
- `CALL FUNCTION 'NAME'` and `CALL FUNCTION "NAME"`
- Section headers naming FMs: `BAPI_...`, `RFC_...`
- The Customer Brief's "Reusable utilities" / "依存関係" / "Dependencies"
list (also from `customer_brief.md` if present)
- Bullet lists in the process flow that look like `- Call BAPI_MATERIAL_SAVEDATA`
De-duplicate; uppercase. Filter out obvious non-FMs (anything containing
spaces, anything < 3 chars, ABAP keywords like `WRITE`, `SELECT`, etc.).
Write the de-duplicated list to `{WORK_TEMP}\fm_request.txt`, one name per
line. If the list is empty, skip the rest of Step 1.5.
### 1.5b — Resolve cache directory and system ID
Build the system ID from the SAP connection params (lowercased server,
trimmed sysnr, trimmed client):
```
{SYSTEM_ID} = "{sap_application_server}_{sap_system_number}_{sap_client}"
```
Example: `saphost.example.com_00_100`. If any of those settings are blank, skip
Step 1.5 (we cannot meaningfully partition the cache).
Resolve the cache directory:
```
{FM_CACHE_DIR} = userConfig.fm_cache_dir, OR if blank: {work_dir}\cache\fm_signatures
```
### 1.5c — Token-replace and run the lookup script
Template at `<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lookup_fm.ps1`.
Tokens to replace:
| Token | Source |
|---|---|
| `%%SAP_SERVER%%` / `%%SAP_SYSNR%%` / `%%SAP_CLIENT%%` / `%%SAP_USER%%` / `%%SAP_PASSWORD%%` / `%%SAP_LANGUAGE%%` | sap-dev-core `settings.json` |
| `%%REQUEST_FILE%%` | `{WORK_TEMP}\fm_request.txt` (from 1.5a) |
| `%%RESULT_FILE%%` | `{work_folder}\_fm_signatures.txt` |
| `%%CACHE_DIR%%` | `{FM_CACHE_DIR}` from 1.5b |
| `%%SYSTEM_ID%%` | `{SYSTEM_ID}` from 1.5b |
| `%%TTL_STD_DAYS%%` | `userConfig.fm_cache_ttl_std_days` (default `30`) |
| `%%TTL_Z_DAYS%%` | `userConfig.fm_cache_ttl_z_days` (default `1`) |
| `%%REFRESH_CACHE%%` | `{REFRESH_CACHE}` from Step 1 (`"true"` if user passed `--refresh-cache`, else `"false"`) |
| `%%RFC_LIB_PS1%%` | `<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lib.ps1` |
Write the filled template to `{WORK_TEMP}\sap_rfc_lookup_fm_run.ps1` and run
with **32-bit** PowerShell:
```bash
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File {WORK_TEMP}\sap_rfc_lookup_fm_run.ps1
```
Expected stdout:
```
INFO: Requested 5 FM(s).
INFO: Cache hits: 3 / 5
INFO: Cache misses (will fetch): 2
INFO: RFC connected to <server> client <client> (NCo 3.1).
WARN: Z_NONEXISTENT not found or call failed: ... ← if any
INFO: Wrote 5 FM signature(s) to {work_folder}\_fm_signatures.txt
INFO: Cache dir: {FM_CACHE_DIR}\<SYSTEM_ID>
```
### 1.5b' — Pre-fetch SU21 field lists for AUTHORITY-CHECK targets
Mirrors 1.5a-d but for authorization objects. Per `abap_code_quality_rules.md`
§14, the generator MUST shape every `AUTHORITY-CHECK` to match the live
SU21 field list — a hardcoded list is brittle across releases and
customer SU24 customization.
**1.5b'.a — Scan for AUTHORITY-CHECK targets**
Before emitting any code, scan the spec (process text + customer brief
"Authorization objects per area" section + any reused HK utilities) for
every authz-object name the generator will reference. Patterns:
- `customer_brief.md` "Authorization Objects" rows
- Spec text "AUTHORITY-CHECK OBJECT 'M_MATE_…'" or "auth object"
- Reusable utilities catalogue if it routes through specific objects
De-duplicate; uppercase. Write to `{WORK_TEMP}\authz_request.txt`, one
name per line. Skip the rest of 1.5b' when empty.
**1.5b'.b — Run the lookup script**
Template at `<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lookup_authz.ps1`.
Tokens to replace:
| Token | Source |
|---|---|
| `%%SAP_SERVER%%` / `%%SAP_SYSNR%%` / `%%SAP_CLIENT%%` / `%%SAP_USER%%` / `%%SAP_PASSWORD%%` / `%%SAP_LANGUAGE%%` | sap-dev-core `settings.json` |
| `%%REQUEST_FILE%%` | `{WORK_TEMP}\authz_request.txt` |
| `%%RESULT_FILE%%` | `{work_folder}\_authz_signatures.txt` |
| `%%CACHE_DIR%%` | `userConfig.authz_cache_dir` if set, else `{work_dir}\cache\authz_signatures` |
| `%%SYSTEM_ID%%` | `{sap_application_server}_{sap_system_number}_{sap_client}` (lowercase server) |
| `%%TTL_DAYS%%` | `userConfig.authz_cache_ttl_days` (default `90`) |
| `%%REFRESH_CACHE%%` | `{REFRESH_CACHE}` from Step 1 |
| `%%RFC_LIB_PS1%%` | `<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lib.ps1` |
Run via 32-bit PowerShell (same as 1.5c).
**1.5b'.c — Inject AUTHORITY-CHECK shape into Step 2 context**
Read `{work_folder}\_authz_signatures.txt` (TSV: `OBJCT\tPOSITION\tFIELD`).
Group by `OBJCT`. When generating each `AUTHORITY-CHECK`, emit one
`ID '<FIELD>' …` clause per row in POSITION order — no more, no less.
The generator decides per call site whether to pass `FIELD <value>` or
`DUMMY` based on what the gate is checking.
Special rows:
- `OBJCT\tNOT_FOUND\t` — auth object doesn't exist on the target system.
Refuse to emit the AUTHORITY-CHECK; emit a TODO comment and surface
to the user as a spec issue (Z-namespace object not yet created, or
typoed standard object name).
- `OBJCT\tUNAVAILABLE\t` — RFC was unreachable. Fall back to AI training
knowledge but emit a `" TODO: verify SU21 field list against live SAP
after RFC available"` comment in generated code.
### 1.5d — Inject signatures into Step 2 context
Read `{work_folder}\_fm_signatures.txt` (TSV format). When generating ABAP
in Step 2, **prefer this file's parameter names AND types over AI training
knowledge** for any FM that appears here. The TSV columns are:
```
FM_NAME SECTION PARAM_NAME OPTIONAL TYPE_REF TYPE_KIND
SECTION = EXPORTING / IMPORTING / CHANGING / TABLES / EXCEPTIONS
OPTIONAL = " " (mandatory) or "X" (optional)
TYPE_KIND = TAB | TDEF | TYP | "" (none / exception)
```
**Type compatibility is mandatory, not advisory.** Per `abap_code_quality_rules.md`
§24, every actual parameter you emit MUST be type-compatible with `TYPE_REF`.
The most common failure mode the historical builds hit is `CX_SY_DYN_CALL_ILLEGAL_TYPE`
at runtime — formal is `STRING`, actual is `rlgrap-filename` or `c LENGTH n`.
Activation passes; ATC catches it as a P1 SLIN finding only after deploy. To
prevent this:
1. For every `<formal> = <actual>` you generate, look up the formal's `TYPE_REF`
in `_fm_signatures.txt`.
2. Check whether the actual's declared type matches. Acceptable: exact match,
same underlying DOMNAME via DDIF, or `LIKE` of a DDIC field with that ROLLNAME.
3. If the types are incompatible, emit a local adapter variable typed exactly
like `TYPE_REF` and pass that — DO NOT pass the original actual directly.
Worked example for `GUI_UPLOAD FILENAME` (formal `STRING`, actual taken from a
selection-screen `PARAMETERS p_file TYPE rlgrap-filename`):
```abap
" Adapter for GUI_UPLOAD FILENAME: formal is STRING, p_file is rlgrap-filename
DATA(lv_filename) = CONV string( p_file ).
CALL FUNCTION 'GUI_UPLOAD'
EXPORTING
filename = lv_filename
...
```
See rule §24's "Documented common traps" table for the audit list per FM —
treat it as a generation pre-flight before you write the `CALL FUNCTION` block.
Special rows to handle:
- `FM_NAME NOT_FOUND ...` — FM does not exist in target SAP. Either
the spec has a typo OR the FM is a Z-FM not yet deployed. WARN the
developer; do not generate calls to this FM unless they confirm.
- `FM_NAME UNAVAILABLE ...` — RFC was unreachable AND no prior cache.
Fall back to AI training knowledge but emit a `" TODO: verify against
live SAP after RFC available"` comment in generated code.
The audit copy at `_fm_signatures.txt` stays in the work folder for
debugging — when generated code is wrong, comparing this snapshot against
current SAP state pinpoints whether the cache was stale.
---
## Step 1.5e — Pre-fetch DDIC Structure Field Lists (Optional, RFC-mode only)
**Purpose:** Close the gap that Step 1.5 leaves open. FM signatures tell
the generator that `CLIENTDATA` is typed `BAPI_MARA` — but NOT what fields
`BAPI_MARA` actually exposes on this S/4HANA build. AI training knowledge
of BAPI structure internals is unreliable: fields are added/removed/renamed
between releases, and some "obvious" fields (e.g. `gross_wt` / `volume` /
`volumeunit` / `pack_vo` on BAPI_MARA) don't exist on the structure at all
on modern releases — they live on adjacent structures (MARM) and are
written via different BAPI parameters.
This step calls `DDIF_FIELDINFO_GET` on each unique TABNAME referenced by
the FMs from Step 1.5 and writes a field-level signature file the
generator consults during BAPI structure-parameter assignment emission.
Skip when Step 1.5 was skipped, OR `userConfig.struct_cache_enabled` is
`false`, OR no FMs were fetched.
### 1.5e.a — Collect unique TABNAMEs from multiple sources
The struct cache should cover every DDIC structure/table the generator
will reference during emission — not just the ones reachable via BAPI
parameters. Walk EVERY source listed below and collect TABNAMEs:
1. **`{work_folder}\_fm_signatures.txt`** — for each row where
`TYPE_KIND = TDEF` (structure) or `TAB` (table type → also walk to
its line type via DDIF), collect the `TYPE_REF` value. This covers
BAPI parameter structures like `BAPI_MARA`, `BAPI_MARC`.
2. **`{work_folder}\_file_mapping_in.txt`** and `_file_mapping_out.txt`
— for each row, collect the `SAP_TABLE` column value. Catches
standard SAP tables the spec maps file rows to (e.g. `MARA`, `T001`,
`T001W`). Without this, gen-abap can validate BAPI struct fields
but NOT direct `SELECT FROM mara` field lists.
3. **`{work_folder}\_tables.txt`** — for each row's `ReferenceTable`
column where the value is a STANDARD SAP table (not in this spec's
own `_tables.txt`), collect that name. Catches CURR/QUAN reference
targets like `T001-WAERS`.
4. **`{work_folder}\_deps.txt`** — under the `STANDARD_TABLES` section,
collect every name. This is the customer's explicit list of
standard tables the report touches; trusted because the customer
wrote it.
5. **`{work_folder}\_process.txt`** — grep for uppercase `<TABLE>-<FIELD>`
patterns where `<TABLE>` is in the SAP standard namespace (doesn't
start with `Z`/`Y`). Heuristic; include conservatively.
De-duplicate, uppercase, filter:
- Drop any name that starts with `Z`/`Y` AND is defined in this spec's
own `_tables.txt` (the table doesn't exist on SAP yet — gen-abap is
about to create it; live lookup would return NOT_FOUND).
- Drop primitive ABAP type tokens (`C`, `N`, `STRING`, etc.) that
occasionally leak in from spec free-text.
Write to `{WORK_TEMP}\struct_request.txt`, one TABNAME per line. Skip
the rest of 1.5e when empty.
When `/sap-docs-check-process` or `/sap-docs-check-ddic` ran earlier in
the pipeline, `_struct_signatures.txt` may already be populated for some
of these names — the per-system disk cache absorbs the duplicate work
silently (Test-CacheHit). No extra logic needed; just include all the
names and let the cache layer dedupe.
### 1.5e.b — Run the lookup script
Template at `<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lookup_struct.ps1`.
Tokens to replace:
| Token | Source |
|---|---|
| `%%SAP_SERVER%%` / `%%SAP_SYSNR%%` / `%%SAP_CLIENT%%` / `%%SAP_USER%%` / `%%SAP_PASSWORD%%` / `%%SAP_LANGUAGE%%` | sap-dev-core `settings.json` |
| `%%REQUEST_FILE%%` | `{WORK_TEMP}\struct_request.txt` |
| `%%RESULT_FILE%%` | `{work_folder}\_struct_signatures.txt` |
| `%%CACHE_DIR%%` | `userConfig.struct_cache_dir` if set, else `{work_dir}\cache\struct_signatures` |
| `%%SYSTEM_ID%%` | `{sap_application_server}_{sap_system_number}_{sap_client}` (lowercase server) — same as 1.5b |
| `%%TTL_STD_DAYS%%` | `userConfig.struct_cache_ttl_std_days` (default `30`) |
| `%%TTL_Z_DAYS%%` | `userConfig.struct_cache_ttl_z_days` (default `1`) |
| `%%REFRESH_CACHE%%` | `{REFRESH_CACHE}` from Step 1 |
| `%%RFC_LIB_PS1%%` | `<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lib.ps1` |
Run via 32-bit PowerShell (same as 1.5c).
### 1.5e.c — Inject struct field lists into Step 2 context
Read `{work_folder}\_struct_signatures.txt` (TSV format). When generating
ABAP that assigns to a BAPI structure parameter, **emit an assignment ONLY
when the target field exists in the cached struct definition** for that
TABNAME. Otherwise emit either:
- a `marmdata`/`marmdatax` table assignment (per rule §22's preferred
pattern for weight/volume traps), OR
- an explicit TODO comment block (per §22's acceptable V0 fallback).
The TSV columns are:
```
TABNAME POSITION FIELDNAME ROLLNAME DOMNAME INTTYPE LENG DECIMALS KEYFLAG
```
Special rows:
- `TABNAME NOT_FOUND ...` — Structure doesn't exist on this server.
Likely a typo in the FM signature (rare) or the structure was renamed
on this release. Refuse to emit any assignment to this typename and
surface to the developer as a spec issue.
- `TABNAME UNAVAILABLE ...` — RFC was unreachable. Fall back to AI
training knowledge with a `" TODO: verify against live SAP after RFC
available"` comment in generated code.
When matching a spec source field to a struct target field, prefer:
1. **Exact FIELDNAME match** (case-insensitive).
2. **ROLLNAME match** — when the spec gives a DTEL name (e.g. `BRGEW`),
look up which struct field uses that ROLLNAME and emit the
FIELDNAME from the struct (e.g. on BAPI_MARA the ROLLNAME `BRGEW`
maps to FIELDNAME ... TODO if exists, else MARM path).
3. **No fuzzy/substring matching** — those cause false positives. If
neither exact FIELDNAME nor ROLLNAME match, emit a TODO with the
spec-supplied name preserved as a comment.
The audit copy at `_struct_signatures.txt` stays in the work folder for
debugging.
---
## Step 2 — Generate ABAP Code
**Naming conventions:** First check if `{custom_url}\abap_naming_rules.tsv` exists. If yes, read naming rules from there. Otherwise use `sap-dev-core/shared/tables/abap_naming_rules.tsv`. Use variable prefixes from the resolved naming rules file (e.g., `lv_` for local variables, `ls_` for local structures, `lt_` for local tables, `gv_`/`gs_`/`gt_` for globals, `p_` for selection parameters). This ensures code passes `sap-check-abap` validation.
**Line-length budget (HARD LIMIT — 72 characters):** Every emitted ABAP
source line MUST be **at most 72 characters** wide (counting from
column 1 inclusive). See *Important: ABAP Source Line Length* below
for wrap rules and an enforcement checklist; apply those rules **as
you generate**, not as a post-pass — fixing wraps after the fact
breaks indentation and string literals.
### 2a. Dialog / Module Pool Program
Generate a complete ABAP program skeleton with these sections in order:
```abap
*&---------------------------------------------------------------------*
*& Program : Z<SERVICE_ID>
*& Title : <SERVICE_NAME>
*& Service : <SERVICE_ID>
*& Generated: <TODAY_DATE>
*& Document : <DOCUMENT_FILENAME>
*&---------------------------------------------------------------------*
PROGRAM z<service_id> MESSAGE-ID <message_class>.
*&---------------------------------------------------------------------*
*& Type Definitions
*&---------------------------------------------------------------------*
" Add TYPES for structured fields if needed
*&---------------------------------------------------------------------*
*& Data Declarations
*&---------------------------------------------------------------------*
" One DATA statement per logical section
" Format: DATA: <field_name> TYPE <type>, "<japanese_label> (<mandatory_flag>)"
DATA:
" ===== <SECTION_NAME_1> =====
<field1> TYPE <type1>, " <label1> (<mandatory1>)
<field2> TYPE <type2>, " <label2> (<mandatory2>)
...
" ===== <SECTION_NAME_2> =====
<fieldN> TYPE <typeN>. " <labelN> (<mandatoryN>)
*&---------------------------------------------------------------------*
*& Screen Flow Logic Includes
*&---------------------------------------------------------------------*
" Note: In real development, PBO/PAI modules go in screen flow logic.
" The following FORM routines can be called from PAI MODULE.
*&---------------------------------------------------------------------*
*& PBO Routine
*&---------------------------------------------------------------------*
FORM pbo_main.
SET PF-STATUS 'MAIN'.
SET TITLEBAR 'T0100'.
" Set default values
<generate default value assignments from デフォルト値 column>
ENDFORM.
*&---------------------------------------------------------------------*
*& Input Validation (called from PAI)
*&---------------------------------------------------------------------*
FORM validate_fields.
" <Generate one validation block per field with validation rules>
" Mandatory checks (◎/●)
" Conditional checks (△) with IF conditions from description
" Single-field checks (単項目チェック)
ENDFORM.
```
**Validation block pattern for each field:**
```abap
"-- <FIELD_LABEL> (<SECTION_NAME>) --
" Mandatory: <◎/○/△>
" Condition: <condition text from description if △>
IF <condition_applies>.
IF <field> IS INITIAL.
MESSAGE e<msgno>(<msgclass>). " mandatory input error
RETURN.
ENDIF.
" Single-field check: <validation text>
IF <validation_condition>.
MESSAGE e<msgno>(<msgclass>) WITH '<error_param>'.
RETURN.
ENDIF.
ENDIF.
```
Use actual message IDs from the document (e.g., `MSGS-ALTM00120` → `e120(msgs)`).
**Combo/dropdown fields:**
Generate a constant or a value table helper:
```abap
CONSTANTS:
gc_<field>_<val_name> TYPE c VALUE '<value>', " <description>
```
### 2b. ABAP Report (帳票設計書 / バッチ設計書)
**Selection-screen sourcing (per Step 1a's hard rule):**
1. If `_selection_definition.txt` is present → emit one `PARAMETERS` or
`SELECT-OPTIONS` line per row, in the row order. Map columns:
- `NAME_EN` (or auto-shortened `P_<name>` from `NAME_JA` if `NAME_EN`
blank) → identifier (≤ 8 chars including `P_` / `S_` prefix).
- `IO_TYPE = INPUT` AND `MANDATORY ≠ REQUIRED` → check `OBLIGATORY` flag
(only `REQUIRED` emits `OBLIGATORY`).
- `DTEL_NAME` non-blank → `TYPE <dtel>`. Else `DATATYPE` + `LENGTH`/
`DECIMALS` → `TYPE c LENGTH n` / `TYPE p LENGTH n DECIMALS d` / etc.
- `DEFAULT_VALUE` non-blank → `DEFAULT <value>` clause.
- `LABEL` (verbatim, in the spec's natural language) → write to the
`[SELECTION_TEXTS]` block of the sibling
`Z<PROGRAM_ID>.text_elements.txt` as `<DTEL_NAME>\t<LABEL>` on the
same row this PARAMETERS / SELECT-OPTIONS emits. Do **not**
translate. See Step 3 sibling-file rules and `abap_code_quality_rules.md`
§21 for the canonical contract.
2. If `_selection_screen_layout.png` is present → read it as a multimodal
input. Use ONLY for: BLOCK boundaries, frame titles (`TEXT-001`,
`TEXT-002`, …), `COMMENT` lines, parameter ordering hints, and visual
alignment notes. Do not use the image to add or remove parameters.
3. If only `_process.txt` is present (legacy flow) → fall back to extracting
parameters from the field-definitions section as before.
```abap
*&---------------------------------------------------------------------*
*& Report : Z<PROGRAM_ID>
*& Title : <PROGRAM_NAME>
*&---------------------------------------------------------------------*
REPORT z<program_id>.
*& Selection screen
" Block / frame structure inferred from _selection_screen_layout.png
" Parameter list and types from _selection_definition.txt (canonical)
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.
" <one PARAMETERS or SELECT-OPTIONS line per Definition row>
PARAMETERS: p_bukrs TYPE bukrs OBLIGATORY,
p_werks TYPE werks_d OBLIGATORY,
p_matnr TYPE matnr.
SELECTION-SCREEN END OF BLOCK b1.
" If the image shows a second block, emit BLOCK b2 here.
" --------------------------------------------------------------------
" FORBIDDEN: do NOT emit an INITIALIZATION block that assigns to
" TEXT-NNN at runtime, e.g.
"
" INITIALIZATION.
" TEXT-001 = 'Material Upload Parameters'(s01).
"
" Modern ABAP (S/4HANA, strict mode) rejects this with
" "The field TEXT-001 cannot be modified". TEXT-NNN symbols are
" read-only at runtime. The frame title text must be defined via
" SE38 → Text Elements → Text Symbols → maintain TEXT-001 = "...".
" The `WITH FRAME TITLE TEXT-001` reference above is enough; no
" assignment statement is needed. The deploy skill (sap-se38) will
" populate the Text Symbols table after activation, OR a separate
" SE38 → Text Elements pass is required (the customer brief may
" specify whether to auto-populate or leave for manual translation).
" --------------------------------------------------------------------
*& Data declarations
DATA: ...
*& Main processing
START-OF-SELECTION.
PERFORM get_data.
PERFORM process_data.
PERFORM display_output.
FORM get_data. " <stub>
ENDFORM.
FORM process_data. " <stub>
ENDFORM.
FORM display_output. " <stub>
ENDFORM.
```
**Layout-image discrepancy handling:** If the image clearly shows a parameter
or block boundary that the Definition TSV lacks, do NOT silently invent code.
Stop and ask the user:
> "The selection-screen image shows a `<NAME>` field that's not in
> Selection Definition. Is this missing from the spec, or should I treat
> the image as outdated? [add to definition / ignore image / cancel]"
### 2c. Function Module (インターフェース設計書)
```abap
FUNCTION z<function_name>.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" <importing_params>
*" EXPORTING
*" <exporting_params>
*" TABLES
*" <table_params>
*" EXCEPTIONS
*" <exceptions>
*"----------------------------------------------------------------------
" TODO: implement
ENDFUNCTION.
```
---
## Step 3 — Output the Generated Code
1. **Display the generated ABAP** in a code block in the response.
2. **Offer to save** the code:
- Default path: save to the same work folder as `Z<PROGRAM_ID>.abap`
- Or `{WORK_TEMP}\Z<PROGRAM_ID>.abap` if no work folder
- Ask: "Save to `<path>`? (or specify another path)"
3. On confirmation, write **five sibling files** (per `abap_code_quality_rules.md` §16, §17, §20, §21):
- `Z<PROGRAM_ID>.abap` — the generated source
- `Z<PROGRAM_ID>.deps.txt` — dependency manifest (STANDARD_TABLES, BAPIS,
CLASSES, AUTHZ_OBJECTS, CUSTOM_OBJECTS sections; one name per line per
section). Hand to basis/security for TR scope + authorization design.
- `Z<PROGRAM_ID>.traceability.txt` — spec-section → ABAP-location map
(e.g. `[Validation #3] → lcl_main->validate (line 142)`). Audit-friendly
deliverable for regulated industries.
- `Z<PROGRAM_ID>.messages.txt` — message-class population for `/sap-se91`.
Tab-separated `<NNN>\t<E|W|I|S|A>\t<text with &1..&4 placeholders>`,
one per line. Generator emits this whenever it uses a `MESSAGE
eNNN(<msgclass>) WITH … INTO …` pattern (per rule §20). The deploy
pipeline runs `/sap-se91 update <msgclass> <messages.txt>` before
`/sap-se38` so the program references resolve.
- `Z<PROGRAM_ID>.text_elements.txt` — text-pool population for
`/sap-se38`. Two blocks per rule §21:
**`[SELECTION_TEXTS]` — source: `{doc_name}_selection_definition.txt`'s
`LABEL` column.** For every row in selection_definition, emit one line
`<DTEL_NAME><TAB><LABEL>`. The `LABEL` value is **already in the spec's
natural language** (Chinese for a CN spec, Japanese for a JA spec,
English for an EN spec) — copy it verbatim, do **NOT** translate it
to English. If `LABEL` is blank for a row, fall back to that
parameter's data-element short text (lookup via `_dataElements.txt`
or its DDIC short-text). Last resort only: omit the line and let
SE38 use the data-element short text at runtime.
**`[TEXT_SYMBOLS]` — source order:**
1. `{doc_name}_textElements.txt` if it has data rows
(`TEXT_ID<TAB>TEXT_VALUE`). A header-only file = no entries.
2. For every `TEXT-NNN` symbol your emitted source references but
`_textElements.txt` doesn't cover, derive a sensible label
from spec context — e.g. TEXT-001 (frame title of the main
selection block) ← `_PGM_summary.txt` "功能規格名 / 機能名 /
Functional Spec Name" line, or the program title; emit in
the spec's natural language.
**Language rule (hard):** the output language of both blocks MUST
match the spec's natural language. Never substitute the English
example below verbatim — it is a format illustration, not a content
template.
Format:
```
[SELECTION_TEXTS]
P_BUKRS <LABEL from selection_definition row for P_BUKRS>
P_WERKS <LABEL from selection_definition row for P_WERKS>
...
[TEXT_SYMBOLS]
001 <frame title for TEXT-001 in spec language>
002 <comment text for TEXT-002 in spec language>
```
`/sap-se38 update` (and `/sap-se38 create`) reads this after source
upload and applies entries via SE38 → Goto → Text Elements.
4. **MANDATORY when `MODE_UNIT_TESTS = TRUE`**: emit `Z<PROGRAM_ID>_TEST.abap`
containing `ltcl_main` with one `test_*` method per golden I/O row in the
spec's `_golden.txt` (or `== TEST CASES ==` section). Pre-fill
`cl_abap_unit_assert=>assert_*` calls.
This is a **mandatory output** (same tier as `<NAME>.text_elements.txt` and
`<NAME>.messages.txt`), NOT a soft "also emit". The 2026-05-27
`ZMMRMAT042R01` build silently dropped this file even though the customer
brief said `yes (mandatory)` — the "also" wording was easy to interpret
as optional. Treat as required when the flag is on.
**Emit a parseable status line as the last line of the generation report**
so callers (especially `abap-developer` Step 2e pre-deploy gate) can verify
without parsing the filesystem:
| Line | Meaning |
|---|---|
| `TEST_FILE: EMITTED Z<PROGRAM_ID>_TEST.abap methods=N` | OK — N test methods written, one per golden row. |
| `TEST_FILE: SKIPPED:MODE_OFF` | `MODE_UNIT_TESTS = FALSE`. No file expected. |
| `TEST_FILE: SKIPPED:NO_GOLDEN_ROWS` | Flag is on but `_golden.txt` has zero data rows. Emit a skeleton anyway with one TODO test and a clear comment; surface as WARN. |
| `TEST_FILE: FAILED:<reason>` | Generator could not produce the file (e.g. `IDENTIFIER_TOO_LONG` when a derived test-method name exceeds 30 chars). Caller MUST surface this — do not silently swallow. |
**Generator self-check before emitting `TEST_FILE: EMITTED`**: verify the
file actually exists on disk AND is non-empty AND contains
`CLASS ltcl_main` AND contains at least one `METHOD test_`. If any check
fails, emit `TEST_FILE: FAILED:SELF_CHECK_FAIL_<which>` instead. Trust the
marker line, not the LLM's claim of "I wrote it".
5. If `MODE_OOP = TRUE` and the project's `ZCX_<PROJ>_ERROR` exception class
does not yet exist (check via `/sap-se24 ZCX_<PROJ>_ERROR` or assume
absent), emit `ZCX_<PROJ>_ERROR.abap` boilerplate alongside the program
so deploy can pick it up.
6. Offer next steps:
- "Validate CALL FUNCTION signatures: `/sap-check-fm <file>` (catches
type-incompatible actual parameters BEFORE deploy; mandatory when
the generated source contains any `CALL FUNCTION 'GUI_UPLOAD'`,
`'GUI_DOWNLOAD'`, BAPI calls, or any FM with a `STRING` parameter)."
- "Validate code quality: `/sap-check-abap <file>`"
- "Deploy DDIC + ABAP: `/sap-se11`, then `/sap-se38 <program-name> <file>`"
- "Run ABAP Unit tests after deploy: `/sap-se38 <program-name>` then SE80 → Test"
- "Hand `<NAME>.deps.txt` to basis team for TR scope + auth design."
---
## Step 4 — Summary
After generating, provide a brief summary:
- Program name and type
- Number of fields extracted (by section)
- Mandatory / optional / conditional field counts
- Validation rules found
- Any assumptions made (e.g., message class defaulted to `MSGS`)
- Items needing manual completion (processing logic stubs, screen layout, GUI status)
End the log run. Use `SUCCESS` when files were written; use `SKIPPED` if
the user declined to save; use `FAILED` with `ErrorClass=GEN_ABAP_FAILED`
if generation was blocked (missing `_process.txt`, unsupported program
type, identifier too long after retry, etc.):
```bash
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action end -StateFile "{WORK_TEMP}\sap_gen_abap_run.json" -Status SUCCESS -ExitCode 0
```
---
## Notes
- **Program name prefix**: Always use `Z` or `Y` prefix for customer programs (e.g., `ZMTTMS0230`).
- **Never insert/update/delete standard SAP objects directly**.
- **Message class**: Extract from the process text if specified; default to `MSGS` if not found.
- **Screen numbers**: Default to `9000` for the main screen.
- **Full-width characters**: ABAP `TYPE c` stores single-byte characters. For Japanese text (Kanji/Kana), use `TYPE string` or ensure the system uses Unicode. Use `TYPE c LENGTH <n>` where n is the byte length, and add a comment noting the character type.
---
## Important: ABAP Identifier Maximum Lengths
SAP ABAP enforces strict maximum lengths on all identifiers. Exceeding these limits causes
syntax errors. **Always verify generated names fit within these limits.**
| Object Type | Max Length | Example |
|---|---|---|
| Program / Report name | 40 | `ZHKCM008R01` |
| Class name (local or global) | **30** | `lcl_material_upload` |
| Method name | **30** | `validate_record` |
| Interface name | 30 | `lif_processor` |
| Variable name (DATA) | **30** | `lv_line_count` |
| Type name (TYPES) | 30 | `ty_file_data` |
| Constant name (CONSTANTS) | 30 | `gc_max_rows` |
| Field-symbol name | 30 | `<ls_data>` (incl. `<>`) |
| FORM routine name | 30 | `get_data` |
| Function module name | 30 | `Z_MY_FUNC` |
| PARAMETERS | **8** | `P_BUKRS` (max 8 total) |
| SELECT-OPTIONS | **8** | `S_MATNR` (max 8 total) |
| Message class name | 20 | `ZHKA04` |
| Data element name | 30 | `ZHKDE_KEY1` |
| Domain name | 30 | `ZHKDM_KEY` |
| Table / Structure name | 30 | `ZHKFIXEDVALS8` |
**Critical — Selection screen parameters (PARAMETERS / SELECT-OPTIONS):**
- Maximum **8 characters total** including prefix (e.g., `P_` or `S_`)
- This means only **6 characters** are available after the standard prefix
- Example: `P_BUKRS` (7 chars) ✓, `P_MATERIAL` (10 chars) ✗ → use `P_MATNR` instead
**Critical — Method names (30 characters):**
- Unit test method names like `test_validate_xxxx` easily exceed 30 chars
- Keep test method names short
---
## Important: ABAP Source Line Length (≤ 72 characters)
Every emitted ABAP source line MUST be **at most 72 characters wide**,
counting column 1 inclusive (i.e. column 73 onward must be empty). This
is a HARD LIMIT applied to:
- Statement lines (`DATA`, `SELECT`, `CALL FUNCTION`, `IF`, `LOOP`, …)
- Comment lines (`*` full-line, `"` trailing)
- Section banners (the `*&---…---*` rules at the top of programs/forms)
- Selection-screen lines (`PARAMETERS`, `SELECT-OPTIONS`, `SELECTION-SCREEN`)
- Local-interface comment blocks for FMs (`*" IMPORTING …`)
### Why 72
- ABAP Editor's default column ruler is set at column 72; tooling
(SE80, ABAP Test Cockpit) flags overruns as a code-style warning.
- TR text-file diffs (E070 / `EPS_DELIVER_PACKAGE`) use a fixed-width
text grid; lines past 72 wrap awkwardly on package check.
- Long literal constants past column 72 trigger
`STATEMENT_TOO_LONG` / "Statement is too long" errors during the
syntax check on some legacy releases.
### Wrap rules
Apply these **while generating**, not after:
1. **Continue with a closing-quote-then-trailing-`&`** for string
literals:
```abap
lv_msg = 'Customer ' && lv_kunnr &&
' has been deleted on ' && lv_today.
```
Never split a literal mid-string with line continuations of
another shape.
2. **Break before keywords / commas**, never inside an identifier:
```abap
SELECT a~kunnr a~name1 b~stcd1
FROM kna1 AS a
INNER JOIN kna1_addr AS b
ON a~kunnr = b~kunnr
INTO TABLE @DATA(lt_customers)
WHERE a~spras = @sy-langu.
```
3. **Build CALL FUNCTION blocks** with one parameter binding per line,
each indented past the `EXPORTING` / `IMPORTING` / `TABLES` /
`EXCEPTIONS` keyword:
```abap
CALL FUNCTION 'Z_LOAD_DATA'
EXPORTING
iv_id = lv_id
iv_run_date = sy-datum
TABLES
ct_results = lt_results
EXCEPTIONS
not_found = 1
OTHERS = 2.
```
4. **Inline declarations (`DATA(...)`, `FIELD-SYMBOL(<...>)`)**: if the
surrounding statement won't fit in 72 chars with the inline form,
split the declaration to its own line above:
```abap
" too long:
" READ TABLE lt_customers ASSIGNING FIELD-SYMBOL(<fs_customer>) WITH KEY kunnr = lv_kunnr.
FIELD-SYMBOLS <fs_customer> LIKE LINE OF lt_customers.
READ TABLE lt_customers ASSIGNING <fs_customer>
WITH KEY kunnr = lv_kunnr.
```
5. **Comment headers** (`*&---…---*`): the rule line is exactly 70
`-` characters between the `*&` and the trailing `*` so the whole
line is exactly 72 characters wide. Match the existing program /
form banner template exactly — do NOT widen it to fit a long
subtitle.
6. **Long type / table names** (e.g. `lt_long_business_partner_table`):
prefer a `TYPES` alias once at the top of the section:
```abap
TYPES ty_partner_tab TYPE STANDARD TABLE OF zhk_business_partner.
DATA lt_partners TYPE ty_partner_tab.
```
7. **Selection-screen `COMMENT` lines** are the most common overrun
source. Use `TEXT-NNN` symbols (defined in text-element table E)
instead of inlining the literal — both keeps the line short AND
respects translation requirements.
### Enforcement checklist (run while emitting each line)
- [ ] Line length ≤ 72 visible characters? (count tabs as the
surrounding indent — the generator emits spaces, not tabs.)
- [ ] If wrapped: continuation indent matches the outer keyword's
column?
- [ ] No identifier split across lines?
- [ ] No string literal split into broken halves (use `&&` join
instead of mid-string break)?
- [ ] Comment banners at exactly 72 columns wide?
The downstream `/sap-check-abap` will flag overruns as
`STYLE_LINE_TOO_LONG` once the matching rule lands; until then this
skill is the front line. Do not rely on a manual post-pass.
---
## Important: ALV Output and SAP GUI Scripting Compatibility
When generating reports that will be automated via SAP GUI Scripting (sap-se38 skill):
- **WRITE output**: Can be captured via **"System > List > Save/Send > Local File"** menu in SAP GUI.
- **ALV output**: The ALV grid has its own **built-in export menu**.
- Do **NOT** add `GUI_DOWNLOAD` calls unless the design specification explicitly requires file export.
---
## Important: Data Type Safety for Generated Code
When generating ABAP code, prefer **standard SAP data types** that exist on all systems:
| Prefer | Instead of | Reason |
|---|---|---|
| `TYPE c LENGTH 2` | `TYPE mmsta_d` | `mmsta_d` may not exist on all systems |
| `TYPE c LENGTH 2` | `TYPE dismm` | Domain may not be in customer namespace |
| `TYPE matnr` | `TYPE matnr18` | `matnr18` is S/4HANA-specific |
For **selection screen parameters**, use the SAP data element (e.g., `TYPE bukrs`, `TYPE werks_d`, `TYPE matnr`) for F4 help. For **local variables**, use explicit `TYPE c LENGTH n` or `TYPE p DECIMALS d`.
---
## Important: Critical Runtime Error Prevention
- **Never use MESSAGE e/a/x type in local class methods** — E-type messages in methods cause `UNCAUGHT_EXCEPTION` short dumps. Use exception classes or RETURN with error flag instead.
- **Always use BAPI structure types directly** — Declare parameters using the exact BAPI structure type (e.g., `TYPE bapi_makt`). Never create custom types that mimic BAPI structures — causes `CALL_FUNCTION_CONFLICT_LENG`.
- **GUI_UPLOAD with TABLE OF char2048** — Use `TABLE OF char2048` for `data_tab` WITHOUT `has_field_separator = 'X'`. Parse tabs manually with `SPLIT ... AT cl_abap_char_utilities=>horizontal_tab`.
- **GUI_UPLOAD FILENAME requires `TYPE string`** — on S/4HANA 1909 (kernel 754, release 7.52) the formal `FILENAME` is typed `STRING`. Passing a `TYPE rlgrap-filename` / `c LENGTH n` actual activates fine but raises ATC P1 (SLIN: `CX_SY_DYN_CALL_ILLEGAL_TYPE` runtime risk). Always introduce a `DATA(lv_filename) = CONV string( p_file ).` adapter line and pass `lv_filename` — never pass the selection-screen `rlgrap-filename` parameter directly. The same trap applies to `GUI_DOWNLOAD FILENAME`. See rule §24's table for the full audit list.
---
## Important: ATC pre-emit checklist (final pass before saving the .abap)
After generating but BEFORE writing the file, walk this list. Every box
must be checked or the line must be rewritten. These map directly to ATC
findings the previous generation pipeline produced — each one is a known
P1/P2/P3 if left unchecked.
1. **No `SELECT *`** when the receiving usage reads less than 80% of the
columns. List columns explicitly. (Rule §12, ATC P1.)
2. **Every `AUTHORITY-CHECK`** lists ALL fields of the SU21 auth object
(use `DUMMY` for unused). Reference table in rule §14. (ATC P2.)
3. **No `LOOP AT itab WHERE … EXIT.`** for first-match lookups. Use
`READ TABLE … WITH KEY … TRANSPORTING NO FIELDS`. (Rule §19, ATC P3.)
4. **No `lv_msg = | hardcoded English { var } |`** for translatable
error text inside class methods. Use `MESSAGE eNNN(<msgclass>) WITH
… INTO lv_msg`. (Rule §20, ATC P3 — fires once per template.)
5. **Every `TEXT-NNN` reference** (frame title / comment) AND every
`PARAMETERS` / `SELECT-OPTIONS` line has a corresponding entry in
`Z<NAME>.text_elements.txt`. (Rule §21, ATC P3 — fires per missing
reference.)
6. **No assignment to `TEXT-NNN`** (the read-only-symbol rule). Reference
only; populate via the sibling text_elements file. (Existing rule.)
7. **Comma-separated SELECT lists when any `@`-host-var appears.** Modern
Open SQL (7.40 SP08+ strict mode) requires `SELECT a, b, c FROM t INTO
@lt WHERE k = @v.` — NOT `SELECT a b c FROM t INTO @lt WHERE k = @v.`.
Mixed syntax is a **compile-time error** (`SAPSQL_FIELDLIST_NO_COMMA`):
*"The elements in the 'SELECT LIST' list must be separated using
commas."* This fires the moment ANY clause in the statement uses an
`@`-escaped host variable — INTO, WHERE, GROUP BY, ORDER BY. Rule of
thumb: when you write `@`, write commas. `/sap-check-abap` reports
this as `SQL_STRICT_COMMA` ERROR severity — it will halt deploy if
left in the generated source.
8. **Every `CALL FUNCTION` actual parameter is type-compatible with its
formal.** Walk `_fm_signatures.txt` for every FM you call. For each
`<formal> = <actual>` binding, verify the actual's declared type
matches the formal's `TYPE_REF` — exact match, same DOMNAME, or
`LIKE` of a DDIC field with that ROLLNAME. If incompatible, declare
a local adapter (`DATA lv_x TYPE <formal_type_ref>.`) and pass that
instead. (Rule §24, ATC P1 — SLIN raises `CX_SY_DYN_CALL_ILLEGAL_TYPE`
for the mismatched call. The most-hit cases: `GUI_UPLOAD FILENAME`,
`GUI_DOWNLOAD FILENAME`, anything typed `STRING` receiving a fixed-
length char actual.)
and re-walk. Do not ship the file with any unchecked box — the
post-deploy ATC gate (`/sap-atc`) will fail at `MAX_PRIORITY=2` for
findings #1, #2, #8; warn for #3-#6.
---
## Important: Local Class Design for Test Classes
- Test classes should **never** call PRIVATE or PROTECTED methods directly
- Do NOT use `FRIENDS ltcl_xxx` — test through the **public interface**
- Pattern: call `execute()` with prepared input data, then verify results via public accessors
No comments yet. Be the first to comment!