# metric-status-map.yaml — машинное зеркало STATUS-SEMANTICS.md
# Единый источник ЗНАЧЕНИЙ в коде: marketplace-collector-v3/kernel/etl/canon.py
# Этот файл — контракт для тестов: тест должен загрузить YAML и сверить с canon.py.
# Если YAML и код расходятся — это БАГ (правило governance-плана §6).
# Сверено с кодом: 2026-07-03 @ d7aaeab (логика kernel не менялась с e346dd5)

marketplace_groups:            # canon.PRIMARY_GROUPS
  wb:     { primary: 1, sub_mp: [1, 2],    schemes: { 1: FBO, 2: FBS } }
  ozon:   { primary: 3, sub_mp: [3, 4, 5], schemes: { 3: FBS, 4: rFBS, 5: FBO } }
  ym:     { primary: 6, sub_mp: [6, 8],    schemes: { 6: FBS, 8: FBY } }   # mp_id=7 (Express) ОТСУТСТВУЕТ в kernel
  lamoda: { primary: 9, sub_mp: [9],       schemes: {} }
  all_sub_mp: [1, 2, 3, 4, 5, 6, 8, 9]

realized_status:               # canon.REALIZED_STATUS — "ПРОДАНО"
  1: sale                      # WB FBO   (wb_sales, for_pay после комиссии)
  2: sale                      # WB FBS   (caveat: строк 'sale' нет вовсе -> realized mp2 = 0)
  3: delivered                 # Ozon FBS
  4: delivered                 # Ozon rFBS
  5: delivered                 # Ozon FBO
  6: DELIVERED                 # YM FBS
  8: DELIVERED                 # YM FBY
  9: Delivered                 # Lamoda (статус флипается на возврате)
  10: delivered                # Gold Apple: shipment-only rolling SKU aggregate (delivered-returned)

# Семантика ПО МЕТРИКЕ — какой набор статусов применяет каждая метрика.
metric_semantics:
  turnover:
    status_set: realized       # SETTLED (epic-05/01)
    net_of_returns: true       # net = sales_28d - returns_qty (в делителе)
    code: marketplace-collector-v3/kernel/etl/build_fact_turnover.py
  rating:
    status_set: realized       # SETTLED (владелец 2026-07-03): только реальные продажи, НЕ заказы
    net_of_returns: true       # profit на net (revenue-returns_amount) - cost*(qty-returns_qty)
    code: marketplace-collector-v3/kernel/etl/build_fact_rating.py
    decision: DNR-001          # ecca7f26 (demand) superseded
  shipment_distribution:
    status_set: realized
    code: data_api/routers/reports/shipment_distribution.py   # :24 импорт canon, :56-74 фильтр
  sales_endpoints:
    status_set: realized
    code: data_api/routers/reports/  # sales.py, bestsellers.py, dashboard.py, structure.py

# "Спрос" (demand) — НЕ используется в расчётах (DNR-001, 2026-07-03). Только справочно,
# чтобы будущий агент не спутал с realized и не вернул demand в рейтинг.
demand_status:
  used_in_metrics: false       # запрет: не возвращать в рейтинг/turnover
  rule: "все статусы заказа, только Lamoda=Delivered"
  lamoda_only: Delivered

# Контракт достоверности ответа Data API — НЕ путать со статусами заказа.
# Значения 1:1 с data_api/envelope.py:14-27 (и app/Support/DataStatus.php).
data_status:
  ok:      "запрос успешен, есть строки"
  no_data: "запрос успешен, 0 строк (легитимная пустота)"    # no_data != ok (DNR-007)
  stale:   "источник/зеркало недоступно (нет таблицы/гранта) -> деградированный ответ вместо 5xx"
  error:   "сбой обработки (баг развёртки бизнес-источника) -> 5xx + лог"

# Инварианты, проверяемые тестом YAML<->код:
invariants:
  - "realized_status ключи == canon.REALIZED_STATUS ключи и значения"
  - "marketplace_groups.*.sub_mp == canon.PRIMARY_GROUPS[primary]"
  - "mp_id=7 НЕ присутствует ни в одной группе"
  - "all_sub_mp == отсортированное объединение всех sub_mp"
