Замеры с живых баз — 2026-08-02
Замерено основным агентом. Делегатам: НЕ ПЕРЕПРОВЕРЯТЬ, у вас нет доступа к серверам. Если цифра нужна для работы — берите отсюда.
Источники:
gwptd_kernelиgwptd_intakeна s3,lamoda_reportsна s2.
Потеря финальных статусов заказов
Механизм. Сборщики ходят окном 7 дней по дате СОЗДАНИЯ. Если заказ перешёл в финальный статус позже, к следующему прогону он вышел из окна и перехода мы не увидим никогда.
Доказанная потеря (финансы против фактов), Ozon:
| площадка | заказов | выручка |
|---|---|---|
Ozon FBS delivering | 620 | 7 849 498 ₽ |
Ozon FBO delivering | 91 | 1 557 742 ₽ |
Ozon rFBS delivering | 2 | 40 984 ₽ |
Ozon FBS awaiting_packaging | 4 | 0 ₽ |
| итого | 717 | 9 448 224 ₽ |
По ним в gwptd_intake.ozon_finance_transactions есть проводка
OperationAgentDeliveredToCustomer — товар передан покупателю, — а в
fact_orders статус не delivered. Самая старая 2026-04-17.
Подозрение — пересчитано по реестру статусов (было завышено):
Первая оценка «3736 заказов, 59 588 171 ₽» получена отбором по подстрокам в имени
статуса и потому включала финальные исходы Lamoda. После подключения
docs/domain/machine-rules/order-status-lifecycle.yaml одна сумма распалась на две:
| группа | заказов | сумма | что это значит |
|---|---|---|---|
достоверно не завершены (IN_FLIGHT) | 1576 | 19 950 309 ₽ | заказ реально ещё движется — это потеря |
смысл статуса не подтверждён (AMBIGUOUS) | 1887 | 34 294 206 ₽ | ждёт решения владельца, в потери не складывать |
AMBIGUOUS — это Lamoda On shelf, Rejected, Not delivered, Claimed ok/defective/used,
Postponed, Failed delivery, Delivery incidence и YM PICKUP, PARTIALLY_DELIVERED.
⚠️ Найден статус, которого нет в реестре: Lamoda «Refund investigation», 1 заказ, 14 900 ₽. Класс не определён — угадывать нельзя, это вход в расчёт потерь.
API-фильтры «по дате изменения» — проверены живьём 2026-08-02
| источник | вердикт |
|---|---|
YM updateFrom/updateTo | ✅ приняты, 36 из 100 заказов страницы созданы раньше окна |
Ozon last_changed_status_date в одиночку | ❌ HTTP 400 «filter.since and filter.to … must not be empty» |
Ozon в комбинации с обязательным since/to | ✅ учитывается: та же выборка сузилась со 100 до 0 |
Положительный пример нужного случая у Ozon: создано 8–30 дней назад (вне окна
базовой спеки), изменено за трое суток — 61 постинг, статусы delivered и
cancelled. Окна создания 30/60/90 дней дали одинаковый результат, поэтому
days: 30 достаточно. limit у v4 — жёстко ≤100.
Новые спеки подхватываются расписанием автоматически: у ozon-daily и
ym-daily режим endpointSet: marketplace с пустым exclude, то есть
--mp ozon перечисляет реестр спек целиком. Отдельная правка расписания не нужна,
генерируемый crontab.s3-next.txt не меняется.
Wildberries структурно неуязвим — не включать в детекторы потерь
У WB выкуп это отдельная строка в wb_sales со своим saleID. Пространства
идентификаторов не пересекаются:
ordered e0.i1b9ac3115974dfa07882186a4f030e52.0.0 (42 симв.)
cancelled 88677034626530973.0.0 (41 симв.)
sale S24176983605 (12 симв., префикс S)
Пересечение order_id между ordered и sale — ноль. Realized у WB это
новое событие со своей датой, оно попадает в окно в момент возникновения.
У Ozon, YM и Lamoda realized — смена статуса старой строки, и она теряется.
fact_orders хранит текущее состояние
У каждого order_id ровно один статус, обновление перетирает прошлый.
Проверено по всем девяти площадкам: ни одного заказа с двумя статусами.
Значит «добор по номеру» ложится на схему без изменений.
Что умеют API по дате изменения
| источник | фильтр «изменено после» | используем |
|---|---|---|
Ozon FBS /v4/posting/fbs/list | filter.last_changed_status_date.{from,to} | нет |
YM /v2/campaigns/{id}/stats/orders | updateFrom/updateTo (взаимоисключающи с dateFrom/dateTo) | нет |
Ozon FBO /v3/posting/fbo/list | нет | — |
Lamoda /api/v1/orders | серверных фильтров нет вовсе | — |
| WB | не установлено, зеркало спеки пустое | — |
Lamoda — худший случай: сервер не фильтрует, коллектор обрывает весь сбор на
первом заказе старше 7 дней (spec_runtime/runner.py:1810-1816, _stop_all).
Квота Wildberries: второй потребитель — мы сами
Оба контура собирают один аккаунт WB: s3 в 05:00, s2 в 09:00. Бьются в лимит на одних и тех же эндпоинтах:
| эндпоинт | 429 на s3 | 429 на s2 |
|---|---|---|
wb.item_rating | 48 | 43 |
wb.stock_turnover | 24 | 21 |
Из-за этого wb.stocks исключён из расписания с 11.07. Внешнего потребителя нет.
Решено 02.08: из прогона s2 убраны 10 эндпоинтов, которые не кормят ни ядро,
ни старый интерфейс: item_rating, stock_turnover, search_queries, tariffs,
claims, deductions, promo_calendar, balance, seller_health, cards_trash.
Основание — ядро читает ровно 26 таблиц интейка, и ни одной из целевых таблиц этих
спек среди них нет; легаси-маппингов у них тоже нет. Оба главных пожирателя квоты
(item_rating, stock_turnover) в этом списке. Бэкап: s2:/home/deploy/crontab.bak-20260802-wb.
Хранение по площадкам
| источник | строк | разных артикулов | сумма | вывод |
|---|---|---|---|---|
ym_storage | 16 994 | 722 | 440 102 ₽ | разбивка по товарам есть |
ozon_storage | 1 458 | 1 | 20 395 ₽ | суточный агрегат, к модели не привязать |
⚠️ По YM резолвится через dim_product_identifier (shopSku, mp_id=6,
is_current=1) только 230 из 722. На 492 нерезолвленных висит
131 865 ₽ из 440 102 — почти треть. Простая проекция потеряет их молча.
Докстрока build_fact_storage.py:17 требует «mapped/unallocated contract».
Дыры в правах
— закрыто 02.08, миграцияgwptd_monitor— нет SELECT наdim_marketplacekernel/schemas/migrations/2026-08-02_monitor_dim_marketplace_grant.sqlприменена.gwptd_collector— нет прав наcollector_runtime_pointerиcollector_lease, коллектор работает без DB-lease и через legacy-маршрутизацию.
Состояние ядра после бэкфилла 01.08
Остатки, цены и заказы за 90 дней — ноль пропусков. Июльский рейтинг
пересчитан на build_all 02.08, экспозиция 21 → 27.09 дня.
Инструменты, удалённые с ветки, но живые в master
Коммит 2e2bae46 от 14.07 удалил 1306 строк: replay_wb_raw_payloads.py
(ре-деривация intake из raw_payload без обращения к API) и
backfill_intake_from_archive.py с диапазоном дат. Живы в master и в
/Volumes/2TB-64/NEW_MP/gwptd-s2-master/.
Первый живой прогон спек добора — 02.08 вечером
Обе спеки отработали status=success, 0 пропущено, ни одного 429.
Ozon (run_id=4410, 1470 постингов) исправил 444 заказа:
| стало | было | заказов | сумма |
|---|---|---|---|
delivered | delivering | 254 | 3 720 155 ₽ |
cancelled | delivering | 122 | 1 581 758 ₽ |
delivering | awaiting_deliver | 38 | 634 559 ₽ |
awaiting_deliver | awaiting_packaging | 28 | 441 443 ₽ |
YM (run_id=4411, 1243 заказа) исправил 299:
| стало | было | заказов | сумма |
|---|---|---|---|
DELIVERED | PICKUP | 117 | 611 749 ₽ |
DELIVERED | DELIVERY | 45 | 295 276 ₽ |
CANCELLED_IN_DELIVERY | PICKUP | 59 | 385 554 ₽ |
RETURNED | DELIVERED | 17 | 119 834 ₽ |
RETURNED | PICKUP | 9 | 61 858 ₽ |
| прочие переходы | 52 | 246 125 ₽ |
⚠️ Коррекция идёт в обе стороны. Строки RETURNED ← DELIVERED — это заказы,
которые ядро считало реализованными, а они возвращены. Рейтинг считается от
продаж за вычетом возвратов, поэтому добор не только добавляет выручку, но и
снимает фантомную.
Это снимает часть неопределённости по классу AMBIGUOUS
YM PICKUP — промежуточный, не конечный. Доказано наблюдением: 117 заказов
из PICKUP перешли в DELIVERED, ещё 59 — в CANCELLED_IN_DELIVERY. Реестр
относил его к AMBIGUOUS из осторожности («не подтверждено, ожидание выдачи это
или уже состоявшееся получение»). Теперь подтверждено: ожидание.
Решение о переводе PICKUP в IN_FLIGHT остаётся за владельцем, но данных для
него достаточно. По статусам Lamoda такого наблюдения нет — у неё нет фильтра по
дате изменения, поэтому её AMBIGUOUS так и остаются открытыми.
⚠️ Поправка 03.08: собранное НЕ доходило до фактов
Первый прогон спек добора показал 743 расхождения между тем, что вернул
маркетплейс, и тем, что лежит в fact_orders. Утверждение «они станут
delivered на ближайшем пересчёте» было неверным.
approved_payload_join (build_input_projection.py:195) подшивает одобренный
payload с фильтром по точному коду эндпоинта, а OZON_POSTING_ENDPOINTS
сопоставлял таблице ровно один код. Строки с *_changed отсекались.
Исправлено (коммит 24b9aa24): одна intake-таблица может законно
наполняться несколькими эндпоинтами. Проверено на живой базе s3 тем же
approved-join, что использует боевой INSERT:
| выборка ядра | заказов | из них delivered |
|---|---|---|
| только базовая спека | 4780 | 2030 |
| базовая + добор | 4829 | 2233 |
Добор добавляет 49 заказов, которых ядро не видело вовсе, и переводит 203 в
delivered. В 1470 заказах побеждает строка добора — через MAX(id), без
всякого приоритета по коду эндпоинта.
Это же ограничение объясняет, почему для Lamoda не поможет отдельная спека: её пришлось бы так же вносить в сопоставление ядра, а сама проблема Lamoda не в фильтре, а в обрыве сбора и в отсутствии серверного фильтра по дате изменения.
Ozon FBO: финансы дают дату, но НЕ статус
OZON_FBO_DELIVERY_ENRICH_SQL (build_fact_orders.py:842) по проводке
OperationAgentDeliveredToCustomer проставляет fo.delivery_date — и только
его. Статус остаётся прежним. Поэтому 91 заказ FBO на 1 557 742 ₽ висят в
delivering, хотя передача покупателю доказана финансами.
Фильтра по дате изменения у /v3/posting/fbo/list нет, поэтому единственный
путь закрыть FBO — выводить исход из финансов. Это меняет то, что считается
продажей, и потому требует решения владельца, а не только кода.
Lamoda: статус каждого заказа собирался всё это время (замер 03.08)
Спека lamoda.status_dates бьёт в тот же эндпоинт /api/v1/orders, что и
lamoda.orders, но без itemStop: проходит все ~449 страниц за ~41 минуту,
ежедневно, и пишет order_id, статус уровня заказа и дату статуса.
Поэтому расширять окно lamoda.orders с 7 до 30 дней не понадобилось — а
стоило бы это ~43 минуты в сутки (1757 заказов против 239, и enrich делает
отдельный GET детали на каждый). Слот бы не выдержал: пакет 08:00 заканчивается
в 08:49, пересчёт ядра в 09:15. Разбивка пакета:
| эндпоинт | секунд |
|---|---|
lamoda.status_dates | 2463 |
lamoda.orders | 344 |
lamoda.nomenclatures | 106 |
| остальные четыре | < 35 каждый |
Расхождения статуса — 1329 заказов. Крупнейшие:
| в свежих данных | в fact_orders | заказов | выручка |
|---|---|---|---|
Returned | Rejected | 400 | 9 480 901 ₽ |
Returned | On shelf | 389 | 9 748 387 ₽ |
Returned | Not delivered | 82 | 1 991 002 ₽ |
Returned | Shipped | 35 | 791 986 ₽ |
Claimed defective | Delivered | 10 | 125 943 ₽ |
Claimed ok | Delivered | 9 | 152 537 ₽ |
Влияние на признание продаж мало: продажами становятся 0, перестают быть 22 на 319 133 ₽.
Сколько это снимает с «неясного» остатка
| статус | всего старше 30 дней | разрешается | выручка |
|---|---|---|---|
On shelf | 619 | 348 | 6 906 660 ₽ |
Rejected | 421 | 330 | 5 801 773 ₽ |
Not delivered | 140 | 74 | 1 455 238 ₽ |
Claimed ok/defective/used | 220 | 0 | — |
Итого 756 заказов на ~14,2 млн ₽ из 34,29 млн уходят без вопросов к
владельцу. Семейство Claimed * не разрешается: там статус тот же и в свежих
данных, нужен человек, знающий кабинет Lamoda.
Ozon: 717 заказов доказанно доставлены, но не признаны продажей
| схема | статус сейчас | заказов | выручка |
|---|---|---|---|
| FBS (3) | delivering | 620 | 7 849 498 ₽ |
| FBO (5) | delivering | 91 | 1 557 742 ₽ |
| FBS (3) | awaiting_packaging | 4 | 0 ₽ |
| rFBS (4) | delivering | 2 | 40 984 ₽ |
Риск: 19 из них имеют запись в fact_returns (не помеха — возврат
вычитается отдельно), 33 имеют финансовую операцию с Return/Cancel в
operation_type — этих исключать, доказательство перестаёт быть однозначным.