logo

Are you need IT Support Engineer? Free Consultant

Reversing the 5HP NBIC Intercompany STO in SAP S/4HANA Cloud Public Edition

  • By Sanjay
  • 19/10/2026
  • 10 Views



Scope Item: 5HP — Advanced Intercompany Stock Transfer
Document Type: NBIC (Cross-Company, Delivery-Linked)
Verified on: SAP S/4HANA Cloud Public Edition

Test Date: 2026-09-22 / 2026-10-19


1. Why 5HP Reversal Has No Standard Path

5HP uses the NBIC purchase order type. Three constraints shape the reversal problem:

  1. No Return STO for NBIC. SAP has confirmed that the aICO framework does not support a Return STO document type. The standard Return PO path for external supplier returns does not apply to intercompany transfers — the financial and stock account logic is fundamentally different.

  2. The delivery chain is mandatory in both directions. Because NBIC requires OBD → PGI → IBD → GR in the forward flow, the reversal must mirror this in strict reverse. Skipping or reordering steps produces VL/066 locks or status inconsistencies.

  3. Two WL-linked goods movements require UI intervention. The GRRV (MT107, WL-linked blocked stock GR) and the GIIV (Post GI from Issuing SiT, WL-linked) cannot be cancelled via OData API. Both must be cancelled through the Monitor Value Chains Fiori app before the delivery documents can be deleted.


2. Verified Reversal Sequence

Document chain (Full 5HP variant):

STO [NBIC — Plant 1310 → Plant 1320]
└─→ OBD
│ PGI material documents:
│ ├── MT681 (GIOD): Plant 1310 unrestricted -1, transit +1
│ └── MT107 (GRRV, WL-linked): Plant 1320 GR blocked +1
├─→ IV2 [auto-generated after PGI]
└─→ IBD [auto-generated after PGI]
│ IBD GR material documents (single GR posting):
│ ├── MT685 (GIIV, WL-linked): Plant 1310 SiT -1
│ ├── MT107 (GRRV, WL-linked): Plant 1320 GR blocked +1
│ └── MT109 (GRID): Plant 1320 GR blocked -1 → unrestricted +1

Full 5HP identification: In Monitor Value Chains, check whether a “Post GI from Issuing SiT (GIIV)” step appears after PGI. If yes, the IBD GR creates all three material documents (GIIV + GRRV + GRID) together. If not, this is the simple variant and only MT109 (GRID) needs to be reversed in Step 2.

Reversal execution order:

Step 1 — Cancel IV2 billing document                    [API ✅]
Step 2 — ReverseGoodsReceipt on IBD [API ✅ — reverses MT109 + MT685 atomically]
Step 3 — Cancel GRRV (MT107, WL-linked) [UI ✅ — Monitor Value Chains, after Step 2]
Step 4 — Cancel GIIV (Post GI from Issuing SiT) [UI ✅ — Monitor Value Chains, after Step 3]
Step 5 — Delete IBD [API ✅ — HTTP 204, Steps 3+4 required first]
Step 6 — ReverseGoodsIssue on OBD [API ✅ — after Step 5]
Step 7 — Delete OBD [API ✅ — HTTP 204, after Step 6]
Step 8 — SetItemToDeleted on STO item [API ✅]

3. Authentication and Session Architecture

All write operations use a Communication User via Basic Auth, provisioned through the relevant Communication Arrangement (SAP_COM_0022 for Delivery Processing Integration, SAP_COM_0006 for Material Documents, SAP_COM_0A80 for Stock Transport Orders, SAP_COM_0A95 for Billing Documents, SAP_COM_0734 for Outbound Delivery GI).

$user      = “SALES_AGENT_API”
$pass=”<your-password>”
$base = “https://<your-tenant>-api.s4hana.cloud.sap”
$cookieJar = “$env:TEMP\session_$(Get-Date -Format ‘HHmmss').txt”

$ibdRoot = “$base/sap/opu/odata/sap/API_INBOUND_DELIVERY_SRV;v=2”
$obdRoot = “$base/sap/opu/odata/sap/API_OUTBOUND_DELIVERY_SRV;v=2”
$stoRoot = “$base/sap/opu/odata4/sap/api_stocktransportorder/srvd_a2x/sap/stocktransportorder/0001”

Session rule: Every POST must use curl -c/-b <cookie-jar> to maintain session continuity. The CSRF token fetch, entity GET, and FunctionImport POST must share the same HTTP session.

CSRF token fetch template:

# Fetch token against the target service root entity set (not base URL)
$fetchOut = & curl.exe -s -D – -o NUL `
-u “${user}:${pass}” `
-H “x-csrf-token: fetch” `
-H “Accept: application/json” `
-c $cookieJar `
“$ibdRoot/A_InbDeliveryHeader”
$csrf = ($fetchOut | Select-String “(?i)x-csrf-token:\s*(.+)”).Matches.Groups[1].Value.Trim()

Each service (API_INBOUND_DELIVERY_SRV, API_OUTBOUND_DELIVERY_SRV, api_stocktransportorder) requires its own CSRF token fetched in a separate session. Do not reuse a token across services.


4. Step 1 — Cancel Intercompany Invoice (IV2)

Fiori: Manage Billing Documents → select IV2 → Cancel

API:

GET /sap/opu/odata/sap/API_BILLING_DOCUMENT_SRV/A_BillingDocument(‘<IV2_NUMBER>')
?$format=json
# Extract __metadata.etag from response

POST /sap/opu/odata/sap/API_BILLING_DOCUMENT_SRV/Cancel?BillingDocument=”<IV2_NUMBER>”
&$format=json
x-csrf-token: <fetched>
If-Match: <etag_from_GET>
Accept: application/json

⚠️ETag required: The Cancel FunctionImport returns HTTP 428 if If-Match is missing. Always do a GET first to retrieve the ETag, then pass it in the POST. Pass BillingDocument as a query string parameter — embedding it in the URL path returns HTTP 404.

Check all IV2 documents for the OBD before cancelling:

GET /sap/opu/odata/sap/API_BILLING_DOCUMENT_SRV/A_BillingDocumentItem
?$filter=ReferenceSDDocument eq ‘<OBD_NUMBER>'
&$select=BillingDocument,BillingDocumentItem,ReferenceSDDocument
&$format=json

Then verify each billing document's status:

GET /sap/opu/odata/sap/API_BILLING_DOCUMENT_SRV/A_BillingDocument(‘<BillingDocument>')
?$select=BillingDocument,BillingDocumentType,OverallBillingStatus,CancelledBillingDocument
&$format=json
  • BillingDocumentType = IV2, OverallBillingStatus = C → active invoice, cancel this
  • BillingDocumentType = IV2S → cancellation document (expected, no action)
  • BillingDocumentType = IV2 with empty CancelledBillingDocument and OverallBillingStatus = A → re-created by background job, also cancel this

All IV2 documents must be in cancelled state before Step 6.

⚠️Billing re-creation trap: After cancelling IV2, the OBD's OverallIntcoBillingStatus resets to A, making it eligible for re-billing. If a background billing job (BILLING_DOCUMENT_RUN) runs before Step 6, it will create a new IV2 and block ReverseGoodsIssue again. Execute Steps 1 through 6 in a single uninterrupted session — no idle time between them.


5. Step 2 — Reverse Goods Receipt on IBD (GRID + GIIV)

Service: API_INBOUND_DELIVERY_SRV (OData V2)

Prerequisite: IBD OverallGoodsMovementStatus = C

In the Full 5HP variant, this single API call atomically reverses:

  • MT109 (GRID): removes stock from receiving plant unrestricted inventory
  • MT685 (GIIV): restores SiT +1 at issuing plant (required for Step 6 to succeed)

The WL-linked MT107 (GRRV) is intentionally not reversed here — it is handled in Step 3.

$ibd = “<IBD_NUMBER>”

# Fetch CSRF token for IBD service
$fetchOut = & curl.exe -s -D – -o NUL `
-u “${user}:${pass}” `
-H “x-csrf-token: fetch” `
-H “Accept: application/json” `
-c $cookieJar `
“$ibdRoot/A_InbDeliveryHeader”
$csrf = ($fetchOut | Select-String “(?i)x-csrf-token:\s*(.+)”).Matches.Groups[1].Value.Trim()

# ReverseGoodsReceipt — do NOT include If-Match (returns HTTP 501 if added)
& curl.exe -s -X POST `
-u “${user}:${pass}” `
-H “x-csrf-token: $csrf” `
-H “Accept: application/json” `
-b $cookieJar -c $cookieJar `
-w “`nHTTP:%{http_code}” `
“$ibdRoot/ReverseGoodsReceipt?DeliveryDocument=”${ibd}”&ActualGoodsMovementDate=datetime'<REVERSAL_DATE>T00%3A00%3A00'&`$format=json”
# Expected: HTTP 200, IBD OverallGoodsMovementStatus → A

Verified response (HTTP 200):

{ “d”: { “results”: [] } }

An empty results array confirms clean reversal with no warnings.

⚠️Critical — never cancel GRID directly: Cancelling MT109 via Monitor Value Chains or API_MATERIAL_DOCUMENT_SRV without going through ReverseGoodsReceipt leaves MT685 (GIIV) unreversed. This depletes SiT to 0 at the issuing plant, causing VL/624 on the subsequent ReverseGoodsIssue call. The chain becomes unrecoverable through standard paths.


6. Step 3 — Cancel WL-linked Blocked Stock (GRRV / MT107)

Fiori: Monitor Value Chains → navigate to the GRRV (MT107) document → Cancel

⚠️The Cancel button only appears AFTER Step 2 (ReverseGoodsReceipt) is complete. It is not present before that — do not look for it earlier.

This step removes the WL-linked GR blocked stock at the receiving plant. It must be performed before Step 5 (Delete IBD) and before Steps 5–7 can succeed.

API status: ❌ M7/492 — WL-linked document cancellation via API_MATERIAL_DOCUMENT_SRV is blocked. Monitor Value Chains is the only viable path.


7. Step 4 — Cancel Post GI from Issuing SiT (GIIV)

Fiori: Monitor Value Chains → navigate to the GIIV document → Cancel

Same pattern as Step 3. While in Monitor Value Chains on the STO header, find the GIIV row in the Documents panel, click its Navigation arrow button to open the detail view, then use the Cancel button in the header toolbar.

This step cancels the GIIV process step. It must be completed (together with Step 3) before Steps 5–7 — without it, OBD OverallGoodsMvtStatus remains C after Step 6, causing Step 7 DELETE to fail with VL/066.

API status: ❌ — no OData equivalent. Monitor Value Chains UI Cancel is the only path.

⚠️UI5 button behavior: Standard DOM element.click() silently fails on SAP UI5 controls. Use Playwright browser_click via snapshot ref (real mousedown/mouseup) or sap.ui.getCore().byId(id).firePress() as fallback.


8. Step 5 — Delete Inbound Delivery

Prerequisite: Steps 3+4 (GRRV and GIIV Monitor Value Chain Cancels) complete. IBD OverallGoodsMovementStatus = A.

API: ✅ DELETE via API_INBOUND_DELIVERY_SRV with If-Match: * (HTTP 204).

$ibd = “<IBD_NUMBER>”
$cookieJarIBD = “$env:TEMP\ibd_$(Get-Date -Format ‘HHmmss').txt”

# Fetch CSRF token
$fetchOut = & curl.exe -s -D – -o NUL `
-u “${user}:${pass}” `
-H “x-csrf-token: fetch” `
-H “Accept: application/json” `
-c $cookieJarIBD `
“$ibdRoot/A_InbDeliveryHeader”
$csrf = ($fetchOut | Select-String “(?i)x-csrf-token:\s*(.+)”).Matches.Groups[1].Value.Trim()

# DELETE IBD
& curl.exe -s -X DELETE `
-u “${user}:${pass}” `
-H “x-csrf-token: $csrf” `
-H “If-Match: *” `
-H “Accept: application/json” `
-b $cookieJarIBD -c $cookieJarIBD `
-w “`nHTTP_STATUS:%{http_code}” `
“$ibdRoot/A_InbDeliveryHeader(‘${ibd}')”
# Expected: HTTP 204 (no body)

Verify deletion: GET the IBD → expect HTTP 404 “Resource not found”.

⚠️VL/066 on DELETE means Steps 3+4 were incomplete. Complete GRRV and GIIV Monitor Value Chain Cancels first, then retry.


9. Step 6 — Reverse Goods Issue on OBD

Service: API_OUTBOUND_DELIVERY_SRV (OData V2)
Prerequisites:

  • Step 2 (GR Reversal) complete: IBD OverallGoodsMovementStatus = A
  • Steps 3+4 (GRRV and GIIV cancel) complete
  • Step 5 (IBD deleted) complete
  • Step 1 (IV2 cancelled) complete: OBD OverallIntcoBillingStatus = A

Verification before calling:

# Confirm OBD billing status is A and all IV2 documents are cancelled
& curl.exe -s -u “${user}:${pass}” `
-H “Accept: application/json” `
“$obdRoot/A_OutbDeliveryHeader(‘<OBD_NUMBER>')?`$select=OverallIntcoBillingStatus,OverallGoodsMvtStatus&`$format=json”
# Required: OverallIntcoBillingStatus=”A”

API call:

$obd = “<OBD_NUMBER>”

$cookieJarOBD = “$env:TEMP\obd_$(Get-Date -Format ‘HHmmss').txt”
$fetchOut = & curl.exe -s -D – -o NUL `
-u “${user}:${pass}” `
-H “x-csrf-token: fetch” `
-H “Accept: application/json” `
-c $cookieJarOBD `
“$obdRoot/A_OutbDeliveryHeader”
$csrf = ($fetchOut | Select-String “(?i)x-csrf-token:\s*(.+)”).Matches.Groups[1].Value.Trim()

# ReverseGoodsIssue — do NOT include If-Match (returns HTTP 501 if added)
& curl.exe -s -X POST `
-u “${user}:${pass}” `
-H “x-csrf-token: $csrf” `
-H “Accept: application/json” `
-b $cookieJarOBD -c $cookieJarOBD `
-w “`nHTTP_STATUS:%{http_code}” `
“$obdRoot/ReverseGoodsIssue?DeliveryDocument=”${obd}”&ActualGoodsMovementDate=datetime'<REVERSAL_DATE>T00:00:00'&`$format=json”
# Expected: HTTP 200

Fiori alternative (after IBD deleted): Reverse GI – Outbound Delivery (VL09) also becomes available once the IBD is deleted in Step 5.

🔑OBD status after this call: When Steps 3+4 Monitor Value Chain Cancels were completed first, OBD.OverallGoodsMvtStatus correctly updates C→A after this call, unblocking Step 7 DELETE. If status stays C, Steps 3 or 4 were incomplete — complete them and retry.


10. Step 7 — Delete Outbound Delivery

Prerequisite: Step 6 (GI reversed) complete. OBD OverallGoodsMvtStatus = A.

API: ✅ DELETE via API_OUTBOUND_DELIVERY_SRV with If-Match: * (HTTP 204).

$obd = “<OBD_NUMBER>”
$cookieJarOBD2 = “$env:TEMP\obd2_$(Get-Date -Format ‘HHmmss').txt”

# Fetch CSRF token
$fetchOut = & curl.exe -s -D – -o NUL `
-u “${user}:${pass}” `
-H “x-csrf-token: fetch” `
-H “Accept: application/json” `
-c $cookieJarOBD2 `
“$obdRoot/A_OutbDeliveryHeader”
$csrf = ($fetchOut | Select-String “(?i)x-csrf-token:\s*(.+)”).Matches.Groups[1].Value.Trim()

# DELETE OBD
& curl.exe -s -X DELETE `
-u “${user}:${pass}” `
-H “x-csrf-token: $csrf” `
-H “If-Match: *” `
-H “Accept: application/json” `
-b $cookieJarOBD2 -c $cookieJarOBD2 `
-w “`nHTTP_STATUS:%{http_code}” `
“$obdRoot/A_OutbDeliveryHeader(‘${obd}')”
# Expected: HTTP 204 (no body)

Verify deletion: GET the OBD → expect HTTP 404.

⚠️If VL/066 appears: OBD status is still C — Steps 3 and/or 4 Monitor Value Chain Cancels were not completed. Go back and complete Steps 3+4, retry Step 6 before retrying Step 7.


11. Step 8 — Set STO Item to Deleted

Service: api_stocktransportorder (OData V4)

$sto  = “<STO_NUMBER>”
$item = “<ITEM_NUMBER>”

$cookieJarSTO = “$env:TEMP\sto_$(Get-Date -Format ‘HHmmss').txt”
$fetchOut = & curl.exe -s -D – -o NUL `
-u “${user}:${pass}” `
-H “x-csrf-token: fetch” `
-H “Accept: application/json” `
-c $cookieJarSTO `
“$stoRoot/StockTransportOrderItem”
$csrf = ($fetchOut | Select-String “(?i)x-csrf-token:\s*(.+)”).Matches.Groups[1].Value.Trim()

‘{}' | Set-Content -Path “$env:TEMP\empty.json” -Encoding UTF8

& curl.exe -s -X POST `
-u “${user}:${pass}” `
-H “x-csrf-token: $csrf” `
-H “Accept: application/json” `
-H “Content-Type: application/json” `
-b $cookieJarSTO -c $cookieJarSTO `
-w “`nHTTP:%{http_code}” `
–data-binary “@$env:TEMP\empty.json” `
“$stoRoot/StockTransportOrderItem(StockTransportOrder=”${sto}”,StockTransportOrderItem='${item}')/SAP__self.SetItemToDeleted”
# Expected: HTTP 200, STO item DeletionCode = L


12. Material Document Chain — Net Inventory Impact

After all reversal steps complete, the net inventory impact is zero across both plants.

Material Document Movement Type Plant Stock Type Direction Reversed By

GIODMT6811310 IssuingUnrestricted−1 (issue)Step 6 ReverseGoodsIssue
GIODMT6811310 IssuingTransit/SiT+1 (receipt into transit)Step 6 ReverseGoodsIssue
GRRVMT107 (WL)1320 ReceivingGR blocked+1 (receipt)Step 3 Monitor Value Chain Cancel
GIIVMT685 (WL)1310 IssuingTransit/SiT−1 (issue from SiT)Step 2 ReverseGoodsReceipt
GRIDMT1091320 ReceivingUnrestricted+1 (transfer from GR blocked)Step 2 ReverseGoodsReceipt

13. Verification Checklist

Use these GET calls to confirm prerequisite state before each write operation:

# Before Step 2: confirm IBD GR is posted
GET $ibdRoot/A_InbDeliveryHeader(‘<IBD_NUMBER>')?$select=OverallGoodsMovementStatus
# Required: OverallGoodsMovementStatus=”C”

# Before Step 6: confirm IV2 cancelled and IBD deleted
GET $obdRoot/A_OutbDeliveryHeader(‘<OBD_NUMBER>')?$select=OverallIntcoBillingStatus,OverallGoodsMvtStatus
# Required: OverallIntcoBillingStatus=”A”

# Before Step 6: no active IV2 remains
GET API_BILLING_DOCUMENT_SRV/A_BillingDocumentItem?$filter=ReferenceSDDocument eq ‘<OBD_NUMBER>'
# All IV2 type docs should have CancelledBillingDocument filled
# Any IV2 with OverallBillingStatus=”A” and empty CancelledBillingDocument is a re-created doc — cancel it first


14. If-Match Quick Reference

FunctionImport Service If-Match Rule Error If Wrong

CancelBillingDoc (API_BILLING_DOCUMENT_SRV)Must be ETag from GET428 (missing)
PostGoodsIssueOBDMust be *412 (outdated) / 428 (missing)
ReverseGoodsIssueOBDMust NOT be included501 (/IWBEP/CM_MGW_RT/018)
PostGoodsReceiptIBDMust be *412 / 428
ReverseGoodsReceiptIBDMust NOT be included501 (/IWBEP/CM_MGW_RT/018)
DELETEIBD / OBDMust be *412

15. API Automation Summary

Step Action API Viable? Method / Endpoint Notes

1Cancel IV2✅YesPOST API_BILLING_DOCUMENT_SRV/CancelGET ETag first; check for billing re-creation between Steps 1 and 6
2Reverse GRID + GIIV✅YesPOST API_INBOUND_DELIVERY_SRV/ReverseGoodsReceiptNo If-Match. Atomic: reverses both MT109 and MT685 in Full 5HP
3Cancel GRRV (MT107)❌UI onlyMonitor Value Chains → CancelButton appears only after Step 2. API blocked M7/492
4Cancel GIIV process step❌UI onlyMonitor Value Chains → CancelRequired before Steps 5–7; without it OBD status stays C
5Delete IBD✅YesDELETE API_INBOUND_DELIVERY_SRV/A_InbDeliveryHeaderIf-Match: *. Steps 3+4 must be complete first (HTTP 204)
6Reverse GI on OBD✅YesPOST API_OUTBOUND_DELIVERY_SRV/ReverseGoodsIssueNo If-Match. VL09 Fiori also works after Step 5
7Delete OBD✅YesDELETE API_OUTBOUND_DELIVERY_SRV/A_OutbDeliveryHeaderIf-Match: *. OBD status must be A after Step 6 (HTTP 204)
8Set STO item to Deleted✅YesPOST api_stocktransportorder/…/SetItemToDeletedOData V4 bound action, empty body {}

Automation boundary: Steps 1, 2, 5, 6, 7, and 8 are fully API-automatable. Steps 3 and 4 require UI intervention via Monitor Value Chains. The two UI steps are a hard prerequisite for the API steps that follow — they cannot be skipped.

The features covered in this article are based on SAP S/4HANA Cloud, Public Edition 2608, please refer to the latest information for changes in subsequent versions.

Hope you LIKE it if it addresses your issue. After that, please feel free to comment after following my account and I will reply ASAP.





Source link

Leave a Reply

Your email address will not be published. Required fields are marked *

Chat with us on WhatsApp!