lumnik. / scénarios Tous les scénarios · English

SAP OData v2: la commande de 48 000 EUR restée non traitée

Objectif Lire les commandes SAP par le Gateway OData v2, puis être prévenu quand une grosse commande reste non traitée.

L'API Sales Order de SAP, API_SALES_ORDER_SRV, servie comme un Gateway: 130 commandes d'octobre.

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 }

Limites

  • Le labo est SAP CAP et son adaptateur OData v2, pas un ECC ni un S/4HANA: le format des réponses est celui d'un Gateway.
  • Le décalage de /Date(…+0060)/ n'est pas appliqué: les millisecondes sont déjà en UTC.
  • odata-v2 n'envoie pas de taille: les 50 par page du Gateway gouvernent le run.
  • La dormance compte depuis la première fois que lumnik voit le statut, pas depuis la création de la commande dans SAP.
  • Commandes, clients et montants synthétiques.

Ventes · SAP

Les commandes sont dans SAP.

Il faut les lire sans export à la main.

API_SALES_ORDER_SRV, OData v2, 130 commandes

Le problème

Une commande de 48 000 EUR reste en statut A.

  • Les commandes d'octobre restent sous 2 000 EUR.
  • Celle-ci n'est pas traitée.

Ce qui n'est pas modifié

SAP reste tel quel.

  • Le service OData v2 que SAP expose déjà.
  • Aucun développement ABAP, aucun export.

Ce qu'il a fallu écrire

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

Une ligne de pagination, une transformation.

  • Un Process: une décision pour la commande.
  • Un Workflow: une alerte quand elle dort.

Ce qui a été corrigé

Les dates SAP arrivaient vides.

  • /Date(ms)/ se lit en date (#1826).
  • Le preset odata-v2 nomme la pagination (#1827).

Essayer

Le scénario se rejoue en local.

Chaque étape est publiée avec sa sortie.

Ce scénario tourne sur lumnik Open, gratuit et open source.

Essayer lumnik Open →