Как посчитать inventory days on hand в SQL

Закрепи формулу inventory days on hand в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать inventory days on hand в Telegram

Зачем days on hand

Days on hand (DOH) показывает, на сколько дней хватит текущего inventory при historical sales velocity. 60 days — overstock (cash в товаре). 5 days — risk of stockout. Sweet spot обычно 15-30 days для fast-movers.

Формула

DOH = current_inventory / average_daily_sales

average_daily_sales обычно за last 30-90 days.

DOH в SQL

WITH current_inventory AS (
    SELECT sku_id, quantity_on_hand AS current_qty
    FROM inventory_current
),
sales_velocity AS (
    SELECT
        sku_id,
        COUNT(*) FILTER (WHERE sold_at >= CURRENT_DATE - INTERVAL '90 days') / 90.0 AS avg_daily_sales
    FROM sales
    GROUP BY sku_id
)
SELECT
    i.sku_id,
    i.current_qty,
    s.avg_daily_sales,
    i.current_qty / NULLIF(s.avg_daily_sales, 0) AS days_on_hand,
    CASE
        WHEN i.current_qty / NULLIF(s.avg_daily_sales, 0) < 7  THEN 'understock'
        WHEN i.current_qty / NULLIF(s.avg_daily_sales, 0) < 30 THEN 'healthy'
        WHEN i.current_qty / NULLIF(s.avg_daily_sales, 0) < 90 THEN 'overstock'
        ELSE 'dead_stock'
    END AS verdict
FROM current_inventory i
LEFT JOIN sales_velocity s USING (sku_id)
ORDER BY days_on_hand;

Sort by understock — приоритет для reorder. Top dead_stock — кандидаты на discount/clearance.

Overstock vs understock

WITH categorized AS (
    SELECT
        CASE
            WHEN days_on_hand < 7  THEN 'understock'
            WHEN days_on_hand < 30 THEN 'healthy'
            WHEN days_on_hand < 90 THEN 'overstock'
            ELSE 'dead_stock'
        END AS category,
        SUM(current_qty * unit_cost) AS cash_value
    FROM doh_with_cost
    GROUP BY 1
)
SELECT * FROM categorized;

Cash в dead_stock = liability. Это «замороженные» деньги.

Закрепи формулу inventory days on hand в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать inventory days on hand в Telegram

По категориям

SELECT
    product_category,
    AVG(days_on_hand) AS avg_doh,
    PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY days_on_hand) AS median_doh,
    SUM(current_qty * unit_cost) AS total_inventory_value
FROM doh_table
JOIN products USING (sku_id)
GROUP BY product_category
ORDER BY avg_doh DESC;

Категория с high avg DOH = либо seasonal (норма), либо проблема merchandising.

Частые ошибки

Ошибка 1. Sales velocity без сезонной корректировки. Winter coat DOH в июне выглядит overstock. Учитывайте сезонность.

Ошибка 2. Включать damaged/returned inventory. Reserved + damaged inventory нельзя продать. Filter to «sellable».

Ошибка 3. Velocity на коротком окне. 7-day velocity слишком шумная. 30-90 days стандарт.

Ошибка 4. Игнорировать lead time. Если reorder lead time 30 days, DOH меньше 30 = stockout risk.

Ошибка 5. DOH сам по себе. Combine с margin: overstock low-margin — small loss. Overstock high-margin — большая.

Связанные темы

FAQ

Какой DOH хороший?

15-30 days для fast-movers. 30-60 для seasonal. 60+ может быть overstock.

DOH vs turnover ratio?

DOH = inverted. Turnover 12× = DOH ≈ 30 days. Эквивалентны.

Сезонность учитывать?

Yes — используйте last-year-same-period velocity, не plain 30-day.

Stockout vs DOH?

Stockout — текущая нехватка. DOH — прогноз нехватки.

Cash on dead stock?

Считайте отдельно — это immobilized capital. Discount или write-off.