Fill rate (units)¶
Of the units customers asked for, how many did we ship from stock?
Current value (inventory case data): 79.1% - ✕ Act. Open the case.
| Field | Value |
|---|---|
| Definition | Units shipped divided by units ordered. |
| Formula | sum(qty_shipped) / sum(qty_ordered) |
| Unit | % |
| Granularity | Order line; reported weekly by SKU class. |
| Direction | Higher is better ↑ |
| Target and thresholds | ≥ 95.0% (amber ≥ 90.0%) |
| Parameters | – |
| Owner | Supply Planner |
| Source | Order lines (demand from UCI Online Retail II) + synthetic stock simulation |
| Input | table order_lines, columns qty_ordered, qty_shipped |
| Recommended chart | line - Weekly trend against target with the stockout episodes annotated. |
| Related KPIs | On time in full (OTIF), Stockout rate (SKU-days), Days of cover |
How to read it¶
Volume-weighted service. Big SKUs dominate it, so a good fill rate can coexist with many small SKUs out of stock - read it next to stockout rate.
Common misreadings¶
- Unit fill rate, line fill rate and order fill rate are three different KPIs; name which one.
- Lost sales that were never ordered (customer saw "out of stock") are invisible here.
Worked example (from the test suite)¶
| qty_ordered | qty_shipped |
|---|---|
| 10 | 10 |
| 5 | 3 |
| 5 | 5 |
Expected result: 90.00%. Shipped 18 of 20 units ordered = 0.9.
This example is asserted in tests/test_formulas.py against the Python function and in tests/test_sql.py against the SQL below.
Implementations¶
Python function (the single implementation used by the site):
@kpi("fill_rate")
def fill_rate(df: pd.DataFrame) -> float:
return safe_ratio(df["qty_shipped"].sum(), df["qty_ordered"].sum())
Reference SQL (tested on SQLite):
SELECT CAST(SUM(qty_shipped) AS REAL) / SUM(qty_ordered) FROM order_lines