Skip to main content

Retail — demand & replenishment

The problem. A retail chain spans regions, each with stores, each tracking sales and stock per SKU. Regional teams must see their numbers and only their numbers, while head office rolls everything up. The data is one big stream; the requirement is clean separation by region — for access, for ownership, for reporting.

This scenario centres on datasets as the unit of segmentation: give each region its own, tag everything with it, and the partitioning falls out for free.

Set up demo data

New workspace? Run this once (Python) to create the region's dataset and a store's SKU sales series with a month of daily units. Safe to re-run.

import datahub_sdk, numpy as np, pandas as pd

client = datahub_sdk.DataHubClient.from_env()

region_id = client.datasets.create([datahub_sdk.Dataset(external_id="region_nordics", name="Nordics region")])[0].id
client.timeseries.create([datahub_sdk.TimeSeries(external_id="store_oslo_01_sku_4471_sales",
name="Oslo-01 SKU 4471 units sold", unit="units", value_type="float", data_set_id=region_id)])
idx = pd.date_range(end=pd.Timestamp.now(tz="UTC"), periods=30, freq="1d")
client.timeseries.insert_from_lists(timestamps=idx, values=np.random.poisson(40, 30).astype(float),
ts="store_oslo_01_sku_4471_sales")

1. A dataset per region

Create a dataset for each region; every series the region owns carries its data_set_id.

DataSetModel region = new DataSetModel();
region.setExternalId("region_nordics");
region.setName("Nordics region");
long regionId = client.datasets().create(List.of(region))
.getItems().iterator().next().getId();

var salesSeries = Timeseries.of("store_oslo_01_sku_4471_sales")
.name("Oslo 01 — SKU 4471 units sold")
.setDataSetId(regionId);
salesSeries.setUnit("units");
client.timeseries().create(salesSeries);

2. Roll up demand per region

Each region lists or filters by its own dataset, then aggregates daily sales to feed replenishment. See Datasets reference and Query & aggregate.

DataSetRetreiver retriever = new DataSetRetreiver();
retriever.getFilter().setExternalIdPrefix("region_nordics"); // scope to the region's dataset
DataWrapper<DataSetModel> regional = client.datasets().list(retriever);

3. Trigger replenishment

When a store's on-hand for a SKU falls below its reorder point, raise a reorder event carrying the store, SKU and suggested quantity — the replenishment system acts on it, scoped to the region's dataset.

See the result

Chart the SKU's daily sales — the demand signal replenishment runs on:

import matplotlib.pyplot as plt

rf = datahub_sdk.RetrieveFilter(ts="store_oslo_01_sku_4471_sales",
start=pd.Timestamp.now(tz="UTC") - pd.Timedelta(days=30), end=pd.Timestamp.now(tz="UTC"),
aggregates=["sum"], granularity="1d")
sales = [dp.sum for dp in client.timeseries.retrieve_datapoints(rf)[0].get_datapoints()]
plt.bar(range(len(sales)), sales); plt.title("Oslo-01 SKU 4471 daily units sold"); plt.show()

See also