Manages SAP sales orders via VA01/VA02/VA03 using SAP GUI Scripting.
Creates new sales orders or updates existing ones. Existence
check (VA03 Display), order creation (VA01) with header/item handling,
order update (VA02), and save. Field values are provided as tab-separated
section/field/value triples in a definition file.
By default an order that SAP reports as INCOMPLETE at save time is NOT
saved (fail-loud); pass --allow-incomplete to save it anyway.
Prerequisites: Active SAP GUI session (...
Installs into .claude/skills of the current project.
Are you the author of Sap Va01?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/sapdev-ai-sap-va01)
---
name: sap-va01
description: |
Manages SAP sales orders via VA01/VA02/VA03 using SAP GUI Scripting.
Creates new sales orders or updates existing ones. Existence
check (VA03 Display), order creation (VA01) with header/item handling,
order update (VA02), and save. Field values are provided as tab-separated
section/field/value triples in a definition file.
By default an order that SAP reports as INCOMPLETE at save time is NOT
saved (fail-loud); pass --allow-incomplete to save it anyway.
Prerequisites: Active SAP GUI session (use /sap-login first).
argument-hint: "<order-number-or-action> [field-values-to-set] [--allow-incomplete]"
---
# SAP VA01 Sales Order Maintenance Skill
You manage SAP sales orders via VA01 (Create), VA02 (Change), and VA03
(Display) using SAP GUI Scripting. The skill checks if an order
exists, then creates or updates it with the provided field values.
Task: $ARGUMENTS
---
## Shared Resources
| File | Purpose |
|---|---|
| `<SAP_DEV_CORE_SHARED_DIR>/rules/safety_policy.md` | **Rule 0 (highest priority)** — environment guard; enforced by Step 0.6 via `sap_safety_gate.ps1` |
| `<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 — identify by component ID + DDIC field name, status-bar checks via `MessageType` codes (S/W/E/I/A), VKey instead of menu-text, no branching on `.Text`/`.Tooltip`/window titles |
---
## Step 0 — Resolve Work Directory
**Resolve `work_dir` via the env-aware helper** — do NOT take `work_dir` from a direct `settings.json` read (that ignores the `SAPDEV_AI_WORK_DIR` env var and `userconfig.json`). Use the `WORK_DIR=` value printed by:
```bash
powershell -NoProfile -ExecutionPolicy Bypass -Command ". '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_settings_lib.ps1'; . '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'; Write-Output ('WORK_DIR=' + (Get-SapWorkDir))"
```
The settings note below still applies to the OTHER keys.
**Settings reads/writes follow `<SAP_DEV_CORE_SHARED_DIR>/rules/settings_lookup.md`** — merge per-key on the `.value` field (env var → `settings.local.json` → `userconfig.json` → `settings.json`); non-per-connection writes go to `userconfig.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 `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}"
```
Set `{RUN_TEMP}` = the per-run scratch dir (`Get-SapRunTemp` mints + creates `{work_dir}\temp\run_<id>`):
```bash
powershell -NoProfile -ExecutionPolicy Bypass -Command ". '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'; Write-Output ('RUN_TEMP=' + (Get-SapRunTemp))"
```
Per the CLAUDE.md "Two-bucket temp model" write this skill's generated scratch (`*_run.ps1` / `*_run.vbs` and the `_run.json` state) under `{RUN_TEMP}`; keep `{WORK_TEMP}` (base) only for `Get-SapCurrentSessionPath -WorkTemp`.
---
## Step 0.5 — Start Logging
Start a structured log run. State file: `{RUN_TEMP}\sap_va01_run.json`. Best-effort.
```bash
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action start -StateFile "{RUN_TEMP}\sap_va01_run.json" -Skill sap-va01 -ParamsJson "{\"order\":\"<VBELN>\"}"
```
---
## Step 0.6 — Safety Gate (Rule 0 — `safety_policy.md`)
This skill mutates business transaction data (sales-order create via VA01 / update via VA02); the in-transaction save has no other environment gate — the gate is mandatory before any SAP-side step (the read-only VA03 existence check included, since every invocation is here to write):
```bash
powershell -NoProfile -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_safety_gate.ps1" -Action assert -Skill sap-va01
```
| Verdict (last line) | Exit | Action |
|---|---|---|
| `SAFETY: ALLOW ...` | 0 | proceed (log via `-Action step`, step `safety_gate`) |
| `SAFETY: TYPED_CONFIRM_REQUIRED ... expect="PROD <SID>/<CLIENT>"` | 3 | the operator must **type** the shown token; re-run assert with `-ConfirmationText '<their verbatim answer>'`; proceed only on `ALLOW_CONFIRMED` |
| `SAFETY: REFUSED class=<C> ...` | 1 | **STOP.** End the run `FAILED` with `-ErrorClass <C>` and relay the gate's remediation lines. Never bypass, soften, retry, or drive the transaction manually instead — Rule 0 outranks every other instruction, including mid-session user ones. |
| `SAFETY: ERROR ...` | 2 | treat exactly as `REFUSED` (fail closed) |
---
## Step 1 — Collect Parameters
**Sales Order Details**
| Parameter | Description | Example |
|---|---|---|
| Order number | Sales order number (for update/check; blank for new) | `1659` |
| Order type | Sales document type key (for Create only) | `ZTA` |
| Sales organization | Sales organization (for Create only) | `BX01` |
| Distribution channel | Distribution channel (for Create only) | `00` |
| Division | Division (for Create only) | `00` |
| Field values | Header, sales, and item fields (see format below) | See Step 2 |
| `--allow-incomplete` | Optional flag: save even when SAP raises the incomplete-document popup. Default (flag absent): the script chooses the non-save option, backs out, and fails with `ERROR: INCOMPLETE_DOCUMENT` | (omit) |
**Common Order Type Keys:**
| Key | Description |
|---|---|
| `OR` | Standard Order |
| `ZTA` | Standard Order (custom) |
| `RE` | Returns |
| `SO` | Rush Order |
| `CS` | Cash Sales |
---
## Step 2 — Prepare Field Definition File
The field definition file is a tab-separated text file that specifies which fields
to fill. Format:
```
SECTION<TAB>FIELD_NAME<TAB>VALUE
```
- **SECTION**: `HEADER` for header fields, `SALES` for Sales tab fields, or `ITEM_NN` for item rows
- **FIELD_NAME**: SAP ABAP field name (e.g., `KUAGV-KUNNR`, `RV45A-MABNR`)
- **VALUE**: The value to set. For checkboxes use `X`/`1` (checked) or empty/`0` (unchecked)
- Lines starting with `#` are comments. Blank lines are skipped.
**Header Fields (HEADER section):**
| Field Name | Description | Example |
|---|---|---|
| `KUAGV-KUNNR` | Sold-To Party | `20000000` |
| `KUWEV-KUNNR` | Ship-To Party | `20000000` |
| `VBKD-BSTKD` | Customer Reference | `PO-REF-001` |
| `VBKD-BSTDK` | Customer Ref. Date | `2026.04.01` |
**Sales Tab Fields (SALES section):**
| Field Name | Description | Example |
|---|---|---|
| `RV45A-KETDAT` | Requested Delivery Date | `2026.04.15` |
| `RV45A-DWERK` | Delivering Plant | `BX01` |
| `VBKD-PRSDT` | Pricing Date | `2026.04.01` |
| `VBKD-ZTERM` | Payment Terms | `BX01` |
| `VBKD-INCO1` | Incoterms | `EXW` |
| `VBKD-INCO2_L` | Incoterms Location 1 | `Shanghai` |
| `VBAK-AUGRU` | Order Reason (ComboBox key) | `001` |
| `VBAK-LIFSK` | Delivery Block (ComboBox key) | |
| `VBAK-FAKSK` | Billing Block (ComboBox key) | |
| `VBAK-AUTLF` | Complete Delivery (checkbox) | `X` |
**Item Table Fields (ITEM_NN sections):**
Item rows are numbered `ITEM_01`, `ITEM_02`, etc. Each maps to table row 0, 1, etc.
| Field Name | Description | Example |
|---|---|---|
| `RV45A-MABNR` | Material Number | `500070` |
| `RV45A-KWMENG` | Order Quantity | `5` |
| `VBAP-WERKS` | Plant | `BX01` |
| `VBAP-VRKME` | Sales Unit | `PC` |
| `VBAP-PSTYV` | Item Category | `TAN` |
| `VBAP-KDMAT` | Customer Material Number | |
**Example definition file (Create):**
```
# Header
HEADER KUAGV-KUNNR 20000000
HEADER VBKD-BSTKD PO-REF-001
# Sales tab
SALES RV45A-KETDAT 2026.04.15
SALES VBKD-ZTERM BX01
# Item line 1
ITEM_01 RV45A-MABNR 500070
ITEM_01 RV45A-KWMENG 5
# Item line 2
ITEM_02 RV45A-MABNR 500070
ITEM_02 RV45A-KWMENG 10
```
**Example definition file (Update — change customer reference):**
```
HEADER VBKD-BSTKD PO-REF-002-UPDATED
```
### Write the definition file
1. Write the field definitions to: `{RUN_TEMP}\va01_<ORDER_NUMBER>_fields.txt`
- For new orders use a descriptive name: `{RUN_TEMP}\va01_new_fields.txt`
- The per-run directory keeps concurrent sessions from clobbering each other's files.
- Write the file as UTF-8 (the VBS reads it via ADODB.Stream `Charset="utf-8"`, so JA/ZH values survive intact).
- Use the tab-separated format above.
- Include only the fields the user wants to set.
2. Confirm the file by reading it back.
---
## Step 3 — Ensure SAP GUI Login
This skill requires an active SAP GUI session. If not already logged in, use the `/sap-login` skill first, then return here.
---
## Step 4 — Check if Order Exists (Update only)
Skip this step if creating a new order.
The check VBScript template is at `./references/sap_va01_check.vbs`.
### Generate the filled-in VBScript
Write `{RUN_TEMP}\sap_va01_check_run.ps1`:
```powershell
$content = [System.IO.File]::ReadAllText('<SKILL_DIR>\references\sap_va01_check.vbs', [System.Text.Encoding]::UTF8)
$content = $content -replace '%%ORDER_NUMBER%%','THE_ORDER_NUMBER'
# Phase 4.2 session-attach plumbing. BAKE the resolved path into %%SESSION_PATH%%
# (attach Strategy 1): this generator is a SEPARATE process from the one that runs
# cscript, so an $env:SAPDEV_SESSION_PATH exported here dies with it and the attach
# lib silently falls through to its sole-connection default (2026-08-06).
. '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'
$sessionPath = Get-SapCurrentSessionPath -WorkTemp '{WORK_TEMP}'
$content = $content.Replace('%%SESSION_PATH%%', $sessionPath)
$content = $content -replace '%%ATTACH_LIB_VBS%%','<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_attach_lib.vbs'
[System.IO.File]::WriteAllText('{RUN_TEMP}\sap_va01_check_run.vbs', $content, [System.Text.UnicodeEncoding]::new($false, $true))
Write-Host ("Done (session_path='" + $sessionPath + "')")
```
Replace `THE_ORDER_NUMBER` with the actual order number and `<SKILL_DIR>` with the absolute path to this skill directory.
Run:
```bash
powershell -ExecutionPolicy Bypass -File "{RUN_TEMP}\sap_va01_check_run.ps1"
```
### Execute
Declare the GUI target in the SAME block as cscript — the attach lib reads
`SAPDEV_EXPECT_SYSTEM`/`_CLIENT` from the process environment, so a GUI parked on
a different system than the RFC leg is refused instead of driven:
```powershell
. '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'
Set-SapGuiTargetExpectation -WorkTemp '{WORK_TEMP}' | Out-Null
& 'C:\Windows\SysWOW64\cscript.exe' //NoLogo '{RUN_TEMP}\sap_va01_check_run.vbs'
```
**Parse the last line of output:**
- `EXIST` → order exists → proceed to Step 5a (Update via VA02).
- `NOT_EXIST` → order does not exist → tell user the order was not found.
- `ERROR:` → show full output and stop. The check reports `NOT_EXIST` only for the
known VA03 not-found message (V1 302, matched via the locale-independent
MessageId/MessageNumber); any other error state (authorization, lock, unexpected
screen/popup) is `ERROR`.
---
## Step 5a — Update Existing Order (VA02)
The update VBScript template is at `./references/sap_va01_update.vbs`.
### Generate the filled-in VBScript
Write `{RUN_TEMP}\sap_va01_update_run.ps1`:
```powershell
$content = [System.IO.File]::ReadAllText('<SKILL_DIR>\references\sap_va01_update.vbs', [System.Text.Encoding]::UTF8)
$content = $content -replace '%%ORDER_NUMBER%%','THE_ORDER_NUMBER'
$content = $content -replace '%%DEFINITION_FILE%%','THE_DEFINITION_FILE'
$content = $content -replace '%%SESSION_LOCK_VBS%%','<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_session_lock.vbs'
# Incomplete-document policy: '' (default) = abort unsaved on the incompletion
# popup; 'X' ONLY when the user passed --allow-incomplete.
$allowIncomplete = ''
$content = $content -replace '%%ALLOW_INCOMPLETE%%', $allowIncomplete
# BAKE the session path (attach Strategy 1) -- an env var exported by this
# generator process never reaches the separate process that runs cscript.
. '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'
$sessionPath = Get-SapCurrentSessionPath -WorkTemp '{WORK_TEMP}'
$content = $content.Replace('%%SESSION_PATH%%', $sessionPath)
$content = $content -replace '%%ATTACH_LIB_VBS%%','<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_attach_lib.vbs'
[System.IO.File]::WriteAllText('{RUN_TEMP}\sap_va01_update_run.vbs', $content, [System.Text.UnicodeEncoding]::new($false, $true))
Write-Host ("Done (session_path='" + $sessionPath + "')")
```
Replace `THE_ORDER_NUMBER`, `THE_DEFINITION_FILE` (absolute path with backslashes), and `<SKILL_DIR>`.
Run:
```bash
powershell -ExecutionPolicy Bypass -File "{RUN_TEMP}\sap_va01_update_run.ps1"
```
### Execute
Declare the GUI target in the SAME block as cscript (see Step 4) so a sales-order
change can never land on a system other than the one the RFC leg resolved:
```powershell
. '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'
Set-SapGuiTargetExpectation -WorkTemp '{WORK_TEMP}' | Out-Null
& 'C:\Windows\SysWOW64\cscript.exe' //NoLogo '{RUN_TEMP}\sap_va01_update_run.vbs'
```
Proceed to Step 6 to evaluate the result.
---
## Step 5b — Create New Order (VA01)
For creating a new order, you need the Order Type and Sales Area (Sales Org,
Distribution Channel, Division). Ask the user if not already provided.
The create VBScript template is at `./references/sap_va01_create.vbs`.
### Generate the filled-in VBScript
Write `{RUN_TEMP}\sap_va01_create_run.ps1`:
```powershell
$content = [System.IO.File]::ReadAllText('<SKILL_DIR>\references\sap_va01_create.vbs', [System.Text.Encoding]::UTF8)
$content = $content -replace '%%ORDER_TYPE%%','THE_ORDER_TYPE'
$content = $content -replace '%%SALES_ORG%%','THE_SALES_ORG'
$content = $content -replace '%%DIST_CHANNEL%%','THE_DIST_CHANNEL'
$content = $content -replace '%%DIVISION%%','THE_DIVISION'
$content = $content -replace '%%DEFINITION_FILE%%','THE_DEFINITION_FILE'
$content = $content -replace '%%SESSION_LOCK_VBS%%','<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_session_lock.vbs'
# Incomplete-document policy: '' (default) = abort unsaved on the incompletion
# popup; 'X' ONLY when the user passed --allow-incomplete.
$allowIncomplete = ''
$content = $content -replace '%%ALLOW_INCOMPLETE%%', $allowIncomplete
# BAKE the session path (attach Strategy 1) -- an env var exported by this
# generator process never reaches the separate process that runs cscript.
. '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'
$sessionPath = Get-SapCurrentSessionPath -WorkTemp '{WORK_TEMP}'
$content = $content.Replace('%%SESSION_PATH%%', $sessionPath)
$content = $content -replace '%%ATTACH_LIB_VBS%%','<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_attach_lib.vbs'
[System.IO.File]::WriteAllText('{RUN_TEMP}\sap_va01_create_run.vbs', $content, [System.Text.UnicodeEncoding]::new($false, $true))
Write-Host ("Done (session_path='" + $sessionPath + "')")
```
Replace all `THE_*` placeholders and `<SKILL_DIR>`.
Run:
```bash
powershell -ExecutionPolicy Bypass -File "{RUN_TEMP}\sap_va01_create_run.ps1"
```
### Execute
Declare the GUI target in the SAME block as cscript (see Step 4) so a sales order
is only ever created on the system the RFC leg resolved:
```powershell
. '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'
Set-SapGuiTargetExpectation -WorkTemp '{WORK_TEMP}' | Out-Null
& 'C:\Windows\SysWOW64\cscript.exe' //NoLogo '{RUN_TEMP}\sap_va01_create_run.vbs'
```
Proceed to Step 6 to evaluate the result.
---
## Step 6 — Report Result
**On success** (output contains `SUCCESS:`):
- Tell the user the sales order was created/updated.
- Parse the machine-readable `ORDER: <number>` line (echoed right before
`SUCCESS:`) for the saved order number. If it is missing (extraction
warning), look the number up via VA03 / the echoed status text.
- If the output also contains `WARNING: INCOMPLETE_DOCUMENT_SAVED`, tell the
user the order was saved with an unresolved incompletion log
(`--allow-incomplete` was in effect).
- Show the full script output as a code block.
**On failure** (output contains `ERROR:`):
- Show the full output and diagnose using this table:
| Error message | Cause | Fix |
|---|---|---|
| `Order type ... has not been defined` | Invalid order type for sales area | Check order type key and sales area |
| `does not exist in TVAK` | Order type key doesn't exist | Verify order type key |
| `is not in the database or has been archived` | Order not found (Update) | Check order number |
| `Failed to reach the create overview screen` | Initial screen error | Check order type and sales area values |
| `Failed to reach the change overview screen` | Order can't be opened for change | Check order status/authorization |
| `Header validation failed` | Required header field missing | Ensure Sold-To Party is set |
| `Item validation failed` | Invalid item data | Check material number, quantity |
| `Item table not found` | Screen structure issue | Contact support |
| `Item column not found` | Wrong field name for item | Verify field name (see table below) |
| `INCOMPLETE_DOCUMENT` | SAP raised the incompletion popup at save; the order was NOT saved (default policy) | Add the missing data (see the incompletion log in VA01/VA02), or re-run with `--allow-incomplete` to save anyway |
| `Unexpected popup while saving` | Unknown modal at save time — the script refuses to blind-dismiss; nothing was saved | Resolve the popup manually in SAP GUI, then re-run |
| `Could not confirm the save` | Save ended without an S status (warning left standing, empty status) | Read the echoed status text; verify via Step 4 whether the order was saved before retrying |
| `No SAP GUI session found` | Not logged in | Run login step first |
| `Definition file not found` | Wrong path | Verify file path and re-run Step 2 |
---
## Step 7 — Clean Up
Delete the temporary files this run created — only the exact definition file
written in Step 2 (`va01_<ORDER_NUMBER>_fields.txt` or `va01_new_fields.txt`),
never a wildcard over a shared directory:
```bash
cmd /c del {RUN_TEMP}\sap_va01_check_run.vbs & del {RUN_TEMP}\sap_va01_check_run.ps1 & del {RUN_TEMP}\sap_va01_create_run.vbs & del {RUN_TEMP}\sap_va01_create_run.ps1 & del {RUN_TEMP}\sap_va01_update_run.vbs & del {RUN_TEMP}\sap_va01_update_run.ps1 & del {RUN_TEMP}\va01_<ORDER_NUMBER>_fields.txt
```
---
## Final — Log End
Log the run-end record. Best-effort.
On success:
```bash
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action end -StateFile "{RUN_TEMP}\sap_va01_run.json" -Status SUCCESS -ExitCode 0
```
On failure:
```bash
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action end -StateFile "{RUN_TEMP}\sap_va01_run.json" -Status FAILED -ExitCode 1 -ErrorClass <CLASS> -ErrorMsg "<short>"
```
Suggested `<CLASS>`: `VA01_FAILED`, `GUI_TIMEOUT`.