Плейбук: «Заказы WB» за любой период
Срабатывает, когда пользователь просит:
- «Заказы WB»;
- «Заказы Tote WB за 7 дней»;
- «Заказы WB за вчера»;
- «Заказы с отм. WB»;
- «Заказы сравнение WB».
Формат такой же, как для Ozon:
Артикул | Заказы с отм. | Сумма | Реклама | CPO
Для WB источник заказов — воронка, а не orders, sales и не финреп.
Магазины
| Магазин | shop_id |
|---|---|
| Siluetta WB | 3 |
| Tote WB | 5 |
Если магазин не указан, брать из контекста. Если контекста нет — уточнить.
Источники
| Метрика | Источник |
|---|---|
| Заказы с отм. | funnel_daily.orders_count |
| Сумма | funnel_daily.orders_sum_rub |
| Отмены, если нужно показать отдельно | funnel_daily.cancel_count |
| Сумма отмен | funnel_daily.cancel_sum_rub |
| Реклама | ad_metrics_daily.sum_cost |
| CPO | Реклама / Заказы с отм. |
Почему не orders/sales: WB Statistics API и воронка дают разные цифры. Для маркетингового отчёта по заказам пользователь хочет цифру из кабинета/воронки — все нажатия «Заказать», включая отмены.
Периоды и полнота
Для периода сначала использовать точное свежее окно funnel_period, если оно
существует: оно лучше учитывает поздние корректировки WB. Если точного окна нет,
источник — funnel_daily: одна строка на день × магазин × nm_id. Так месяц и
произвольные даты (с 02.08 по 26.08) считаются обычным SUM по
date BETWEEN :since AND :to.
Перед расчётом обязательно проверить покрытие:
SELECT COUNT(DISTINCT date) AS actual_days
FROM funnel_daily
WHERE shop_id = :shop_id
AND date BETWEEN :since AND :to;
actual_days должен равняться календарной длине периода. Если дневное покрытие
неполное, разрешён только fallback на точное окно funnel_period, где одновременно
period_start = :since и period_end = :to. Частичную сумму дневных строк выдавать
как полный отчёт нельзя.
7 дней
Заказы WB за 7 дней = последняя закрытая календарная неделя Пн–Вс.
Пример для текущей даты 2026-06-29:
2026-06-22 … 2026-06-28
Брать дневные строки из funnel_daily:
WHERE date BETWEEN :since AND :to
1 день
Заказы WB за день = один календарный день, по умолчанию вчера.
Пример для текущей даты 2026-06-29:
2026-06-28 … 2026-06-28
Также брать строку дня из funnel_daily:
WHERE date = :day
Ночной сбор обновляет в funnel_daily последние 7 закрытых дней, чтобы поздние
корректировки WB перезаписывали старые значения. Окна funnel_period 1/7/30/90
временно сохраняются как совместимый fallback.
WB /products/history не позволяет запросить даты старше последних 7 дней. Для
первичного наполнения старой истории использовать накопленные однодневные снимки:
.venv/bin/python scripts/seed_funnel_daily_from_period.py --shop-id 3 --shop-id 5
.venv/bin/python scripts/seed_funnel_daily_from_period.py --shop-id 3 --shop-id 5 --apply
Первая команда — обязательный dry-run. Скрипт не перезаписывает свежие строки API; изолированную дырку восстанавливает разницей точного 7-дневного окна и остальных шести дней, причём отклоняет вариант с отрицательными метриками.
Сравнение
Если пользователь пишет «сравнение», «сравни», «сравни данные», «сравни отчёты»:
7 дней сравнение
Текущий период = последняя закрытая неделя.
Предыдущий период = предыдущая закрытая неделя.
текущий: 2026-06-22 … 2026-06-28
предыдущий: 2026-06-15 … 2026-06-21
1 день сравнение
Текущий период = выбранный день.
Предыдущий период = среднее за предыдущие 7 календарных дней, не включая выбранный день.
день: 2026-06-28
сравнение: среднее за 2026-06-21 … 2026-06-27
Для среднего делить абсолютные метрики на 7:
orders_count;orders_sum_rub;cancel_count;cancel_sum_rub;sum_cost.
CPO считать после усреднения:
CPO = avg_ads / avg_orders
SQL-скелет
WITH f AS (
SELECT
sku,
nm_id,
SUM(orders_count) AS orders_count,
SUM(orders_sum_rub) AS orders_sum_rub,
SUM(cancel_count) AS cancel_count,
SUM(cancel_sum_rub) AS cancel_sum_rub
FROM funnel_daily
WHERE shop_id = :shop_id
AND date BETWEEN :since AND :to
GROUP BY sku, nm_id
),
ads AS (
SELECT
nm_id,
SUM(sum_cost) AS ad_spend
FROM ad_metrics_daily
WHERE shop_id = :shop_id
AND date BETWEEN :since AND :to
GROUP BY nm_id
)
SELECT
f.sku,
f.orders_count,
f.orders_sum_rub,
COALESCE(ads.ad_spend, 0) AS ad_spend,
CASE
WHEN f.orders_count > 0 THEN COALESCE(ads.ad_spend, 0) / f.orders_count
ELSE NULL
END AS cpo,
f.cancel_count,
f.cancel_sum_rub
FROM f
LEFT JOIN ads ON ads.nm_id = f.nm_id
WHERE COALESCE(f.orders_count, 0) <> 0
OR COALESCE(f.orders_sum_rub, 0) <> 0
OR COALESCE(ads.ad_spend, 0) <> 0
ORDER BY f.orders_sum_rub DESC;
Формат ответа
Основной формат:
Артикул Заказы с отм. Сумма Реклама CPO
skirt-atlas-max-2 53 191 566 6 189 117
ELS04-black 37 125 121 8 151 220
ИТОГО 114 398 667 16 669 146
Если нужно показать отмены, добавлять колонку Отмены.
Для Telegram сравнение выводить второй строкой сравн., чтобы таблица не плыла.
Чек-лист
- Заказы WB взяты из полной дневной воронки
funnel_daily, не изorders/sales. COUNT(DISTINCT date)равен числу календарных дней периода.- При неполном дневном слое использовано только точное окно
funnel_period; частичная сумма не выдана. - Реклама взята из
ad_metrics_dailyза тот же календарный период. - CPO = реклама / заказы с отменами.
- В ответе указаны абсолютные даты периода.