lumnik. / scenarios All scenarios · Français

SAP OData v2: the 48,000 EUR order left unprocessed

Goal Read SAP's sales orders through the OData v2 Gateway, then be told when a large order stays unprocessed.

SAP's Sales Order API, API_SALES_ORDER_SRV, served the way a Gateway serves it: October's 130 orders.

manifests/lab-docker-compose.yml
# SAP's Cloud Application Programming Model (CAP) and its OData v2 adapter, serving API_SALES_ORDER_SRV the way a# Gateway does: /sap/opu/odata/sap, server-driven paging, /Date()/ dates. October's 130 orders are its seed data.name: lumnik-sap-sales-ordersservices:  gateway:    build: ../lab    ports: ["127.0.0.1:${SAP_PORT:-9391}:4004"]       # this machine only
manifests/10-sales-orders.yaml
apiVersion: connectors.lumnik.io/v1kind: Connectormetadata:  name: scenario-sap-a  connector_type: rest-generic  scopes: [sales]spec:  base_url: http://host.docker.internal:9391/sap/opu/odata/sap/API_SALES_ORDER_SRV  endpoints:    - id: sales-orders      path: /A_SalesOrder?$format=json&$inlinecount=allpages   # JSON, and the total beside the first page      method: GET      pagination: { kind: odata-v2 }                   # d.results, the next page in d.__next ($skiptoken), the total in d.__count      target:        kind: hybrid        table: ext.scenario_sap_a        unique_key: $.SalesOrder        promote:          - { name: sales_order, path: $.SalesOrder, type: text }          - { name: sold_to_party, path: $.SoldToParty, type: text }          - { name: customer_po, path: $.PurchaseOrderByCustomer, type: text }          - { name: net_amount, path: $.TotalNetAmount, type: decimal }    # "1939.25": Edm.Decimal travels as text          - { name: currency, path: $.TransactionCurrency, type: text }          - { name: requested_delivery, path: $.RequestedDeliveryDate, type: timestamptz, transform: from_odata_date }   # /Date(1793577600000+0000)/          - { name: status, path: $.OverallSDProcessStatus, type: text }   # A not processed, B partly, C completely
manifests/20-big-order-waiting.yaml
apiVersion: apps.lumnik.io/v1kind: Processmetadata:  name: scenario-sap-big-order-aspec:  scope: sales  on:    event: data.row.changed    table: ext.scenario_sap_a  when:    - status=A                                         # not processed yet    - net_amount>=10000                                # October's orders all stay below 2 000 EUR  emit:    type: decision.big-order-waiting-a    key: sales_order    message: "{sales_order} ({sold_to_party}, {customer_po}): {net_amount} {currency}, delivery requested {requested_delivery} — not processed"
$ lm connector run scenario-sap-a
run scheduled: 62aff238-467b-4f1c-9cd3-2d0c82927a9a

$ lm run get 62aff238-467b-4f1c-9cd3-2d0c82927a9a
ID        CONNECTOR       ENDPOINT      STATUS     IN   OUT  SKIPPED  DEAD-LETTERS  STARTED           ENDED             EVALUATED
62aff238  scenario-sap-a  sales-orders  Completed  131  131  0        0             2026-11-05 10:10  2026-11-05 10:10  no

$ lm run get 62aff238-467b-4f1c-9cd3-2d0c82927a9a --wait-evaluated --timeout 2m
ID        CONNECTOR       ENDPOINT      STATUS     IN   OUT  SKIPPED  DEAD-LETTERS  STARTED           ENDED             EVALUATED
62aff238  scenario-sap-a  sales-orders  Completed  131  131  0        0             2026-11-05 10:10  2026-11-05 10:10  yes

$ lm inbox --process scenario-sap-big-order-a
ID     WHEN      PROCESS                   MESSAGE                                                                                                  ENTITY_KEY
23649  just now  scenario-sap-big-order-a  0000000131 (17100004, PO-2026-0131): 48000 EUR, delivery requested 2026-11-16T00:00:00Z — not processed  0000000131
manifests/30-order-processing.yaml
apiVersion: apps.lumnik.io/v1kind: Workflowmetadata:  name: scenario-sap-processing-aspec:  scope: sales  provenance: declared  on:    table: ext.scenario_sap_a    column: status                                     # SAP's OverallSDProcessStatus, as the Gateway writes it    key: sales_order  dormancy:    after: 1m                                          # a sales desk would write 1d; shortened for the replay    message: "{doc_key} ({sold_to_party}): {net_amount} {currency}, {state_label} since {since}"  states:    - { code: A, label: "Not yet processed", initial: true }    - { code: B, label: "Partially processed" }    - { code: C, label: "Completely processed", terminal: true }

Limits

  • The lab is SAP CAP and its OData v2 adapter, not an ECC or S/4HANA system: the answers have a Gateway's format.
  • The offset in /Date(…+0060)/ is not applied: the milliseconds are already UTC.
  • odata-v2 sends no page size: the Gateway's 50 per page governs the run.
  • The dormancy counts from the first time lumnik sees the status, not from the order's creation in SAP.
  • Synthetic orders, customers and amounts.

Sales · SAP

The orders are in SAP.

They must be read without a manual export.

API_SALES_ORDER_SRV, OData v2, 130 orders

The problem

A 48,000 EUR order stays in status A.

  • October's orders stay below 2,000 EUR.
  • This one is not processed.

What is not changed

SAP stays as it is.

  • The OData v2 service SAP already exposes.
  • No ABAP development, no export.

What had to be written

pagination: { kind: odata-v2 }
transform: from_odata_date

One pagination line, one transform.

  • A Process: one decision for the order.
  • A Workflow: an alert when it sleeps.

What was fixed

SAP dates used to land empty.

  • /Date(ms)/ is read as a date (#1826).
  • The odata-v2 preset names the pagination (#1827).

Try it

The scenario replays locally.

Every step is published with its output.

This scenario runs on lumnik Open, free and open source.

Try lumnik Open →