EU hierarchy: district-to-area fallback and the France & Benelux merge
**Shipped.** 19 of the 72 European stores carried no district in the retail roster, so they appeared nowhere in the hierarchy drill-down. The region read 72 while its groups summed to 53, and nothing anywhere indicated that 19 were missing.
## What was wrong
Germany and Austria were entirely absent. Italy & Switzerland showed 1 store when it should have shown 10. This was a roster data gap rather than an application defect, but the app was silently dropping stores while its own totals disagreed.
Two findings shaped the fix:
- **Italy & Switzerland already had a district code** (TPR_IT_CH_D1), assigned to exactly one store: 42421 Landquart, which opened April 2026. The other nine were never given it.
- **Germany has no district code at all.** Neither TPR_DE_A1 nor TPR_DE_AT_A1 has one, so there was nothing to assign.
## The rule
A store's group resolves as: **district → area → country → raise (never hide)**. Then a display-group mapping merges group keys that should render as one row.
Safe by inspection, not assumption: area is populated on all 72 EU stores, and on the 49 that carry a district, district and area names are identical. So the fallback cannot move a store into a different group from the one it was already in. Every North American store carries a district, so the fallback never fires there and NA is unchanged.
Group by the **code**, take the label from the **name**, add nothing.
## The merge
Belgium, France and Netherlands render as one group: **France & Benelux**. The roster already had all three reporting to the same area manager and the same district manager, so this reflects how they are actually run rather than imposing a new structure.
The mapping covers both the district codes and the area codes, so a group cannot split across the fallback boundary. That mattered: Italy & Switzerland had nine stores resolving via area and one via district, and without mapping both it would have rendered as two rows with the same name.
Germany and Germany & Austria stay as **two separate rows** — different area codes, different area managers. Merging them would be a management change, not a display one.
## Result: seven groups, 72 stores
| Group | Stores | Source |
|---|---|---|
| France & Benelux | 11 | Merged display group |
| Germany | 5 | Area fallback (was invisible) |
| Germany & Austria | 5 | Area fallback (was invisible) |
| Italy & Switzerland | 10 | Area fallback (was showing 1) |
| Spain & Portugal | 21 | District, unchanged |
| UK North & Ireland | 10 | District, unchanged |
| UK South | 10 | District, unchanged |
## Applied to Analytics too
Analytics had its own district resolution in SQL and could not call the shared rule — the rule lived in a TypeScript file and the analytics compute is plain JS, which cannot import it. The rule now lives in a .mjs with a typed facade over it, and all three surfaces import one file. Analytics resolves stores in JS and hands SQL a store-to-label lookup to join against.
Verified against prod: 57 statements, zero query failures, unassigned bucket empty across all 859 stores.
## The guardrail that matters
**The merged group key is a value no database column holds**, so it cannot reach an access decision even by accident. Scope still resolves from each store's real district, area, region and country. A manager scoped to Belgium still sees Belgium only.
## Still with Tapestry
- Populate the existing code TPR_IT_CH_D1 onto the other nine Italy & Switzerland stores
- Create district codes for the two German areas and assign them
- Attach a district manager to store 42421, which carries a district code today with no manager against it
The fallback is a safety net, not a substitute for the roster being right. As each of these lands, the fallback stops firing for those stores and the hierarchy is unchanged — which is the proof the two agree.
---
Docs: *EU Hierarchy Resolution v1* and *Analytics Hierarchy Resolution v1* (FutureProof, 27 Aug 2026).
Status: Completed And Live In Production
Log in to comment and vote
No comments yet
Be the first to share your thoughts.