UC-RWD-008. Платформа настраивает ставки реферального вознаграждения
Назначение
Описывает сценарий управления ставками реферального вознаграждения: Менеджер ПИН задаёт процент
вознаграждения партнёра от конечной цены сделки приведённого им инвестора — на одном из четырёх уровней
специфичности (партнёр × застройщик / партнёр / застройщик / глобально, ADR-0066), с периодом действия
(в т.ч. временно повышенные ставки на акцию), — активирует ставку и получает её автоматическое
применение: при создании реферального начисления резолвится наиболее специфичная действующая ставка на
момент завершения сделки. Связан с контекстом rewards.
Metadata
| Поле | Значение |
|---|---|
| ID | UC-RWD-008 |
| Контекст | rewards (см. docs/06-services/rewards/) |
| Тип | основной |
| Статус | draft |
Актор
| Поле | Значение |
|---|---|
| Роль | Менеджер ПИН |
| Permissions / scope | rewards.referral-rates.manage (управление и чтение реестра); scope — платформа |
| Tenant | вне tenant-модели — платформенная сущность (владелец денег реферальной программы — ПИН, ADR-0052) |
| Record-level | не применяется |
Предусловие
- Актор аутентифицирован, права платформенного scope.
- Для персональных ставок: партнёр существует в
identity; для ставок по застройщику: компанияType = Developerсуществует. - Глобальная бессрочная ставка — страховочный уровень резолюции — заводится первым шагом внедрения (без неё сделки без специфичных ставок реферальных начислений не порождают, A5).
Триггер
| Поле | Значение |
|---|---|
| Источник | UI button |
| Триггерящий элемент | Админ-панель ПИН → «Финансы» → «Реферальные ставки» → «Создать» / «Активировать» / «Завершить досрочно» / «Архивировать» |
Основной Поток
Фаза 1. Создание и активация ставки
- Менеджер ПИН открывает реестр ставок (
GET /api/v1/platform/referral-rate-rules, фильтры по партнёру/застройщику/статусу/специфичности) и нажимает «Создать». - Фронт вызывает
POST /api/v1/platform/referral-rate-rulesс телом{ partnerUserId?: Guid, developerCompanyId?: Guid, percent: decimal (0 < x <= 100, scale 2), effectiveFrom: DateTimeOffset, effectiveTo?: DateTimeOffset (> effectiveFrom; null = бессрочно), comment?: string (<= 2000) }(../api/platform/create-referral-rate-rule.md). Специфичность (ReferralRateScope) не передаётся — выводится из заполненности измерений:PartnerDeveloper/Partner/Developer/Global. - Система создаёт ставку-черновик: проверка диапазона процента (INV-RR-1, иначе
REWARDS_REFERRAL_RATE_PERCENT_OUT_OF_RANGE) и периода (effectiveTo > effectiveFrom, иначеREWARDS_REFERRAL_RATE_PERIOD_INVALID); запись вStatus = Draft— в резолюции не участвует. Результат —{ ruleId, scope, status = Draft }. - Менеджер нажимает «Активировать»; фронт вызывает
POST /api/v1/platform/referral-rate-rules/{ruleId}/activate { expectedVersion }(../api/platform/activate-referral-rate-rule.md). - Система активирует ставку (INV-RR-2): периоды активных ставок той же пары значений
(
PartnerUserId,DeveloperCompanyId, включаяnull) не пересекаются — пересечение отклоняетсяREWARDS_REFERRAL_RATE_OVERLAP(гарантия детерминированности резолюции: внутри уровня на любой момент действует не более одной ставки). Ставка переходитDraft → Active. Результат —{ ruleId, status = Active }.
Фаза 2. Резолюция при создании реферального начисления (автоматически, system)
crmзавершает сделку приведённого инвестора → реферальная ветка consumer-а (../consumers/crm-deal-completed.md) резолвит ставку на моментasOf = CompletedAt: кандидаты —Active-ставки, период покрываетasOf, измерения совместимы со сделкой (PartnerUserIdпуст или равен рефереру связи;DeveloperCompanyIdпуст или равен застройщику сделки).- Из кандидатов выбирается наиболее специфичный непустой уровень:
PartnerDeveloper→Partner→Developer→Global(«специфичное побеждает»); внутри уровня кандидат ровно один (INV-RR-2). Алгоритм — ../domain/referral-rate-rule.md. - В реферальное начисление снимается снапшот применённой ставки (
AppliedRateSnapshot:RateRuleId,Scope,Percent, период, версия ставки) и считаетсяAmount = FinalPrice × Percent / 100(banker's, 2 знака, INV-RA-2) — последующая смена ставок существующие начисления не пересчитывает (UC-RWD-009).
Альтернативные Потоки
A1. Правка черновика
PUT /api/v1/platform/referral-rate-rules/{ruleId}(те же поля +expectedVersion) — только вDraft(../api/platform/update-referral-rate-rule.md). Возвращается к шагу 4.
A2. Пересечение периодов при активации
- На шаге 5 найдена активная ставка той же пары значений с пересекающимся периодом →
REWARDS_REFERRAL_RATE_OVERLAP(409). Менеджер либо правит период черновика (A1), либо досрочно завершает действующую ставку (A3) и повторяет активацию.
A3. Временная акция и досрочное завершение
- Повышенная ставка на период акции — обычная ставка с ограниченным
EffectiveFrom/EffectiveTo(например,Developer-уровня): на время акции резолюция выбирает её, послеEffectiveTo— снова базовую (страховочнуюGlobalили персональную). - Досрочное завершение действующей ставки — единственная разрешённая мутация в
Active(INV-RR-3): сокращениеEffectiveTo(не раньше текущего момента; сокращение пересечений не создаёт); остальные поля иммутабельны —REWARDS_REFERRAL_RATE_ACTIVE_IMMUTABLE. Начисления, созданные в период действия, не пересчитываются (снапшот).
A4. Архивирование ставки
POST /api/v1/platform/referral-rate-rules/{ruleId}/archive { expectedVersion }(../api/platform/archive-referral-rate-rule.md) — ставка выводится из резолюции навсегда; снапшоты в созданных начислениях живы. Уровень остаётся резолюции нижележащим специфичностям (вплоть доGlobal). Завершается.
A5. Ставка не найдена при начислении
- На момент
CompletedAtни один уровень не дал кандидата → реферальное начисление не создаётся; warn-метрикаrewards_referral_accrual_no_rate_total. Комиссионная ветка сделки не затронута. - Менеджер ПИН заводит недостающую ставку (страховочную
Global— бессрочно) и при необходимости инициирует replay событий завершения сделок — начисления создадутся по ставкам, действующим наasOf = CompletedAt(ретроактивное покрытие требует периода, включающего прошлое). Завершается.
A6. Гонка двух активаций
- Две параллельные активации ставок одной пары значений с пересекающимися периодами → активные ставки
той же специфичности читаются под блокировкой: побеждает первая, проигравшая получает
REWARDS_REFERRAL_RATE_OVERLAP(409) и пересматривает период.
Постусловие
- Внутри каждой пары значений (
PartnerUserId,DeveloperCompanyId) периоды активных ставок не пересекаются (INV-RR-2) — резолюция детерминирована на любой момент времени. - Новые реферальные начисления по сделкам с
CompletedAtв периоде действия получают снапшот ставки (AppliedRateSnapshot) и сумму по ней; созданные ранее начисления не пересчитаны. - Партнёру ставка отдельно не отдаётся — применённый процент виден в снапшоте его начисления
(
rewards.referral.view-own, UC-RWD-009).
Возможные Ошибки
| Код | HTTP | Условие | Поведение клиента |
|---|---|---|---|
403 FORBIDDEN | 403 | Нет rewards.referral-rates.manage | Скрыть управление ставками |
REWARDS_REFERRAL_RATE_PERCENT_OUT_OF_RANGE | 400 | Процент вне (0; 100] (INV-RR-1) | Подсветить поле процента |
REWARDS_REFERRAL_RATE_PERIOD_INVALID | 400 | effectiveTo <= effectiveFrom | Подсветить период |
REWARDS_REFERRAL_RATE_OVERLAP | 409 | Пересечение периодов активных ставок одной специфичности (INV-RR-2, A2/A6) | Поправить период либо завершить действующую ставку |
REWARDS_REFERRAL_RATE_ACTIVE_IMMUTABLE | 409 | Правка Active сверх сокращения EffectiveTo (INV-RR-3, A3) | Создать новый черновик |
REWARDS_REFERRAL_RATE_INVALID_STATUS_TRANSITION | 409 | Активация не из Draft / повторная активация | Обновить карточку ставки |
REWARDS_REFERRAL_RATE_NOT_FOUND | 404 | Ставка не существует | Вернуться к реестру |
REWARDS_CONCURRENCY_CONFLICT | 409 | expectedVersion не совпал | Перечитать и повторить |
Доменные События
| Событие | Когда поднимается | Subscribers |
|---|---|---|
ReferralRateRuleCreatedEvent | Успешное создание черновика (шаг 3) | in-proc аудит |
ReferralRateRuleActivatedEvent | Draft → Active (шаг 5) | in-proc аудит |
ReferralRateRuleArchivedEvent | * → Archived (A4) | in-proc аудит |
Kafka-события ставки не публикуют — применяются только внутри rewards при создании реферального
начисления (../domain/referral-rate-rule.md).
Acceptance Criteria
| # | Критерий | Проверка |
|---|---|---|
| AC1 | Специфичное побеждает: при активных ставках всех четырёх уровней применяется PartnerDeveloper | сделка партнёра по застройщику при 4 ставках → снапшот Scope = PartnerDeveloper |
| AC2 | Уровень без кандидата пропускается: без персональных ставок применяется Developer, затем Global | поэтапное отключение уровней → резолюция спускается ниже |
| AC3 | Пересечение периодов одной специфичности отклоняется при активации, включая гонку (INV-RR-2, A2/A6) | два периода с наложением → 409; параллельные активации → одна успешна |
| AC4 | Резолюция идёт на CompletedAt: сделка в период акции получает акционную ставку, после — базовую | две сделки до/после EffectiveTo акции → разные снапшоты |
| AC5 | Смена/архив ставки не пересчитывает созданные начисления (снапшот) | архивировать ставку → перечитать начисление: AppliedPercent прежний |
| AC6 | В Active меняется только EffectiveTo и только сокращением (INV-RR-3, A3) | попытка сменить процент → 409; сокращение периода → 200 |
| AC7 | Нет применимой ставки → начисление не создаётся, метрика rewards_referral_accrual_no_rate_total (A5) | сделка без покрытия ставками → нет записи + метрика |
| AC8 | Границы процента (0; 100] соблюдаются (INV-RR-1) | 0 / 100.01 → 400; 100 → 200 |
Обратные ссылки
Автоссылки: где используется этот документ.
- Use Cases (1): US-реестр: Финансовый блок ПИН (US-FIN)
- registries (1): Реестр use cases и user stories PIN
Связанные Документы
- Domain entity: ../domain/referral-rate-rule.md (поля, INV-RR-1..3,
ReferralRateScope, резолюция, stateDiagram). - API методы: ../api/platform/create-referral-rate-rule.md, ../api/platform/update-referral-rate-rule.md, ../api/platform/activate-referral-rate-rule.md, ../api/platform/archive-referral-rate-rule.md, ../api/platform/list-referral-rate-rules.md.
- Точка применения: ../consumers/crm-deal-completed.md
(реферальная ветка), ../domain/referral-accrual.md
(
AppliedRateSnapshot, INV-RA-2). - Факт связи «кто кого привёл»:
../../referral/domain/referral-bond.md (владелец денег —
rewards,referral— только связь). - Связанные UC: UC-RWD-009 (жизненный цикл реферального начисления), UC-RWD-007 (условия платформы — парная настройка финансового контура).
- ADR: 0066 (4 уровня специфичности, «специфичное побеждает»), ADR-0052, ADR-0056 (резолюция выборкой в память).