界面已本地化;技能介绍与说明文档按所选语言由 AI 自动翻译,代码、命令、路径和专业术语保留原文,以原作者文档为准。
探索技能
文档与办公 · SKILL.md

s4hana-update-record

s4hana-update-record

【文档与办公】Update existing records in SAP S/4HANA Cloud Public or on-prem private edition via OData V2 PATCH. Use whenever the user wants to update, change, edit, modify, patch, set, rename, adjust, or correct any existing record on S/4HANA — purchase orders (dates, payment terms, item text/qty/price), business partners (name, address, payment terms, recon acco…

独立 GitHub 快照 · 自动分类 · 以原作者文档为准

技能说明

GitHub

name: s4hana-update-record description: Update existing records in SAP S/4HANA Cloud Public or on-prem private edition via OData V2 PATCH. Use whenever the user wants to update, change, edit, modify, patch, set, rename, adjust, or correct any existing record on S/4HANA — purchase orders (dates, payment terms, item text/qty/price), business partners (name, address, payment terms, recon account), product descriptions (Toblerone-style overrides), service entry sheets (name, qty before approval), purchasing info records, supplier purchasing-org assignments, etc. Triggers on phrases like "update PO X", "change supplier address", "rename product", "backdate PO 4500...", "adjust PIR price for supplier Y", "set new payment terms for X". Handles GET-before-PATCH (to show current state), composite key resolution, CSRF flow, the "204 looks-like-success-but-no-change" trap (verifies via GET-after-PATCH), write-once field detection, and entity-disabled checks. Do NOT use for CREATE operations (use the s4hana-create-* skills), DELETE (most entities don't support DELETE on Cloud Public — block + rename instead), or for entities where update is entirely disabled (Supplier Invoice and Material Document — those return 405 CX_SADL_ENTITY_CUD_DISABLED and need cancel-recreate workflows).

s4hana-update-record

Generic update skill for SAP S/4HANA records. Verified pattern against <your-tenant>.s4hana.cloud.sap 2026-05-12 — 6 different entity types successfully PATCH'd; 2 confirmed entity-disabled; 1 "silent success" trap documented.

When to trigger

Verbs: update / change / edit / modify / patch / set / rename / adjust / correct / backdate / amend Objects: any existing S/4HANA record — PO, supplier, customer, BP, PIR, SES, product description, etc.

When NOT to trigger

  • User wants to CREATE a record → use s4hana-create-* skills
  • User wants to DELETE → Cloud Public blocks DELETE on most entities. Use block-and-rename (PATCH BusinessPartnerIsBlocked=true etc.) instead.
  • User wants to update Supplier Invoice or Material Document → those are entity-disabled (405). They need cancel-and-recreate (use the cancellation flow in the relevant create skill instead).

Hard rules (never violate)

  1. GET before PATCH, GET after PATCH. Always show user the BEFORE state, then PATCH, then VERIFY the change actually took effect with a fresh GET. Many fields return 204 "success" but don't actually update — only the post-PATCH GET reveals the truth.
  2. Confirm with user before PATCHing batches >3. Show the planned BEFORE/AFTER diff for the first record, get explicit confirmation, then proceed.
  3. If post-PATCH GET shows unchanged value, surface it as a "silent failure" — DON'T claim success.
  4. Never invent values. If the user says "set PO X date to Aug 2025", use a specific date they confirmed, not a random one.
  5. Scripts go in mcp-server/src/sap/.tmp/<user>-<YYYYMMDD-HHMM>/update-<entity>/. Never commit, never modify .env.

Phase 0 — Setup check (MANDATORY, no exceptions)

Before any API call, before any tool use, do this check. Even if you already know credentials from earlier in the conversation — IGNORE that knowledge and re-check from scratch each invocation.

  1. Announce: tell the user "Checking for SAP credentials in <current cwd>..."
  2. Check ONLY these two sources:
    • A .env file at ./.env (in the current working directory — NOT parent dirs, NOT ~/.env, NOT any memory file)
    • Shell-exported env vars: SAP_HOST AND SAP_AUTH_MODE both present
  3. If neither: auto-create ./.env from the bundled template (curl from https://raw.githubusercontent.com/ilia-inovaflow/s4hana-create-record-skills/main/.env.example), append .env to .gitignore if not already there, tell the user clearly what to fill in (auth mode + host + creds), and wait for them to say "ready" before making any API call. Never overwrite an existing .env.
  4. If credentials are present: report back to the user: "✓ Loaded credentials for <host> in <auth-mode> mode. Proceeding with [task]."

Never use credentials from conversation memory, from another project's .env, or from any tenant the user previously worked with in a different session. Each project gets its own .env. See shared/setup-check.md for the full rationale and edge cases.

Phases

Phase 1 — Identify entity + record + field(s)

Parse the user's intent:

  • Entity: PO, BP, PIR, etc. — map to OData service + entity set.
  • Record key(s): Single-key (e.g. PurchaseOrder='4500000020') or composite (e.g. PIR org-plant has 4 keys).
  • Fields: What does the user want changed? Translate domain language ("payment terms") to API field names (PaymentTerms).

If ambiguous, ask once before proceeding. Show the user your planned interpretation: "I'm going to update PO 4500000020's PurchaseOrderDate to 2025-08-15. Correct?"

For known entities, use the table below to find the right endpoint. For unknown entities, switch to research mode (Phase 1b).

Phase 1b — Research mode (for unknown entities)

If the entity isn't in the verified table:

  1. Identify the OData service via $metadata lookup or search SAP API Hub.
  2. Fetch the entity's $metadata and inspect:
  • Key fields: which fields make up the primary key
  • sap:updatable="false" on properties (write-once fields)
  • sap:creatable annotations (which fields appear in PATCH vs POST)
  1. Try minimal PATCH, iterate on errors.
  2. Document the working pattern in references/mutable-fields-catalog.md for next time.

Phase 2 — GET current state (before)

GET /sap/opu/odata/sap/<SERVICE>/<EntityKey>?$select=<key fields + target fields>&$format=json
Authorization: Basic <auth>
Accept: application/json

Show user: "Current value of PurchaseOrderDate is 2026-04-30. Want me to change it to 2025-08-15?"

For batch operations, GET each record (or a sample) and show the user a table of planned changes before any PATCH.

Phase 3 — PATCH

PATCH /sap/opu/odata/sap/<SERVICE>/<EntityKey>?sap-client=<n>
Authorization: Basic <auth>
X-CSRF-Token: <captured>
Cookie: <captured>
Content-Type: application/json
Accept: application/json

{"<field>": "<new value>"}

PATCH semantics: partial update. Only send the field(s) you want to change — don't echo the full record. Body should be minimal.

Expected response: HTTP 204 No Content (success) or HTTP 400 with error.innererror.errordetails for failures.

Phase 4 — GET to verify (after)

GET /sap/opu/odata/sap/<SERVICE>/<EntityKey>?$select=<target fields>&$format=json

Compare BEFORE → AFTER for each field. Report to user:

  • ✅ Changed as expected: PurchaseOrderDate: 2026-04-30 → 2025-08-15
  • ❌ Unchanged despite 204: NetPriceAmount: 50.00 → 50.00 (silent failure — field may be read-only despite 204 response)
  • ❌ Rejected: log the error details

Phase 5 — Report

After single or batch: summarize changes, surface any silent failures or rejections, write log to disk.

Output structure

mcp-server/src/sap/.tmp/<user>-<YYYYMMDD-HHMM>/update-<entity>/
├── targets.json # records to update + proposed changes
├── before.json # GET-before snapshots
├── update-log.jsonl # one line per PATCH attempt
├── after.json # GET-after snapshots
├── diff.md # human-readable before/after table
└── results.json

Verified update patterns (mutable-fields catalog)

See references/mutable-fields-catalog.md for the full per-entity catalog. Quick summary:

EntityServiceWhat worksNotes
A_BusinessPartnerAPI_BUSINESS_PARTNEROrganizationBPName1 and most header fieldsSingle key: BusinessPartner
A_BusinessPartnerAddressAPI_BUSINESS_PARTNERCityName, StreetName, PostalCode, CountryComposite key: BusinessPartner + AddressID
A_ProductDescriptionAPI_PRODUCT_SRVProductDescriptionComposite key: Product + Language. Useful for Toblerone-style overrides
A_PurchaseOrderAPI_PURCHASEORDER_PROCESS_SRVPurchaseOrderDate, PaymentTerms, header text, most header fieldsNOT Supplier, CompanyCode, PurchasingOrganization — write-once
A_PurchaseOrderItemAPI_PURCHASEORDER_PROCESS_SRVPurchaseOrderItemText, NetPriceAmount, OrderQuantity, Plant, StorageLocationNOT Material, PurchaseOrderItemCategory — write-once
A_ServiceEntrySheetAPI_SERVICE_ENTRY_SHEET_SRVServiceEntrySheetName, PostingDate (pre-approval)After approval (workflow status changes), most fields lock
A_PurgInfoRecdOrgPlantDataAPI_INFORECORD_PROCESS_SRVStatus fields, validity datesTRAP: NetPriceAmount returns 204 but DOES NOT change — see known-traps.md. Price is in to_PurInfoRecdPrcgCndnValidity
A_SupplierPurchasingOrgAPI_BUSINESS_PARTNERPaymentTerms, PurchaseOrderCurrencyComposite key: Supplier + PurchasingOrganization
A_SupplierCompanyAPI_BUSINESS_PARTNERPaymentTerms, ReconciliationAccountComposite key: Supplier + CompanyCode

Entity-disabled (PATCH always fails)

Don't even try — these entities reject all PATCH with 405 CX_SADL_ENTITY_CUD_DISABLED. They need cancel-and-recreate flows:

EntityWhyAlternative
A_SupplierInvoicePosted invoices are immutable for auditCancel via Cancel_SupplierInvoice action + create new
A_MaterialDocumentHeader (GR)Material movements are immutable for inventory integrityReverse via movement type 102 (creates negative MatDoc)
A_ProductCloud Public locks Product master to MDGUpdate via A_ProductDescription for description; A_ProductPlant for plant data

Reference files

  • references/mutable-fields-catalog.md — full per-entity table of what works
  • references/known-traps.md — the "204 looks like success but didn't change" cases
  • references/key-resolution.md — how to construct keys for composite-key entities (PIR org-plant, BP address, etc.)
  • references/cancel-vs-update.md — when to cancel-recreate vs PATCH
  • scripts/safe-update.mjs — reference implementation with GET-PATCH-GET verify flow + dry-run mode
我的收藏