AI Asist
v2
Д
Д

Плейбук: «Заказы WB» за любой период

← Заметки  ·  Раздел: Wildberries  ·  Файл: data/notes/wb/orders-funnel-playbook.md

Плейбук: «Заказы 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 сравнение выводить второй строкой сравн., чтобы таблица не плыла.


Чек-лист

  1. Заказы WB взяты из полной дневной воронки funnel_daily, не из orders/sales.
  2. COUNT(DISTINCT date) равен числу календарных дней периода.
  3. При неполном дневном слое использовано только точное окно funnel_period; частичная сумма не выдана.
  4. Реклама взята из ad_metrics_daily за тот же календарный период.
  5. CPO = реклама / заказы с отменами.
  6. В ответе указаны абсолютные даты периода.
Фильтры