logo

Are you need IT Support Engineer? Free Consultant

External Workflow for S/4HANA Sales Orders: Event …

  • By Sanjay
  • 27/05/2026
  • 9 Views


How to escape the “single approval reason” limit of the standard sales document workflow by handing the case off to a parallel approval flow on BTP — and what every moving part is actually for.

Why this matters

The standard Sales Document approval workflow in S/4HANA Public Cloud is built around one approval reason per document. The framework evaluates configured conditions in order; whichever condition is met last wins, and a single reason routes the document to a single approver chain. As long as your business rules are mutually exclusive (“price changed → pricing manager,” “incoterm changed → incoterm manager”), the standard mechanism is fine.

Things break down the moment you need parallel approvals: “if both price and incoterm changed at the same time, I want both managers to approve, in parallel, and the document only proceeds when both have signed off.” That's not something the in-system workflow framework expresses, because it never produces more than one reason for a given save.

This blog walks through one way to get there: leave the in-system workflow alone, and let an external workflow on BTP own the parallel-approval logic. S/4HANA tells BTP “this document needs attention,” BTP figures out who the approvers are, drives the parallel flow, and calls back into S/4HANA to release or reject.

The architecture in one picture

┌─────────────────────────┐                ┌─────────────────────┐
│  S/4HANA Public Cloud   │                │        BTP          │
│                         │                │                     │
│  Sales Order save       │                │  ┌───────────────┐  │
│        │                │  ApprovalSts   │  │ SAP Event Mesh│  │
│        ▼                │   Changed      │  │   (broker)    │  │
│  BAdI sets reason  ─────┼────event──────▶│  └──────┬────────┘  │
│        │                │                │         │ webhook   │
│  Z change-doc API ◀─────┼────────────────┼──┐      ▼           │
│        │                │                │  │  ┌───────────┐   │
│  Release / Reject ◀─────┼────OData call──┼──┤  │ CAP bridge│   │
│        │                │                │  │  │  (Node.js)│   │
│        ▼                │                │  │  └─────┬─────┘   │
│  Standard workflow      │                │  │        │ trigger │
│  closes the document    │                │  │        ▼         │
│                         │                │  │  ┌───────────┐   │
│                         │                │  └──│   SAP BPA │   │
│                         │                │     │ (parallel │   │
│                         │                │     │ approval) │   │
│                         │                │     └───────────┘   │
└─────────────────────────┘                └─────────────────────┘

Three things to notice:

  1. S/4HANA still owns the document. The in-system workflow is not bypassed — it is paused, waiting for a release/reject signal that comes back from BPA via OData.
  2. Event Mesh is the boundary. S/4HANA publishes ApprovalStsChanged; whatever consumes it lives outside the system.
  3. The CAP bridge is small but load-bearing. It exists because Event Mesh's webhook payload and BPA's trigger contract don't line up. We'll come back to this.

Part 1 — S/4HANA side

Communication scenarios

You need two communication arrangements:

  • SAP_COM_0109 — Sales Order A2X. This is what BPA calls back into to release or reject the document.
  • SAP_COM_0092 — Event Enablement. This is what publishes the ApprovalStsChanged event to Event Mesh. SAP help: ABAP Cloud — Communication Arrangements.

BAdI: route everything to “external”

Inside the Cloud BAdI SD_APM_SET_APPROVAL_REASON, return a dedicated reason code (ZEXT in this PoC) that exists in customizing only as a hand-off marker — its sole purpose is to put the document into “In Approval” status so the event fires.

METHOD if_sd_apm_set_approval_reason~set_approval_reason.
  " Hand off all sales order saves to the external workflow
  IF salesdocument-sddocumentcategory = 'C'.
    salesdocapprovalreason = 'ZEXT'.
  ENDIF.
ENDMETHOD.

If you also use the AI-enhanced BAdI variant (covered in my earlier blog), the same hand-off applies — the AI returns ZEXT only when it detects multi-category changes, so single-category cases stay in the in-system flow and only true parallel cases leave the system.

Z change-document API

BPA needs to know what changed, not just that something changed. The standard change-document API is C2 (private), so for the PoC we wrapped it as a Z OData service and exposed it as C1 (released for cloud consumption). BPA calls this Z service after receiving the event to decide which approver groups to involve.

This is the part of the design most likely to be replaced by a standard released API in the future — track the SAP Help portal for change-document C1 availability before committing this in production.

Part 2 — Event Mesh as the boundary

Why Event Mesh and not direct webhook

The “official” pattern for triggering BPA from S/4 events is direct: S/4 publishes, BPA's event trigger consumes. In our setup we deliberately put SAP Event Mesh in between, for two reasons:

  1. Buffering. If BPA is briefly unavailable, Event Mesh holds the message. A direct webhook would simply fail.
  2. Payload reshaping. The event payload S/4 publishes does not match the field types BPA's event trigger expects. Event Mesh + a small bridge lets us reshape without touching either end.

Service descriptor that subscribes to the S/4 event

The service-key descriptor needs publish/subscribe rights on both your own namespace and the SAP-published topic family sap/s4/beh/*:

{
  "options": {
    "management": true,
    "messagingrest": true,
    "messaging": true
  },
  "rules": {
    "topicRules": {
      "publishFilter":   ["${namespace}/*"],
      "subscribeFilter": ["${namespace}/*", "sap/s4/beh/*"]
    },
    "queueRules": {
      "publishFilter":   ["${namespace}/*"],
      "subscribeFilter": ["${namespace}/*", "sap/s4/beh/*"]
    }
  },
  "version": "1.1.0",
  "emname":     "your-em-instance",
  "namespace":  "your/namespace"
}

The event you specifically want is:

sap/s4/beh/salesorder/v1/SalesOrder/ApprovalStsChanged/v1

Part 3 — The CAP bridge

This is the piece that surprises most people. Why a CAP application in the middle?

BPA's HTTP event trigger expects a request body shaped a certain way. Event Mesh's webhook posts a different shape. There is no built-in mapping in either product to bridge them. So we built the smallest possible CAP service whose only job is:

  1. Accept Event Mesh's webhook POST.
  2. Pull the sales order ID and approval status from the message.
  3. Optionally enrich it (call the Z change-document API to attach what changed).
  4. POST a BPA-shaped trigger to the configured BPA destination.

That's it. ~50 lines of CAP, deployed once, then forgotten. The reason it deserves a dedicated component instead of an iflow or a generic mapper is that you also want it to handle:

  • Idempotency — Event Mesh can re-deliver. Track the message ID, drop duplicates.
  • Backoff — if BPA is down, return a non-success so Event Mesh redelivers, instead of black-holing the event.
  • Observability — one place to log “what came in, what went out,” which you'll desperately want the first time a sales order gets stuck.

If you have an existing iPaaS (Integration Suite) that already does request reshaping plus retry plus tracing, that is a perfectly fine alternative — the CAP bridge is just the lightest path when you don't.

Part 4 — SAP Build Process Automation

Inside BPA, the project has three responsibilities:

1. Decide who needs to approve

Call back into S/4 via the Z change-document API to learn which fields changed, then map field categories (price, incoterm, …) to approver groups.

2. Run the approvers in parallel

BPA's parallel gateway is the whole reason this approach exists. Each approver group sees the case in their My Inbox (or the BPA-provided inbox); the case proceeds only when all required approvers have decided.

3. Call back into S/4HANA

Once the parallel branch resolves, BPA invokes the Sales Order A2X release or reject action via SAP_COM_0109. The standard in-system workflow then closes the document — from S/4's perspective, an external party simply provided the decision.

Useful reference for setting up the BTP destinations and the BPA event/action plumbing: Setup Actions and Events for Integration in SAP Build Process Automation.

What we observed

Scenario What happened Outcome

Single-category change (price only) BAdI variant kept it in-system; BPA never triggered Standard pricing-manager approval ✓
Multi-category change (price + incoterm) Event published → CAP bridge → BPA parallel branch (pricing + incoterm) Both approvers in parallel; release callback ✓
BPA temporarily unavailable Event Mesh held the message and redelivered Document eventually approved without manual intervention ✓

Honest limitations

  • Operational surface area. You are now running an Event Mesh instance, a CAP application, and a BPA project alongside S/4. That is real ops cost. Worth it when you genuinely need parallel approval; overkill if you don't.
  • Latency. Event publication, broker hop, CAP reshape, BPA dispatch — typically a few seconds end to end. Not a problem for an approval flow that already runs in human time, but don't promise sub-second.
  • Change-document C1 API gap. The Z wrapper is the most fragile piece. Plan to replace it with a standard released API when SAP delivers one.
  • Two systems of record for “what's pending.” The document shows “In Approval” in S/4; the actual pending tasks live in BPA. Make sure your operations team knows where to look.

When to pick this — and how it pairs with the AI BAdI approach

Two patterns address the same underlying gap from different angles:

  • AI-enhanced BAdI (covered in ABAP AI Meets Workflow: ISLM-Powered Routing in S/4HANA) — keeps everything in-system, uses an LLM to pick the most appropriate single reason. Cheap to operate, fast, and great when business rules change often. Cannot run approvers in parallel.
  • External workflow with BPA (this blog) — moves the decision to BTP, handles real parallel approval, costs more to operate.

They compose. The AI BAdI returns a normal in-system reason for single-category changes and only escalates to ZEXT when it detects a true multi-category case. The external workflow then takes over only for the cases that need it. You get the smart-and-cheap path most of the time, and the heavy machinery only when business reality demands it.

Closing thoughts

The interesting design choice in this pattern is not any single component — it's the boundary. S/4HANA stops trying to express something it was not built to express (parallel approval) and instead exposes a clean event + a clean callback API. BTP picks up the orchestration responsibility and gives it back to the system in a shape S/4 already understands: “release this document” or “reject this document.”

That separation is what makes the pattern reusable. The same shape — event out, decision in — works for credit limit overrides, contract amendments, anything else where the in-system workflow framework runs out of room.

Where would you draw the boundary in your own landscape? Happy to discuss in the comments.



Source link

Leave a Reply

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