Skip to content
Back to skills

Sap Gen Abap

ASecurity

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.

  • 7 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 27, 2026
toolsrustgoshellbashsqldebuggingapisecurityperformance

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add sapdev-ai/sap-dev --skill sap-gen-abap --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Sap Gen Abap?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Sap Gen Abap
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/sapdev-ai-sap-gen-abap/badge)](https://www.skillsdirectory.com/skills/sapdev-ai-sap-gen-abap)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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

Files in this skill

  • README.md3.9 KB
  • SKILL.md51.1 KB
  • references/sample_ZMTTMS0230.abap7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.