Timer → OData → Data Store: automate daily snapshots without Excel

A nightly process design that extracts Sales Orders from S/4HANA Cloud, transforms them to JSON and persists them in a Data Store — including five common mistakes to avoid.

A familiar task in many companies: someone in management reporting manually exports an ERP spreadsheet every night. This laboratory case designs an automated daily snapshot of open Sales Orders from S/4HANA Cloud, persisted for a dashboard to consume.

The pattern is Start Timer → OData → Message Mapping → Data Store, a reusable Cloud Integration exercise for practical preparation.

Scenario

Globex Industries needs to extract the day's sales orders every night at 23:30, transform them into a standard internal JSON format and store them locally. To practice without production S/4HANA, I use the public SAP Business Accelerator Hub sandbox (API_SALES_ORDER_SRV).

iFlow architecture

Timer_DailyAt2330
   → CM_BuildCorrelationAndTimestamp   (context: timestamp, correlationId, APIKey)
   → OData_Get_SalesOrders             (Request-Reply to the sandbox)
   → MM_SalesOrder_to_SnapshotJson     (internal JSON transformation)
   → DS_Write_SnapshotBatch            (persistence, 30-day retention)

Exc_SnapshotErrorHandler               (Exception Subprocess → error store, 90 days)

1. Timer with an externalized schedule

Externalize the cron expression as {{param_timer_cron}}: 0 30 23 * * ? for the intended production schedule and hourly during development. Configure the frequency through the deployment parameter.

2. Context before the backend call

A Content Modifier prepares the internal context as properties, keeping it separate from the payload:

3. The OData call

Request-Reply calls {{param_target_baseUrl}}/A_SalesOrder, using $top and $select to request the necessary fields. One detail can cost hours of debugging:

The APIKey header injected by the Content Modifier does not reach the backend unless it is included in the receiver's Allowed Headers. The symptom is a puzzling 401: the header exists inside the flow, but CPI filters it before sending.

4. Mapping against a verified contract

Before creating MM_SalesOrder_to_SnapshotJson, manually GET the endpoint and inspect $metadata. Mapping an imagined structure instead of the actual response is a common mistake.

5. A unique Entry ID

Entry ID = ${property.snapshotTimestamp}_${property.correlationId}

The combination keeps entries unique even when executions coincide, preventing duplicate-entry errors or silent overwrites.

6. Errors in a separate Data Store

The Exception Subprocess builds JSON containing code, ${exception.message}, correlationId and a timestamp. It persists the result in DS_SalesOrderSnapshotErrors with 90-day retention, allowing more time to investigate errors than regular data.

Five common mistakes in this pattern

From my laboratory error catalogue:

CodeErrorPrevention
CPI-007Hardcoded endpoints and scheduleExternalized parameters
BTP-001APIKey pasted as literal textSecurity Material alias
CPI-008Header filtered by CPIAdd it to Allowed Headers
CPI-003Disconnected Exception SubprocessError branch with its own Data Store
CPI-006Mapping without a verified contractManual GET + $metadata before mapping

Design decisions

Takeaway

This pattern appears in nightly reports, catalogue synchronization and inventory snapshots. Learning it with these five common errors addressed is useful for real integrations and certification preparation.

This article documents a design. When I build it in the tenant, I will publish a second part with evidence, as I did for the wrapper case and the governed API.