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

POST /api/v1/platform/commission-terms — Создание условий платформенной комиссии (черновик)

Создание записи условий удержания ПИН в статусе Draft: измерения специфичности — ось агентства (AgencyCompanyId?) и объектная ось-цепочка (DeveloperCompanyId? ⊃ ProjectId? ⊃ LayoutGroupId?), размер удержания в процентных пунктах ставки застройщика (0.25–1.50 п.п.) и период действия — domain/platform-commission-terms.md.

Назначение

Менеджер ПИН заводит условия монетизации платформы: сколько пунктов ставки застройщика ПИН удерживает в свою пользу — по сделкам конкретного агентства или всех агентств, по конкретному застройщику / проекту / группе планировок или всем объектам (глобальный дефолт). Период даёт выразить временные акции ПИН (понижение удержания на срок) обычной записью с ограниченным EffectiveTo. Условия создаются черновиком и не влияют на расчёт удержания до активации. Owning-контекст — rewards. Feature group — platform-terms.

Metadata

ПолеЗначение
Сервис/контекстrewards
API areaplatform
Feature groupplatform-terms
Статусdraft
Документdocs/06-services/rewards/api/platform/create-platform-terms.md

Актор и доступ

ПроверкаЗначение
Actor typeUser (Менеджер ПИН)
Auth policy / Permissionsrewards.platform-terms.manage
Scope (RBAC)платформа
Tenant isolationвне tenant-модели — платформенная сущность (ADR-0052); tenant-фильтрация не применяется
Auditcreate: CreatedByUserId = ActorUserId, Version = 1, доменное событие PlatformTermsCreatedEvent

HTTP-контракт

ПолеЗначение
MethodPOST
Route/api/v1/platform/commission-terms
Success status201 (+ Location: /api/v1/platform/commission-terms/{id})
Idempotency headerIdempotency-Key (Guid)
Correlationсквозной trace через заголовок traceparent

Входные данные / параметры запроса

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
ActorUserIdidentity claims (sub)GuidДаRequired
IdempotencyKeyheader Idempotency-KeyGuidДаRequired
AgencyCompanyIdbodyGuid?Нетnull — по всем агентствам; если задан — непустой Guid, логическая ссылка на компанию Type ∈ {Agency, Broker} в identity (синхронно не проверяется — значение выбирается из реестра организаций)
DeveloperCompanyIdbodyGuid?Нетnull — по всем застройщикам; если задан — непустой Guid, логическая ссылка на компанию Type = Developer в identity (синхронно не проверяется)
ProjectIdbodyGuid?Нетnull — по всем проектам; если задан — требует заданного DeveloperCompanyId (INV-PT-5), логическая ссылка на проект каталога (синхронно не проверяется)
LayoutGroupIdbodyGuid?Нетnull — по всем группам планировок; если задан — требует заданного ProjectId (INV-PT-5), логическая ссылка на группу планировок каталога (синхронно не проверяется)
RetentionPercentagePointsbodydecimalДа0.25 <= x <= 1.50, scale 2 (INV-PT-1)
EffectiveFrombodyDateTimeOffsetДаRequired
EffectiveTobodyDateTimeOffset?Нет> EffectiveFrom, если задан; null — бессрочно
Commentbodystring?Нет<= 2000

Специфичность условий (PlatformTermsScope: AgencyLayoutGroup / LayoutGroup / AgencyProject / Project / AgencyDeveloper / Developer / Agency / Global) не передаётся — выводится из заполненности измерений (domain/platform-commission-terms.md).

Модель ответа CreatePlatformTermsResult

ПолеТипОбязательностьИсточникMaskingОписание
IdGuidДаPlatformCommissionTermsEntity.IdId созданных условий.
Scopestring (enum PlatformTermsScope)Даcomputed из измеренийВыведенный уровень специфичности.
Statusstring (enum PlatformCommissionTermsStatus)ДаStatusВсегда Draft.
VersionlongДаVersion1 — для последующего ExpectedVersion.

Предусловия и постусловия

  • Предусловия. Актор — Менеджер ПИН с правом rewards.platform-terms.manage (платформенный scope).
  • Постусловия (успех). Появилась ровно одна новая запись условий в статусе Draft с Version = 1 и зафиксированным автором; в резолюции удержания (domain/platform-commission-terms.md, «Резолюция условий») черновик не участвует; действующие условия и созданные начисления не затронуты.
  • Постусловия (отказ). Ни одна запись не создана; повтор с тем же Idempotency-Key после успеха возвращает исходный результат и второй записи не создаёт.

Алгоритм

  1. Контекст и доступ. Операцию выполняет Менеджер ПИН в платформенном scope; требуется право rewards.platform-terms.manage. Повторный вызов с тем же IdempotencyKey возвращает ранее сохранённый результат.
  2. Проверка входа. RetentionPercentagePoints в диапазоне 0.25–1.50 (INV-PT-1) — иначе REWARDS_PLATFORM_TERMS_RETENTION_OUT_OF_RANGE; ось объекта заполнена сверху вниз (ProjectId требует DeveloperCompanyId, LayoutGroupId требует ProjectId, INV-PT-5) — иначе REWARDS_PLATFORM_TERMS_SCOPE_INVALID; EffectiveTo > EffectiveFrom, если задан — иначе REWARDS_PLATFORM_TERMS_PERIOD_INVALID; длина Comment — иначе ValidationError. Существование и тип компаний / проекта / группы синхронно не проверяются (ответственность актора). Непересечение периодов одной специфичности (INV-PT-2) на черновике не проверяется — черновики-дубли и пересечения допустимы, коллизия всплывёт при активации (REWARDS_PLATFORM_TERMS_OVERLAP), поэтому будущие акции готовятся заранее.
  3. Создание черновика. Формируется запись в статусе Draft: измерения (AgencyCompanyId, DeveloperCompanyId, ProjectId, LayoutGroupId), RetentionPercentagePoints, EffectiveFrom/EffectiveTo, Comment — из тела; фиксируются актор, CreatedAt, Version = 1. Запись и запись идемпотентности сохраняются в одном согласованном изменении.
  4. Результат. Id, выведенный Scope, Status = Draft, Version = 1201 + Location. Интеграционные события не публикуются (условия применяются только внутри rewards).

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
REWARDS_PLATFORM_TERMS_RETENTION_OUT_OF_RANGE400RetentionPercentagePoints вне [0.25; 1.50] (INV-PT-1)«Удержание вне допустимого диапазона 0.25–1.50 п.п.»no retry
REWARDS_PLATFORM_TERMS_SCOPE_INVALID400Ось объекта заполнена не сверху вниз (INV-PT-5)«Проект требует застройщика, группа планировок — проекта»no retry
REWARDS_PLATFORM_TERMS_PERIOD_INVALID400EffectiveTo <= EffectiveFrom«Некорректный период действия»no retry

Общие коды (ValidationError, FORBIDDEN, UNAUTHORIZED) — README.md.

Acceptance-критерии

  • Happy path (условия по проекту для агентства). Дано: Менеджер ПИН с rewards.platform-terms.manage; AgencyCompanyId, DeveloperCompanyId, ProjectId заданы, LayoutGroupId = null, RetentionPercentagePoints = 1.00, корректный период. Когда: POST /api/v1/platform/commission-terms. Тогда: 201 + Location, Scope = AgencyProject, Status = Draft, Version = 1; запись видна в list-platform-terms.md; резолюция удержания черновик не применяет.
  • Happy path (глобальный дефолт, бессрочно). Дано: все измерения null, RetentionPercentagePoints = 0.50, EffectiveTo = null. Когда: POST. Тогда: 201, Scope = Global — бессрочный страховочный уровень.
  • Удержание вне диапазона. Дано: RetentionPercentagePoints = 0.20 (или 1.60). Когда: POST. Тогда: 400 REWARDS_PLATFORM_TERMS_RETENTION_OUT_OF_RANGE, запись не создана.
  • Ось объекта не сверху вниз. Дано: ProjectId задан, DeveloperCompanyId = null (или LayoutGroupId без ProjectId). Когда: POST. Тогда: 400 REWARDS_PLATFORM_TERMS_SCOPE_INVALID.
  • Некорректный период. Дано: EffectiveTo <= EffectiveFrom. Когда: POST. Тогда: 400 REWARDS_PLATFORM_TERMS_PERIOD_INVALID.
  • Пересечение на черновике допустимо. Дано: активные условия той же комбинации измерений с пересекающимся периодом. Когда: POST. Тогда: 201 — непересечение (INV-PT-2) проверяется только при активации.
  • Идемпотентный повтор. Дано: условия уже созданы с ключом K. Когда: повторный POST с тем же Idempotency-Key = K. Тогда: тот же Id/Scope/Status/Version, второй записи нет.
  • Нет права. Дано: актор без rewards.platform-terms.manage. Когда: POST. Тогда: 403 FORBIDDEN.

Совместимость и наблюдаемость

  • Новые уровни специфичности — additive-расширение enum PlatformTermsScope (клиент игнорирует unknown при чтении); измерения расширяются только nullable-полями.
  • Logs: ActorUserId, TermsId, Scope, AgencyCompanyId, DeveloperCompanyId, ProjectId, LayoutGroupId; metrics: rewards_platform_terms_create_total{result}; traces: OTel; dashboard rewards-overview.

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

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

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