Перейти к основному содержимому

UC-RWD-015. Настройка индивидуальных условий вознаграждения по области и сроку (акции и boost)

Назначение

Описывает единый scoped + time-bounded паттерн настройки условий вознаграждения (ADR-0068) в трёх слоях, каждый из которых задаётся на область специфичности и период действия, а вместе они складываются в итоговую комиссию агента по сделке:

  1. Ставка застройщика — правило вознаграждения по оси каталога Застройщик → Проект → Группа планировок → Планировка; временная акция застройщика (например 10% → 12% на неделю) выражается второй, более специфичной записью с ограниченным периодом.
  2. Удержание ПИН — условия платформенной комиссии по оси Застройщик → Проект → Группа планировок с уточнением по агентству; временная акция ПИН (пониженное удержание на срок промо) — обычной записью с ограниченным EffectiveTo.
  3. Boost агентства — мотивационная кампания, поднимающая долю набора агентов на период (например 4% → 7%), поверх постоянной комиссионной политики агентства.

Каждый слой резолвится по правилу «наиболее специфичное активное на момент asOf»; более специфичная и/или более поздняя (по периоду) запись перекрывает базовую на время своего действия, не переписывая её. Применённая область и период снимаются в снапшот начисления — расчёт объясним постфактум и не пересчитывается при последующей смене условий. Связан с контекстом rewards.

Metadata

ПолеЗначение
IDUC-RWD-015
Контекстrewards (см. docs/06-services/rewards/)
Типосновной (кросс-слойная настройка условий вознаграждения)
Статусdraft

Актор

ПолеЗначение
РольМенеджер застройщика (слой 1); Менеджер ПИН (слой 2); Руководитель/администратор агентства (слой 3); System — резолюция при завершении сделки и превью в каталоге
Permissions / scopeСлой 1 — rewards.rules.manage (scope организация-застройщик); слой 2 — rewards.platform-terms.manage (платформенный scope); слой 3 — rewards.commission-policy.manage (scope организация-агентство)
TenantСлой 1 — TenantId = CompanyId застройщика (Type = Developer); слой 2 — вне tenant-модели (платформенная сущность); слой 3 — TenantId = CompanyId агентства (ADR-0052)
Record-levelНе применяется (все три — конфигурация уровня области/tenant)

Предусловие

  • Справочник каталога (Project/LayoutGroup/Layout) наполнен в реплике rewards consumer-ами chessboard — нужен для валидации ссылок области при активации (INV-4 правила, ссылки условий ПИН).
  • Для слоя 3: состав агентов агентства синхронизирован (реплика identity) — нужен для проверки набора boost при активации (INV-11).
  • Каждый актор аутентифицирован и имеет право своего слоя; компании-акторы Status = Active.

Триггер

ПолеЗначение
ИсточникUI button
Триггерящий элементЛК застройщика → «Вознаграждения агентов» → «Создать правило/акцию»; админ-панель ПИН → «Условия платформенной комиссии» → «Создать/акция»; ЛК агентства → «Комиссии» → «Boost-кампании» → «Создать»

Основной Поток

Слой 1. Застройщик настраивает scoped-ставку и акцию

  1. Менеджер застройщика заводит базовую ставку без измерений каталога (подходит любому объекту застройщика): POST /rewards/developer/reward-rules с calculationBase (BasePrice/FinalPrice), commissionPercent, priority, effectiveFrom, effectiveTo? = null (бессрочно), пустыми conditions, и активирует правило (/activate) — переход Draft → Active (UC-RWD-001).
  2. На неделю акции заводит более специфичную запись: conditions.projectIds = [ЖК-X] (или layoutGroupIds/layoutIds для более узкой области), commissionPercent = 12, effectiveFrom/effectiveTo на границы недели, и активирует её. Система при активации проверяет: диапазон процента (INV-1), существование ссылок области в реплике каталога (INV-4), свободность Priority среди активных (INV-3) и непересечение периодов среди правил той же сигнатуры специфичности (INV-9, иначе REWARDS_REWARD_RULE_OVERLAP). Публикуется снапшот набора активных правил pin.rewards.commission-rules.v1 (и per-rule pin.rewards.reward-rule-changed.v1) → catalog пересчитывает денормализованную комиссию (UC-RWD-002).
  3. Резолвер ставки для объекта ЖК-X в период акции выберет запись 12% (более специфичный Project + покрывающий период перекрывает базовые 10%); вне периода и для объектов других ЖК — базовые 10%. По завершении акции запись архивируется или истекает по EffectiveTo; базовая ставка ретроактивно не менялась.

Слой 2. ПИН настраивает scoped-удержание и акцию

  1. Менеджер ПИН заводит глобальные страховочные условия удержания (все измерения null): POST /api/v1/platform/commission-terms с retentionPercentagePoints (0.25–1.50 п.п.), effectiveFrom, и активирует их (UC-RWD-007). Модель монетизации — только процентные пункты ставки застройщика (ADR-0066): при ставке 10% и удержании 1.00 п.п. платформа получает 1% цены, пул агентства — 9%.
  2. Для точечной настройки заводит более специфичные условия на пересечении оси объекта (developerCompanyId → projectId → layoutGroupId) и измерения agencyCompanyId; ось объекта заполняется сверху вниз (проект требует застройщика, группа — проект; иначе REWARDS_PLATFORM_TERMS_SCOPE_INVALID, INV-PT-5). Акция ПИН (пониженное удержание на срок) — запись с ограниченным EffectiveTo. Активация проверяет диапазон пунктов (INV-PT-1) и непересечение периодов той же комбинации измерений (INV-PT-2, иначе REWARDS_PLATFORM_TERMS_OVERLAP).
  3. При завершении сделки consumer резолвит удержание на asOf = CompletedAt: наиболее специфичная активная запись по рангу AgencyLayoutGroup → … → Global; при отсутствии подошедших — fallback 0 п.п. (метрика rewards_platform_terms_missing_total). Guard конфигурации (INV-PT-4): если удержание ≥ CommissionPercent применённого правила застройщика (пул ≤ 0) — начисление не создаётся, событие в DLQ (REWARDS_PLATFORM_RETENTION_EXCEEDS_RULE, разбор ПИН).

Слой 3. Агентство настраивает boost на период

  1. Руководитель агентства заводит постоянную комиссионную политику (удержание агентства в %/фикс, с оверрайдами по агентам; делит пул = комиссия − удержание ПИН) и активирует её (UC-RWD-005 фаза 1).
  2. Поверх политики заводит временный agent-boost: POST /api/v1/agency/agent-boost-campaigns с boostRule (Kind ∈ {AgentPercentOfPool, AgentPercentOfPrice}, повышенная доля агента напрямую), agentUserIds (1..1000), effectiveFrom/effectiveTo, и активирует кампанию (/activate). Активация проверяет существование/активность агентов набора (INV-11, REWARDS_AGENT_BOOST_TARGETS_INVALID) и непересечение периодов по общим агентам с другими активными кампаниями (INV-9, REWARDS_AGENCY_POLICY_OVERLAP) — на любой asOf к агенту применима не более одной кампании. В отличие от политики, активная кампания предыдущую не архивирует.
  3. При подтверждении начисления (Confirm, UC-RWD-004) резолвится удержание агентства: если сделка попала в период boost и агент входит в набор — доля агента считается по BoostRule вместо базового правила политики; иначе — по политике. Boost применяется только внутри rewards (интеграционных событий не публикует).

Резолюция и снапшот (System)

  1. При завершении сделки итоговое распределение складывается по слоям (в каталоге — та же резолюция для превью, UC-RWD-002): CommissionAmount = база × ставка застройщика (слой 1) → PlatformFeeAmount = база × удержание ПИН (слой 2) → пул = CommissionAmount − PlatformFeeAmount → доли Agency/Agent по политике с учётом boost (слой 3). В начисление снимается снапшот применённой области и периода каждого слоя (AppliedRuleSnapshot, AppliedPlatformTermsId + область/период, AppliedBoostCampaignId) — последующая смена условий существующие начисления не пересчитывает (неретроактивность, ADR-0068).

Альтернативные Потоки

A1. Пересечение периодов внутри одной специфичности

  1. Активация правила застройщика/условий ПИН/boost-кампании с периодом, пересекающим уже активную запись той же сигнатуры специфичности (для boost — по общему агенту), отклоняется: слой 1 — REWARDS_REWARD_RULE_OVERLAP, слой 2 — REWARDS_PLATFORM_TERMS_OVERLAP, слой 3 — REWARDS_AGENCY_POLICY_OVERLAP (все 409). Гарантирует детерминизм резолюции: на любой asOf в одной области активна не более одной записи. Актор сдвигает период (или набор агентов) и повторяет активацию.

A2. Некорректная область

  1. Слой 2: ось объекта заполнена не сверху вниз (projectId без developerCompanyId, layoutGroupId без projectId) → REWARDS_PLATFORM_TERMS_SCOPE_INVALID (400, INV-PT-5). Слой 1: ссылки projectIds/layoutGroupIds/layoutIds не найдены в реплике или чужого застройщика → REWARDS_RULE_LAYOUT_NOT_FOUND (400, INV-4). Актор исправляет область и повторяет.

A3. Удержание съедает ставку правила

  1. На момент CompletedAt применимое удержание ПИН ≥ CommissionPercent применённого правила застройщика (пул ≤ 0) → ни одно начисление по сделке не создаётся, событие в DLQ (REWARDS_PLATFORM_RETENTION_EXCEEDS_RULE, INV-PT-4). Менеджер ПИН правит условия либо согласует ставку с застройщиком; после исправления событие переигрывается из DLQ (ретроактивное исправление требует условий с периодом, покрывающим asOf).

A4. Область без условий — fallback

  1. Слой 2: на asOf нет ни специфичных, ни глобальных условий → удержание 0 п.п. (PlatformFeeAmount = 0, AppliedPlatformTermsId = null), пул = всей комиссии; warn-метрика rewards_platform_terms_missing_total. Слой 1: нет подошедшего правила застройщика → комиссия в каталоге не показывается, агентское начисление по сделке не создаётся (UC-RWD-003). Слой 3: сделка вне периода boost или агент не в наборе → доля считается базовой политикой. Не ошибки.

A5. Досрочное завершение и архив

  1. Активную запись любого слоя можно досрочно завершить сокращением EffectiveTo (единственная разрешённая мутация в Active: слой 1 — INV-5, слой 2 — INV-PT-3, слой 3 — INV-10; сокращение пересечений не создаёт) или архивировать. С момента EffectiveTo/архива запись выбывает из резолюции; уже созданные начисления по снапшотам не пересчитываются. Слои 1–2 публикуют изменение в catalog; boost событий не публикует.

A6. Гонка активаций

  1. Две параллельные активации в одной области разрешаются блокировкой ключа специфичности (слои 1–2 — непересечение периодов; слой 1 дополнительно — partial-unique по Priority; слой 3 — блокировка по общим агентам): проходит одна, проигравший получает соответствующий overlap-/priority-код (409) и повторяет после пересмотра. Гонка активации boost с Confirm начисления сериализуется: Confirm применяет boost, только если прочитал кампанию уже активной (иначе AppliedBoostCampaignId = null), промежуточного состояния с двумя пересекающимися кампаниями по агенту не наблюдается.

Постусловие

  • В каждой области каждого слоя на любой asOf активна не более одной записи данной специфичности (детерминизм промо-акций и boost); история — цепочка записей со своими периодами и областями.
  • Итоговая комиссия по сделке считается сквозь три слоя по наиболее специфичным активным записям на CompletedAt; применённые область и период каждого слоя зафиксированы снапшотом в начислении.
  • Смена/архив условий меняет только будущие расчёты (каталог и новые начисления); созданные начисления неретроактивны (объяснимы своими снапшотами).

Возможные Ошибки

КодHTTPУсловиеПоведение клиента
403 FORBIDDEN403Нет права своего слоя (rewards.rules.manage / rewards.platform-terms.manage / rewards.commission-policy.manage)Скрыть управление
REWARDS_RULE_PERCENT_OUT_OF_RANGE400Слой 1: commissionPercent вне (0; 100] (INV-1)Подсветить процент
REWARDS_RULE_RANGE_INVALID400Слой 1: некорректный диапазон комнат/площади или effectiveTo <= effectiveFrom (INV-2)Подсветить диапазон
REWARDS_RULE_LAYOUT_NOT_FOUND400Слой 1: ссылка области не в реплике/чужого застройщика (INV-4, A2)Показать отсутствующие id
REWARDS_RULE_PRIORITY_TAKEN409Слой 1: priority занят активным правилом (INV-3, A6)Предложить другой приоритет
REWARDS_REWARD_RULE_OVERLAP409Слой 1: пересечение периодов правил одной специфичности (INV-9, A1)Сдвинуть период
REWARDS_PLATFORM_TERMS_RETENTION_OUT_OF_RANGE400Слой 2: пункты вне 0.25–1.50 (INV-PT-1)Подсветить поле пунктов
REWARDS_PLATFORM_TERMS_SCOPE_INVALID400Слой 2: ось объекта не сверху вниз (INV-PT-5, A2)Исправить область
REWARDS_PLATFORM_TERMS_OVERLAP409Слой 2: пересечение периодов условий одной специфичности (INV-PT-2, A1)Сдвинуть период
REWARDS_AGENT_BOOST_RULE_INVALID400Слой 3: boostRule не форма доли агента / value вне 0..100 (INV-8)Исправить правило boost
REWARDS_AGENT_BOOST_TARGETS_INVALID400Слой 3: набор агентов пуст/дубликат/> 1000 / недоступный агент при активации (INV-11, A6)Исправить набор
REWARDS_AGENT_BOOST_PERIOD_INVALID400Слой 3: effectiveTo <= effectiveFrom (INV-12)Исправить период
REWARDS_AGENCY_POLICY_OVERLAP409Слой 3: пересечение периодов кампаний по общему агенту (INV-9, A1)Сдвинуть период/набор
REWARDS_PLATFORM_RETENTION_EXCEEDS_RULE— (DLQ)Резолюция: удержание ≥ ставки правила (INV-PT-4, A3)Разбор ПИН + replay
REWARDS_CONCURRENCY_CONFLICT409Параллельная мутация записи слоя (A6)Перечитать и повторить

Доменные События

СобытиеКогда поднимаетсяSubscribers
RewardRuleCreatedEvent / RewardRuleActivatedEvent / RewardRuleArchivedEventСлой 1: создание/активация/архив (в т.ч. акции)in-proc аудит
PlatformTermsCreatedEvent / PlatformTermsActivatedEvent / PlatformTermsArchivedEventСлой 2: создание/активация/архив условий (в т.ч. акции ПИН)in-proc аудит
AgentBoostCampaignCreatedEvent / AgentBoostCampaignActivatedEvent / AgentBoostCampaignArchivedEventСлой 3: жизненный цикл boost-кампанииin-proc аудит

Kafka Интеграционные События

СобытиеTopicКогда публикуетсяConsumers
CommissionRulesChangedKafkaEvent.v1pin.rewards.commission-rules.v1 (key = DeveloperCompanyId)Слой 1: любое изменение набора активных правил застройщика (снапшот набора, ADR-0068)catalog — атомарная замена денормализованного набора и пересчёт комиссий
RewardRuleChangedKafkaEvent.v1pin.rewards.reward-rule-changed.v1 (key = Id)Слой 1: Activate/Deactivate/Archive/смена EffectiveTocatalog — per-rule дельта того же изменения

Слой 2 (условия ПИН) и слой 3 (boost) Kafka-событий не публикуют — применяются только внутри rewards (при создании и подтверждении начисления).

Acceptance Criteria

#КритерийПроверка
AC1Акция застройщика (более специфичный Project + период) перекрывает базовую ставку на время действия; вне периода — базоваяобъект ЖК-X в период → 12%, вне → 10%
AC2Пересечение периодов одной специфичности отклоняется в каждом слое (INV-9 / INV-PT-2 / INV-9 boost)активация перекрывающей записи → соответствующий overlap-код 409
AC3Ось объекта условий ПИН заполняется сверху вниз (INV-PT-5); наиболее специфичная активная запись побеждает по рангуprojectId без застройщика → 400; резолюция двух условий → выигрывает специфичная
AC4Boost применяется агенту из набора в период кампании поверх политики; вне периода/набора — базовая доляConfirm с asOf в периоде boost и агентом набора → доля по BoostRule
AC5Пересечение периодов boost по общему агенту отклоняется (INV-9)активация второй кампании на того же агента с пересечением → 409
AC6Удержание ≥ ставки правила → событие в DLQ, начисления не созданы (INV-PT-4); после исправления replay создаётусловия 1.50 п.п. при правиле 1% → DLQ
AC7Итоговое распределение считается сквозь три слоя; применённая область/период каждого слоя зафиксированы снапшотомзавершение сделки → снапшоты AppliedRule/AppliedPlatformTerms/AppliedBoostCampaign
AC8Смена/архив условий не пересчитывает существующие начисления (неретроактивность)активировать новую акцию → перечитать начисление: суммы прежние
AC9Досрочное сокращение EffectiveTo выводит запись из резолюции с указанного момента, не создавая пересеченийPATCH effective-to → объект после момента резолвится в базовую запись
AC10Изменения слоя 1 распространяются в каталог снапшотом набора; слои 2–3 внешних событий не публикуютконтракт-тест commission-rules; отсутствие Kafka у условий/boost

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные Документы