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

PUT /api/v1/platform/commission-terms/{termsId} — Изменение условий платформенной комиссии

Полная правка условий удержания ПИН только в статусе Draft; для условий в статусе Active допускается изменение только EffectiveTo — досрочное завершение действия сокращением периода (INV-PT-3). Смена измерений и RetentionPercentagePoints действующих условий — через новый черновик + активацию (сохраняет воспроизводимость снапшотов в начислениях) — domain/platform-commission-terms.md.

Назначение

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

Metadata

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

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

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

HTTP-контракт

ПолеЗначение
MethodPUT
Route/api/v1/platform/commission-terms/{termsId}
Success status200
Idempotency headerне применяется (естественная идемпотентность PUT + ExpectedVersion)
Correlationсквозной trace через заголовок traceparent

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
ActorUserIdidentity claims (sub)GuidДаRequired
TermsIdroute {termsId}GuidДаRequired, непустой
ExpectedVersionbodylongДа>= 1; сверка с Versionoptimistic concurrency
AgencyCompanyIdbodyGuid?Нетnull — по всем агентствам; логическая ссылка на компанию Type ∈ {Agency, Broker} (синхронно не проверяется)
DeveloperCompanyIdbodyGuid?Нетnull — по всем застройщикам; логическая ссылка на компанию Type = Developer (синхронно не проверяется)
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, если задан; для Active — единственное изменяемое поле: только сокращение периода (новое значение >= UtcNow и раньше текущего EffectiveTo либо текущий EffectiveTo = null) — сокращение пересечений не создаёт (INV-PT-3)
Commentbodystring?Нет<= 2000

PUT — полная замена: клиент присылает все поля (форма заполнена текущими значениями из list-platform-terms.md).

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

ПолеТипОбязательностьИсточникMaskingОписание
TermsPlatformCommissionTermsModelДаобновлённый агрегатПолная проекция после правки (модель — list-platform-terms.md), в т.ч. новый Version и пересчитанный Scope.

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

  • Предусловия. Актор — Менеджер ПИН с rewards.platform-terms.manage; условия существуют в статусе Draft (полная правка) или Active (только досрочное завершение); клиент передаёт актуальный ExpectedVersion.
  • Постусловия (успех, Draft). Все поля черновика заменены значениями из тела, Scope пересчитан из измерений; статус остаётся Draft, Version увеличен, зафиксирован актор; резолюция удержания и созданные начисления не затронуты.
  • Постусловия (успех, Active). Изменён только EffectiveTo (период сокращён); Version увеличен; с нового EffectiveTo условия перестают применяться к новым начислениям — резолюция уходит на менее специфичный уровень / следующие по периоду условия / fallback 0 п.п. Уже созданные начисления не пересчитываются — в них зафиксирован снапшот (PlatformRetentionPercentagePoints, AppliedPlatformTermsIddomain/commission-accrual.md).
  • Постусловия (отказ). Условия без изменений, версия не меняется.

Алгоритм

  1. Контекст и доступ. Операцию выполняет Менеджер ПИН в платформенном scope; требуется право rewards.platform-terms.manage.
  2. Проверка входа и предусловий. RetentionPercentagePoints в диапазоне 0.25–1.50 (INV-PT-1) — иначе REWARDS_PLATFORM_TERMS_RETENTION_OUT_OF_RANGE; ось объекта заполнена сверху вниз (INV-PT-5) — иначе REWARDS_PLATFORM_TERMS_SCOPE_INVALID; согласованность периода — иначе REWARDS_PLATFORM_TERMS_PERIOD_INVALID; длина Comment — иначе ValidationError. Условия существуют — иначе REWARDS_PLATFORM_TERMS_NOT_FOUND. Статус ∈ {Draft, Active}; Archived не редактируется → REWARDS_PLATFORM_TERMS_ACTIVE_IMMUTABLE. Version совпадает с ExpectedVersion — иначе REWARDS_CONCURRENCY_CONFLICT (правка не должна затирать чужую).
  3. Правка и переход.
    • Черновик (Status = Draft) — заменяются все поля из тела (полная замена PUT); Scope пересчитывается из измерений; смена измерений (в т.ч. агентство ↔ по всем агентствам, углубление/ обобщение оси объекта) допустима; непересечение периодов (INV-PT-2) здесь не проверяется — коллизия всплывёт при активации (activate-platform-terms.md);
    • действующие условия (Status = Active) — разрешено изменить только EffectiveTo и только сокращением: новое значение >= UtcNow (нельзя завершить задним числом — резолюция уже созданных начислений воспроизводима) и раньше текущего конца периода (либо текущий бессрочный). Изменение измерений, RetentionPercentagePoints, EffectiveFrom или расширение периода → REWARDS_PLATFORM_TERMS_ACTIVE_IMMUTABLE (INV-PT-3); некорректное новое значение EffectiveToREWARDS_PLATFORM_TERMS_PERIOD_INVALID. Сокращение периода пересечений не создаёт — повторная проверка INV-PT-2 не нужна;
    • фиксируются актор и новая версия.
  4. Результат. Полная проекция после правки (PlatformCommissionTermsModel, включая новый Version) → 200. Интеграционные события не публикуются.

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
REWARDS_PLATFORM_TERMS_NOT_FOUND404Условия не существуют«Условия не найдены»no retry
REWARDS_PLATFORM_TERMS_ACTIVE_IMMUTABLE409Правка полей кроме EffectiveTo у Active; расширение периода Active; правка Archived (INV-PT-3)«Действующие условия можно только досрочно завершить — создайте новый черновик и активируйте его»no 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; для Active — новый EffectiveTo < UtcNow«Некорректный период действия»no retry
REWARDS_CONCURRENCY_CONFLICT409ExpectedVersion устарел«Условия изменены другим пользователем»retry после refresh

Общие коды — README.md.

Acceptance-критерии

  • Правка черновика. Дано: условия в Draft (Version = N). Когда: PUT с валидными полями и ExpectedVersion = N. Тогда: 200, все поля заменены, Scope пересчитан, Status = Draft, Version = N + 1, актор зафиксирован.
  • Углубление оси объекта в черновике. Дано: черновик уровня Developer (ProjectId = null). Когда: PUT с заданным ProjectId (при заданном DeveloperCompanyId). Тогда: 200, Scope = Project; коллизия периодов всплывёт только при активации.
  • Досрочное завершение действующих. Дано: условия Active с EffectiveTo = T (или бессрочные). Когда: PUT, где изменён только EffectiveTo на T' ∈ [UtcNow; T), остальные поля равны текущим. Тогда: 200, EffectiveTo = T', Version + 1; новые начисления после T' эти условия не применяют; созданные начисления не пересчитываются (снапшот).
  • Иммутабельность действующих. Дано: условия Active. Когда: PUT с изменённым RetentionPercentagePoints (или измерениями, или EffectiveFrom). Тогда: 409 REWARDS_PLATFORM_TERMS_ACTIVE_IMMUTABLE, условия не изменены.
  • Расширение периода действующих. Дано: условия Active с EffectiveTo = T. Когда: PUT с EffectiveTo > T. Тогда: 409 REWARDS_PLATFORM_TERMS_ACTIVE_IMMUTABLE (только сокращение).
  • Завершение задним числом. Дано: условия Active. Когда: PUT с EffectiveTo < UtcNow. Тогда: 400 REWARDS_PLATFORM_TERMS_PERIOD_INVALID.
  • Ось объекта не сверху вниз. Дано: черновик, PUT с ProjectId без DeveloperCompanyId. Когда: PUT. Тогда: 400 REWARDS_PLATFORM_TERMS_SCOPE_INVALID.
  • Удержание вне диапазона. Дано: RetentionPercentagePoints = 2.00. Когда: PUT. Тогда: 400 REWARDS_PLATFORM_TERMS_RETENTION_OUT_OF_RANGE.
  • Правка архива. Дано: условия в Archived. Когда: PUT. Тогда: 409 REWARDS_PLATFORM_TERMS_ACTIVE_IMMUTABLE.
  • Конкурентная версия. Дано: ExpectedVersion не совпадает с текущим Version. Когда: PUT. Тогда: 409 REWARDS_CONCURRENCY_CONFLICT; клиент перечитывает запись и повторяет.

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

  • Контракт PUT полный: добавление нового optional поля требует одновременного обновления формы (иначе PUT затрёт значение).
  • Logs: ActorUserId, TermsId, ветка (draft-edit/close-period); metrics: rewards_platform_terms_update_total{result,branch}; traces: OTel; dashboard rewards-overview.

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

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

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