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

UC-RWD-007. Платформа настраивает условия удержания ПИН

Назначение

Описывает сценарий управления условиями платформенной комиссии: Менеджер ПИН задаёт, сколько процентных пунктов ставки застройщика платформа удерживает в свою пользу с каждой подтверждённой комиссии (0.25–1.50 п.п., ADR-0066) — персонально для агентства или глобальным дефолтом, — активирует условия и получает их автоматическое применение к новым начислениям: при завершении сделки consumer снимает снапшот условий на момент CompletedAt, а при подтверждении застройщиком строится строка распределения Platform. Связан с контекстом rewards.

Metadata

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

Актор

ПолеЗначение
РольМенеджер ПИН
Permissions / scopeУправление — rewards.platform-terms.manage, чтение реестра — rewards.platform-terms.view; scope — платформа
Tenantвне tenant-модели — платформенная сущность (владелец процесса — ПИН, ADR-0052)
Record-levelне применяется

Предусловие

  • Актор аутентифицирован, права платформенного scope.
  • Для per-agency условий: компания-агентство существует в identity (Type ∈ {Agency, Broker}).
  • Ставки застройщиков (правила вознаграждений, UC-RWD-001) настроены независимо — удержание применяется к любой ставке; конфликт «удержание ≥ ставки» ловится при начислении (A5, INV-PT-4).

Триггер

ПолеЗначение
ИсточникUI button
Триггерящий элементАдмин-панель ПИН → «Финансы» → «Условия платформенной комиссии» → «Создать» / «Активировать» / «Архивировать»

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

Фаза 1. Создание и активация условий

  1. Менеджер ПИН открывает реестр условий (GET /api/v1/platform/commission-terms, permission rewards.platform-terms.view, фильтры по агентству/статусу) и нажимает «Создать».
  2. Фронт вызывает POST /api/v1/platform/commission-terms с телом { agencyCompanyId?: Guid (null = глобальный дефолт), retentionPercentagePoints: decimal (0.25..1.50, scale 2), comment?: string (<= 2000) } (../api/platform/create-platform-terms.md).
  3. Система создаёт условия-черновик: проверка диапазона пунктов (INV-PT-1, иначе REWARDS_PLATFORM_TERMS_RETENTION_OUT_OF_RANGE); запись в Status = Draft (Version = 1) — в резолюции удержания черновик не участвует. Результат — { termsId, status = Draft }.
  4. Менеджер проверяет черновик и нажимает «Активировать»; фронт вызывает POST /api/v1/platform/commission-terms/{termsId}/activate { expectedVersion } (../api/platform/activate-platform-terms.md).
  5. Система активирует условия (INV-PT-2): guard Status = Draft; предыдущие Active-условия той же области (то же агентство либо глобальные) атомарно архивируются (ArchivedAt = now — конец их интервала действия); новые переходят Draft → Active (ActivatedAt = now — начало интервала). Единственность активных на область страхуют partial-unique индексы — гонка двух активаций даёт REWARDS_PLATFORM_TERMS_ACTIVE_EXISTS (retry). Результат — { termsId, status = Active, activatedAt, previousTermsId? }.

Фаза 2. Применение к начислению (автоматически, system)

  1. crm завершает сделку → consumer rewards (../consumers/crm-deal-completed.md) резолвит условия на момент asOf = CompletedAt сделки: область «агентство» (AgencyCompanyId сделки, интервал [ActivatedAt, ArchivedAt) покрывает asOf) → область «глобально» → fallback 0 п.п. (A2). Алгоритм — ../domain/platform-commission-terms.md.
  2. Guard конфигурации (INV-PT-4): если пункты удержания ≥ CommissionPercent применённого правила застройщика (пул агентства ≤ 0) — начисление не создаётся, событие уходит в DLQ (A5).
  3. В начисление снимается снапшот условий: PlatformRetentionPercentagePoints, PlatformFeeAmount = база × п.п. / 100 (banker's, 2 знака; база — по CalculationBase правила, INV-10) и AppliedPlatformTermsId — последующая смена условий существующие начисления не пересчитывает (UC-RWD-003).
  4. При подтверждении начисления застройщиком (UC-RWD-004) строится строка распределения Platform = PlatformFeeAmount — первая из трёх строк (INV-4/5/9); политика агентства делит только пул (CommissionAmount − PlatformFeeAmount). Те же цифры агент видит заранее в витрине — расчёт для catalog использует ту же резолюцию условий (../api/internal/calculate-property-object-commission.md, UC-RWD-002).

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

A1. Правка черновика

  1. PUT /api/v1/platform/commission-terms/{termsId} (agencyCompanyId?, retentionPercentagePoints, comment?, expectedVersion) — только в Draft (../api/platform/update-platform-terms.md). Возвращается к шагу 4.

A2. Условий нет — fallback 0 п.п.

  1. На момент CompletedAt нет ни агентских, ни глобальных действующих условий → начисление создаётся без удержания: PlatformRetentionPercentagePoints = 0, PlatformFeeAmount = 0, AppliedPlatformTermsId = null; строка Platform при подтверждении строится с нулевой суммой. Инкрементируется warn-метрика rewards_platform_terms_missing_total + алерт (платформа теряет доход, пока условия не заведены). Не ошибка. Возвращается к шагу 8.

A3. Изменение действующих условий

  1. Поля Active-условий иммутабельны (INV-PT-3, REWARDS_PLATFORM_TERMS_ACTIVE_IMMUTABLE) — воспроизводимость снапшотов начислений. Менеджер создаёт новый Draft с новыми пунктами (шаги 2–3) и активирует его (шаг 5) — прежние условия архивируются автоматически, их интервал [ActivatedAt, ArchivedAt) остаётся источником asOf-резолюции для исторических сделок.

A4. Архив условий без замены

  1. POST /api/v1/platform/commission-terms/{termsId}/archive { expectedVersion } (../api/platform/archive-platform-terms.md) — область остаётся без условий: новые начисления агентства пойдут по глобальному дефолту (если он активен) либо по fallback 0 п.п. (A2). Ссылки AppliedPlatformTermsId из существующих начислений живы (архив, не удаление). Завершается.

A5. Удержание съедает ставку правила (INV-PT-4)

  1. При создании начисления пункты удержания применимых условий ≥ CommissionPercent применённого правила застройщика (пул агентства ≤ 0) → ни одно начисление по сделке (включая реферальное) не создаётся; событие завершения сделки уходит в DLQ с кодом REWARDS_PLATFORM_RETENTION_EXCEEDS_RULE
    • алерт.
  2. Менеджер ПИН разбирает конфликт: правит условия платформы (новый Draft + Activate, A3) либо договаривается с застройщиком о правке правила; после исправления событие переигрывается из DLQ — начисления создаются по условиям, действующим на asOf = CompletedAt (ретроактивное исправление требует интервала действия, покрывающего прошлое). Завершается.

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

  1. Два менеджера параллельно активируют условия одной области → побеждает первая зафиксированная активация; проигравший получает REWARDS_PLATFORM_TERMS_ACTIVE_EXISTS (409), перечитывает реестр (его черновик остаётся Draft) и решает, актуальна ли его версия.

Постусловие

  • В области (агентство / глобально) не более одних Active-условий (INV-PT-2); история условий — цепочка архивных записей с непрерывными интервалами [ActivatedAt, ArchivedAt) для asOf-резолюции.
  • Новые начисления по сделкам с CompletedAt в интервале действия получают снапшот условий (PlatformRetentionPercentagePoints, PlatformFeeAmount, AppliedPlatformTermsId); уже созданные начисления не пересчитаны.
  • Строка Platform каждого подтверждаемого начисления равна PlatformFeeAmount снапшота; по гейту получения денег (UC-RWD-010) счёт PlatformRevenue кошелька кредитуется этой суммой (ADR-0065).

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

КодHTTPУсловиеПоведение клиента
403 FORBIDDEN403Нет rewards.platform-terms.manageСкрыть управление условиями
REWARDS_PLATFORM_TERMS_RETENTION_OUT_OF_RANGE400Пункты вне диапазона 0.25–1.50 (INV-PT-1)Подсветить поле пунктов
REWARDS_PLATFORM_TERMS_ACTIVE_EXISTS409Гонка активаций одной области (INV-PT-2, A6)Перечитать реестр, повторить при необходимости
REWARDS_PLATFORM_TERMS_ACTIVE_IMMUTABLE409Правка Active-условий (INV-PT-3, A3)Создать новый черновик
REWARDS_PLATFORM_TERMS_INVALID_STATUS_TRANSITION409Активация не из Draft / повторная активацияОбновить карточку условий
REWARDS_PLATFORM_TERMS_NOT_FOUND404Условия не существуютВернуться к реестру
REWARDS_PLATFORM_RETENTION_EXCEEDS_RULE— (DLQ)Удержание ≥ ставки применённого правила при начислении (INV-PT-4, A5)Разбор Менеджером ПИН + replay из DLQ
REWARDS_CONCURRENCY_CONFLICT409expectedVersion не совпал (параллельная мутация)Перечитать и повторить

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

СобытиеКогда поднимаетсяSubscribers
PlatformTermsCreatedEventУспешное создание черновика (шаг 3)in-proc аудит
PlatformTermsActivatedEventDraft → Active (шаг 5)in-proc аудит
PlatformTermsArchivedEvent* → Archived (шаг 5 автоархив, A4)in-proc аудит

Kafka-события условия не публикуют — применяются только внутри rewards при создании начисления (../domain/platform-commission-terms.md).

Acceptance Criteria

#КритерийПроверка
AC1Активация новых условий атомарно архивирует предыдущие активные той же области; активные всегда единственны (INV-PT-2)параллельные активации → одна Active; partial-unique index
AC2Агентские условия побеждают глобальные при резолюции; глобальные применяются к агентствам без персональных условийначисления двух агентств (с условиями / без) → разные AppliedPlatformTermsId
AC3Резолюция идёт на момент CompletedAt, а не обработки: сделка, завершённая до активации новых условий, получает старыесделка с CompletedAt в интервале архивных условий → снапшот старых пунктов
AC4Смена условий не пересчитывает существующие начисления (снапшот)активировать новые условия → перечитать начисление: PlatformFeeAmount прежний
AC5Без условий — fallback 0 п.п. (PlatformFeeAmount = 0, AppliedPlatformTermsId = null) + метрика rewards_platform_terms_missing_total (A2)тест без активных условий
AC6Удержание ≥ ставки правила → событие в DLQ, начисления (оба) не созданы (INV-PT-4, A5); после исправления replay создаёт начислениятест условий 1.50 п.п. при правиле 1%
AC7Пункты вне 0.25–1.50 отклоняются на создании/правке (INV-PT-1)границы 0.24 / 1.51 → 400
AC8Агентству условия не отдаются — виден только применённый снапшот в начислениизапрос реестра условий актором агентства → 403

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

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

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