Source → DWH data flow:
-
WMS service emits operational log events (e.g.
replenish_stock,start_replenishment_round) with raw JSON fields likefromLocation,toLocation,object,load_carrier_barcode, etc. -
picnic_wms__operational_log_parsed(dbt evt model) normalizes these raw events into a unified JSON structure via a largeCASE WHEN event_name = ...block. For each event type, it maps raw fields into standardized keys:replenish_stock:source_hu_id←event_raw:"fromLocation",target_hu_id←coalesce(event_raw:"toLocation", event_raw:"object")start_replenishment_round:target_hu_id←event_raw:"load_carrier_barcode"
-
wms_events(dbt intgr model) reads from the parsed model and:- Computes
h_source_hu_dv_hashkey=md5(site_id || '-' || source_hu_id)and similarly for target - Left joins to
dv.hs_handling_uniton those hashkeys (filtered to latest version:r_timestamp_end = '9999-12-31') - Builds
event_detailas:initcap( replace( event_type || iff(source_hu_id is not null, ' > ' || coalesce(source_hshu.subtype, source_hshu.type_desc, 'Unknown'), '') || iff(target_hu_id is not null, ' > ' || coalesce(target_hshu.subtype, target_hshu.type_desc, 'Unknown'), ''), '_', ' ' ) )
- Computes
Formula: event_type [> source HU type] [> target HU type]
Each segment is only appended when the corresponding source_hu_id / target_hu_id is non-null. The type label comes from coalesce(subtype, type_desc, 'Unknown') in hs_handling_unit.
Not from the CSV export (/admin/handlingunits, which only exports PL and AREA types).
Instead, the Data Vault domain wms_handling_units uses the OpsController REST API:
GET /api/1/ops/handling_units/types— returns allHandlingUnitTypeenum values mapped to their display names (e.g.LP → "License Plate",PL → "Pick Location",AREA → "Area")- For each type,
GET /api/1/ops/handling_units?type={type}— returns all handling units of that type
The ETL code (wms_handling_units/__init__.py) iterates over all types and yields a Datum per handling unit, setting type_desc from the display name. This flows through:
API response → raw table → flow.sql (staging) → Data Vault (h_handling_unit hub + hs_handling_unit satellite)
For LP (License Plate) handling units:
type=LPtype_desc=License Plate(fromHandlingUnitType.LP.getDisplayName())subtype= NULL (LicensePlate class has no subtype override)
So coalesce(subtype, type_desc, 'Unknown') → License Plate for LP handling units.
The DV load runs on a cron: Sundays and Wednesdays at 22:20.
| Before (~2025-11-27) | After |
|---|---|
Replenish Stock > Shelf Pick Location |
Replenish Stock > License Plate > Shelf Pick Location |
Start Replenishment Round |
Start Replenishment Round > License Plate |
The change could mean:
- For
replenish_stock:event_raw:"fromLocation"went from NULL to populated (with an LP barcode), causingsource_hu_idto become non-null - For
start_replenishment_round:event_raw:"load_carrier_barcode"went from NULL to populated, causingtarget_hu_idto become non-null
The code responsible is in picnic-ws-replenishment:
StockServiceImpl.replenishStockToPickLocation()—.setFromLocation(stockIdentifier.locationPicnicId())(commit36914aea6, June 2024)RoundReplenishmentServiceImpl.startReplenishmentRound()—.setLoadCarrierBarcode(round.assignedMobileLocationPicnicId())(commitd162948a8, April 2024)
To confirm the root cause, query the raw operational log events around the change date:
select
event_raw:"fromLocation"::varchar as from_location,
min(event_ts) as first_seen,
max(event_ts) as last_seen,
count(*) as event_count
from picnic_wms__operational_log
where lower(event_raw:"action"::varchar) = 'replenish_stock'
and site_id = '<WAREHOUSE_ID>'
and event_ts between '2025-11-01' and '2025-12-15'
group by 1
order by 1;If the hypothesis is correct, you should see:
from_location = NULLrows before the change datefrom_location = <LP barcode>rows after the change date
Similarly for start_replenishment_round:
select
event_raw:"load_carrier_barcode"::varchar as load_carrier_barcode,
min(event_ts) as first_seen,
max(event_ts) as last_seen,
count(*) as event_count
from picnic_wms__operational_log
where lower(event_raw:"action"::varchar) = 'start_replenishment_round'
and site_id = '<WAREHOUSE_ID>'
and event_ts between '2025-11-01' and '2025-12-15'
group by 1
order by 1;
https://gist.github.com/irbis22/e5109f35ac370ab177b62d8c60409c44