0022. Модель правил репрайсера с ограничениями по цене
Копия ADR из репозитория:
backend/docs/adr/0022-repricer-rules-model.md. Канонический текст — в репозитории; портал обновляется командойnpm run sync:adr.
- Статус: accepted
- Дата: 2026-06-25
- Связанные задачи: OP-62 (5.3 · Репрайсер)
Контекст и проблема
ТЗ 5.3: репрайсер автоматически изменяет цены на WB по условиям («если показатель X выполняет условие → цена ±Y»). Нужна модель правил с:
- условиями по показателям оцифровки (остаток/дни, рейтинг, маржа, разрыв с рынком);
- действием в двух форматах (абсолютный руб. / процент);
- ограничениями min/max цены (цена не выходит за допустимый диапазон);
- областью применения (один SKU / бренд / список nmId);
- мягким удалением (история применений сохраняется).
Рассмотренные варианты
-
Единая таблица
repricer_rules— все поля правила в одной строке;scope_valueхранится какtext(строка nmId / бренд / JSON-массив для list). Просто, читаемо, гибко. -
EAV / JSONB-условия — условие и действие хранятся в JSONB без фиксированной структуры. Гибкость, но сложная валидация на уровне БД и ORM.
-
Нормализованные таблицы условий и действий — отдельные таблицы
rule_conditions,rule_actions. Позволяет несколько условий/действий на правило, но избыточно для текущего ТЗ (одно условие + одно действие на правило).
Решение
Выбран вариант 1 — единая таблица с явно типизированными колонками и enum-ами
для метрики/оператора/типа-действия/scope. scope_value — text, интерпретируется
сервисом согласно scope. Валидация min <= max и корректности scope_value — на
уровне сервиса с BadRequestException.
Показатели условий (RepricerConditionMetric): stock_days, sales_rank,
margin_pct, price_gap_pct — покрывают данные оцифровки/РНП из OP-55–58.
Последствия
- Положительные: простая схема, прямая типизация, легко расширяется новыми метриками.
- Отрицательные: одно правило = одно условие + одно действие (сложные комбинированные правила потребуют следующего тикета).
- Что нужно сделать дальше: движок применения правил (OP-63+), интеграция с
PriceHistoryService(source=Repricer, уже есть в OP-61).