Case studiesReference integration: maintenance to ERP

OpexMX to SAP: confirmed delivery for every posting

How OpexFlow carries goods issues and work order confirmations from OpexMX to SAP so that each one is either confirmed by SAP or visible as an open item: accepted once, tallied on both sides, checked every night, and parked for a person when SAP refuses it.

Why delivery needs proof

Maintenance work creates ERP documents: spare parts issued to a work order, labour and cost confirmed when the order closes. Between two systems the costly failure is the silent one: a goods issue SAP refuses, or a confirmation that never lands, found only when stock or cost centres stop adding up.

An HTTP 200 on one call is not enough. The flow below gives three independent signals for every posting: the answer from SAP, a dead letter when SAP refuses, and a nightly comparison of what was sent against what SAP confirmed. The same pattern applies to any system pair, for example an MES posting to SAP.

The flow

OpexMX sends a signed webhook for each goods issue or work order confirmation. One flow in OpexFlow sits between OpexMX and the SAP middleware and does the following, in this order.

Nodes in the OpexMX to SAP flow, in order

  1. signed webhook
  2. dedupe
  3. map
  4. ledger: OpexMX
  5. http: SAP
  6. ledger: SAP
  7. http: ack to OpexMX
  8. reconcile
  1. Check the signature. A request that fails it is refused with 401 and never enters the flow.
  2. Accept each event id once. A resend of the same event id is recognised and dropped.
  3. Map the event to the SAP payload: a goods issue (movement type 261) or a PM order confirmation.
  4. Write the event to the ledger, OpexMX side.
  5. POST the payload to the SAP middleware with an Idempotency-Key header.
  6. Write SAP’s answer to the ledger, SAP side.
  7. Return the SAP document number to OpexMX, which stores it on the transaction.

Two failure paths are built in. If SAP refuses the posting (a 4xx answer), the record is parked in Dead letters with its original payload. If SAP is down (a 5xx answer), the hub answers the sender with 503 and records nothing, so the delivery can be sent again safely. OpexMX’s webhook sender today re-sends only after a network error or timeout, not after a 503, so sending again on a 5xx answer has to be enabled on the OpexMX side. A nightly reconcile at 01:00 compares the two ledgers and catches any delivery that never arrived.

The OpexMX to SAP posting flow in the OpexFlow editor: a signed webhook source, then once-per-event, map to SAP payload, tally OpexMX side, POST to SAP middleware, tally SAP side and return SAP document number to OpexMX. A comment above the chain describes the 4xx and 5xx handling.
The flow in the editor, cropped to the nodes. The line under the SAP call shows the last error from the test run, with the host address blurred.

Tested failure cases

The flow was run with five events, a resend and a forged request against a simulated SAP middleware, so the document numbers are not real SAP documents.

Test cases and results
CaseWhat happenedResult
Three normal eventsPosted to SAP, which answered 201Document numbers returned to OpexMX
The same event sent againRecognised by its event idNo second posting
A forged signatureRefused at the door401, nothing reached SAP
SAP refuses a postingSAP answered 422, “KI235 cost center blocked”Parked in Dead letters with the original payload, editable, ready to replay
SAP is downThe hub answered the sender 503 twiceThe test sender, standing in for OpexMX, sent again, and the posting went through when SAP came back

The refused posting is the failure that usually goes unnoticed. It did not vanish and it did not block the others. It sits in Dead letters with the exact record that was sent and SAP’s message. Someone can correct the payload and replay it into the flow, and each action is recorded under their sign-in.

The Dead letters tab of the Operations panel with one open entry. The error reads: POST returned 422 Unprocessable Entity, KI235 Cost center CC-9999 is blocked for posting. The record at the failing node and the original record are shown with Edit buttons and a Replay button.
The refused posting in Dead letters, cropped to the panel. The host address in the error text is blurred.

The call log of the stand-in service shows the sequence for the other cases: three postings accepted with 201, one 422, two 503 while SAP was down, then a 201 once it returned. Every accepted posting is followed by an acknowledgement call back to OpexMX with the document number.

The Mock services panel listing recorded calls. POST /sap/documents answers 201, 201, 201, then 422, then 503 twice, then 201. Each 201 is followed by a POST /api/sap-ack answering 200.
Recorded calls of the stand-in SAP middleware and the OpexMX acknowledgement endpoint.

The nightly check

Every night at 01:00 a reconcile node compares the OpexMX ledger with the SAP ledger by event id. In the test run, OpexMX held 5 events totalling 4,971.50 and SAP held 4 totalling 3,631.50. Four matched. The one gap, event evt-1004 for 1,340, was the refused posting.

The Reconciliation tab showing the opexmx-vs-sap report with 1 exception: OpexMX 5 keys totalling 4,971.5, SAP 4 keys totalling 3,631.5, 4 matched. The exception row is evt-1004, missing in SAP, amount 1,340.
The opexmx-vs-sap report: 5 events against 4, one missing in SAP.

The gap became an exception case. A case has a status, an assignee and a history, so it is worked rather than read: assign it, investigate, then resolve it as matched, correction posted or written off. The dead letter tells you at once and the reconcile gives a second, independent view of the same posting. Here the two agree.

The Exceptions tab with one open case: key evt-1004, kind Missing right, amount 1,340, status open, unassigned. Counters above the list show 1 needing work, 1 unassigned and 1,340 at stake.
The same event as an open exception case, unassigned, with the amount at stake.

Health and alerts

The Health and SLA tab lists every external call the flow makes, with calls, errors, retries and latency. In the test window the SAP call had 7 calls, 3 errors and 2 retries, which matches the 422 and the two 503 answers above.

The Health and SLA tab: 9 runs, 3 errors, two flows listed, and an external systems table where the POST to SAP middleware shows 7 calls, 3 errors and 2 retries.
Health and SLA for the test run: flows on top, external calls below.

An email when SAP refuses a posting

The POST to SAP node sends permanent failures (4xx) down its error wire. That wire runs an email node, which sends the message over SMTP to the operations mailbox, and then a step that parks the record in Dead letters, so it can still be corrected and replayed. Transient failures (5xx) do not take this path. They go back to the sender as a 503, to be sent again.

The OpexMX to SAP flow with a red error wire from the POST to SAP middleware node down to an email node and a park step.
The error wire: email operations, then park the record for replay.
An email titled "SAP posting FAILED" for event evt-1006, listing the work order, plant, cost center CC-9999, amount 212.7 USD and the SAP error KI235 cost center blocked for posting.
The email for a goods issue refused by SAP. Host addresses are blurred.

Scope

  • Tested against a simulated SAP middleware, not a live SAP system.
  • The email in the screenshots went to a test mailbox.
  • Field mapping, document types and the SAP interface (middleware REST, IDoc or BAPI) are set per installation.

Related

From download to first record in minutes.

Run one binary, connect a PLC, a CNC or a webhook, and watch the records arrive in the editor.

Builds for Linux (x86_64 and ARM64), Windows and macOS.

  • One binary
  • Raspberry Pi to rack server
  • Signed delivery with retries

Sizing questions or an air-gapped install? Email marketing@opexcg.com or read the quick start.