Supply Chain Analytics · Apex Consumer Goods

1,105 orders every night. Some of them lie.

Signal in the Noise — turning messy distribution data into decisions you can defend.

Scroll to explore
B2

This is what the data actually looks like.

Every night, 1,105 in-transit orders flow out of SAP. The extract arrives dirty — two date formats in one column, the same status spelled three ways, phantom SKU codes, missing weights, impossible quantities. Hover over the highlighted cells to discover what's hiding in plain sight.

Problems found0/15
fx
=QUERY(SAP_Extract, "SELECT * WHERE SAP_Status = 'In Transit'")
#Order_IDSKUDescriptionQtyWeight_kgDispatch_DateSAP_StatusDestination
1SO-50312LAC-1013Leche Entera 1L7038.322026-05-31In TransitCD-MER
2SO-50624768CD-TOL
3SO-5081790602-Jun-2026CD-TOL
4SO-50095PRO-1012Temporada Pack 64.102026-06-03In TransitCD-CDMX
5SO-50709ABA-1017Frijol 1kg1.8002-Jun-2026In TransitCD-SAL
6SO-50058BEB-100219802026-06-03In TransitCD-CHI
7SO-50776BEB-101626832026-06-02DeliveredCD-LEO
8SO-50491PRO-1005Temporada Pack 3157711.02In TransitCD-TIJ
9SO-51008LAC-1016Yoghurt Bebible 250ml15310.921-May-2026CD-MOR
10SO-50000103612.512026-06-03In TransitCD-LEO
Showing 10 of 1,105 rowsSource: SAP_Transit_Extract_2026-06

This is 10 rows. The real extract has 1,105 — including 12 orphan codes that match no product on file. Every one of these problems — mixed dates, orphans, nulls, inconsistent text — compounds across the full dataset. Every decision downstream inherits these errors, so the cleaning isn't optional. It's the foundation.

C1

Four stages. One pipeline.

Every night, the same four stages run in the same order. Each one is built to surface truth and discard noise — and no step is optional, because the next one inherits whatever the last one missed.

01

Ingest

Raw data lands

1,105 in-transit orders pulled nightly from SAP. Three plants, 15 distribution centers, 100 SKUs across 5 categories. The data arrives as-is — messy, inconsistent, and full of silent errors.

Order_IDSKUDispatch_DateWeight_kgSAP_Status
fx
=IMPORTDATA(SAP_Extract)
02

Clean

Find & fix the lies

Normalize the two date formats to ISO. Quarantine orphan SKU codes that match no master entry. Impute missing weights from historical averages. Flag negatives, zeros, and nulls. The invisible work that makes everything downstream trustworthy.

Date_StdSKU_ValidWeight_CleanQty_AbsFlag
fx
=IF(ISBLANK(Weight_kg), AVERAGE(Hist_Weight), Weight_kg)
03

Analyze

Detect what matters

Four exception detectors run in parallel: inefficient loads (truck < 75% full), FEFO-risk dairy orders, ghost shipments stuck in transit, and orphan codes with no master match. Statistical safety stock at 95% service level (Z = 1.65).

Exception_TypeThresholdCountSeverityAction
fx
=IF(MAX(Peso/24000, Pallets/33) < 0.75, "Ineficiente", "OK")
04

Decide

Allocate with fairness

When supply falls short, the Fair Share algorithm distributes proportionally across all 15 DCs — no DC gets zero, no DC gets everything. The dashboard presents actionable decisions, not just data.

DCDemandCoverageAllocationΔ_Units
fx
=ROUND(DC_Demand * Coverage_Ratio, 0)

By the time an order reaches the fourth stage, it has been parsed, validated, weighed, and stress-tested against every other order competing for the same truck. Nothing reaches a decision until it has earned the right to be trusted.

D1

The invisible work.

Before a single order can be counted, it has to be made true. This is the unglamorous, detail-heavy layer where the extract stops lying — two real problems from the Apex data, and how each one was run to ground.

Before → After

Dispatch_Date03-Jun-2026Text-month — ISO elsewhere
Dispatch_Date_Std2026-06-03ISO 8601 — one format
Weight_kgNULLNo weight → invisible row
Weight_kg_Clean1.05Imputed from SKU historical avg
Qty-25Negative — return? error?
Qty_Abs25Absolute value + flagged for review
SKUXXX-8415XXX- = SAP capture error
SKU_StatusORPHANQuarantined — excluded from allocation
Lesson 01

The dates that lied

Dispatch_Date arrived in two formats at once. Most rows were ISO — "2026-05-31" — but a stubborn minority were text-month: "31-May-2026". No flag marked which was which. Parse the whole column as ISO and the text-month rows fail silently, dropping out of every transit-time calculation and making the network look faster than it really is. Parse it the other way and the ISO rows break instead. The fix was disciplined, not clever: detect the format row by row, normalize everything to ISO, and reject anything that still refused to parse so nothing failed in silence. A short Power Query step — and every duration built on top of it became trustworthy.

Lesson 02

The phantom SKUs

Twelve orders pointed at SKU codes that matched nothing in the product master — and all twelve shared the same XXX- prefix. That prefix is the tell: it's what SAP writes when the capture step fails, so these weren't products, they were data-entry ghosts. They couldn't be weighed, costed, or allocated. The easy move is to delete them and move on. But deleting also erases a real signal: twelve orders' worth of demand that someone genuinely tried to place. So they're quarantined, not buried — pulled out of the allocation math, listed on a reconciliation report, and handed back to the team that owns SAP capture. A broken row still tells you something; the job is to flag it, not hide it.

These two stories are why the cleaning layer exists as a separate stage. It's not preprocessing — it's quality assurance. The model is only as honest as the data it accepts.

E1

Four detectors. Zero false comfort.

The cleaned data feeds four parallel exception detectors. Each one surfaces a specific risk that the raw data hides. Together, they caught 469 actionable flags across 1,105 orders.

fx
0

total exception flags across 1,105 orders

F1

When there isn't enough.

Eight SKUs are in shortage. The model auto-detects them and asks the hard question: who gets what? The Fair Share algorithm distributes proportionally — no DC gets zero, no DC gets everything.

~0

units the network cannot fulfill

SKUBOT-1019Agua Mineral 600ml
Demand: 9,278Coverage: 48.6%
4,509
DCCoverageAllocShort
CDMX-01
48%
690730
GDL-03
48%
525555
MTY-02
48%
462488
PUE-05
48%
423447
TIJ-04
48%
394416
MER-06
48%
369391
QRO-07
48%
330350
CUN-08
48%
287303
LEO-09
48%
253267
CHI-10
48%
233247
HMO-11
48%
189201
VER-12
48%
151159
OAX-13
48%
96102
AGS-14
48%
6367
SLP-15
48%
4446
Allocation = ROUND(DC_Demand × 0.486, 0)| Unfulfilled: 4,769 units

Drag the slider to zero. Watch every DC lose supply fairly. Drag it past demand. Watch every DC get exactly what it asked for. The algorithm is proportional by design — the same coverage ratio applies everywhere. No favorites, no politics, just math.

G1

From noise to signal.

The messy extract became a decision-ready dashboard. Every number is defensible. Every exception is explained. Every allocation is fair.

0

Orders analyzed nightly

0

Exception flags surfaced

0

SKU shortages auto-detected

0

DCs allocated fairly

Before
Two date formats mixed in one column
12 orphan SKUs — ignored or manually patched
No visibility into truck fill rates
Ghost shipments distorting inventory counts
Shortage allocation by gut feel
After
Standardized ISO dates — one format, zero ambiguity
Orphan codes quarantined for SAP capture review
361 inefficient loads flagged automatically
51 ghost shipments surfaced for follow-up
Fair Share proportional allocation — math, not politics

The Dashboard

Exception Summary

All four detector outputs in one view — filterable by type, severity, and DC.

Fair Share Allocator

Live allocation table for every SKU in shortage, with coverage ratios and deficit tracking.

Transit Monitor

Ghost shipment tracker with days-in-transit aging and escalation flags.

Load Optimizer

Fill-rate analysis across all active shipments, with consolidation suggestions.

H1

What changes by morning.

The planners no longer open a spreadsheet of 1,105 raw rows. They open a short list of decisions: which trucks to consolidate, which dairy to expedite, which codes to reconcile, and exactly how to split what's scarce. The noise became a shortlist of things worth acting on — and every number on it traces back to a row that was made true.

Inputs you can trust

Every order is parsed, validated, and reconciled before it ever reaches a calculation.

Safety stock that holds

Coverage set statistically at a 95% service level — not by gut, and defensible line by line.

Decisions, not dashboards

Each exception arrives with the action attached: consolidate, expedite, reconcile, reroute.

Fairness under scarcity

When supply falls short, every DC is cut by the same ratio — an allocation no one can dispute.

Back to perezfajardo.com

Apex Consumer Goods is a fictional company created for this case study. All data is synthetic.