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

POST /api/v1/agency/commission-policies/{policyId}/archive — Архивирование политики

«Удаление» комиссионной политики в терминах API: перевод * → Archived + soft-delete (DeletedAt). Физическое удаление запрещено — на политику ссылаются начисления (AppliedPolicyId, restrict) — domain/agency-commission-policy.md, INV-5 ArchiveConsistency.

Назначение

Руководитель/администратор агентства навсегда выводит политику из работы. Архивирование активной политики оставляет агентство без действующей политики: последующие подтверждения начислений (Confirm) считают распределение по fallback INV-6 (удержание агентства = 0, вся комиссия агенту, AppliedPolicyId = null). Уже применённые начисления не пересчитываются — их ссылки AppliedPolicyId остаются живыми (soft-delete консервирует запись). Owning-контекст — rewards, feature group — agency-policy, операция op17 реестра README.md.

Metadata

ПолеЗначение
Сервис/контекстrewards
API areaagency
Feature groupagency-policy
Статусdraft
Документdocs/06-services/rewards/api/agency/archive-agency-policy.md

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

ПроверкаЗначение
Actor typeUser (руководитель/администратор агентства)
Auth policy / Permissionsrewards.commission-policy.manage
Scope (RBAC)организация: компания Type ∈ {Agency, Broker, Pin}
Record-levelне применяется (tenant-интерсептор ограничивает выборку своим агентством)
Tenant isolationTenantId = CompanyId агентства из claims (tenant-интерсептор); чужая политика → 404 (INV-7 TenantOwnership)
Auditdelete (soft): DeletedAt, DeletedByUserId = ActorUserId, UpdatedByUserId, Version + 1; доменное событие AgencyPolicyArchivedEvent

HTTP-контракт

ПолеЗначение
MethodPOST
Route/api/v1/agency/commission-policies/{policyId}/archive
Success status200
Idempotency headerне применяется (повтор из ArchivedREWARDS_POLICY_INVALID_ARCHIVE_STATE)
Correlationсквозной trace через заголовок traceparent

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
TenantIdidentity claims (tenant_id)GuidДаRequiredTenant isolation
ActorUserIdidentity claims (sub)GuidДаRequired
PolicyIdroute {policyId}GuidДаRequired, != Guid.Empty
ExpectedVersionbodylongДа>= 1; сверка с Version агрегатаoptimistic concurrency

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

Полная проекция AgencyCommissionPolicyEntityдом формы: create-agency-policy.md (AgencyCommissionPolicyModel + вложенная AgencyDistributionRulesModel); дом полей сущности — domain/agency-commission-policy.md. Здесь — только дельта результата архивирования.

ПолеТипОбязательностьИсточникMaskingОписание
Statusstring (enum AgencyCommissionPolicyStatus)ДаStatusВсегда Archived.
VersionlongДаVersionНовая версия (ExpectedVersion + 1).

Остальные поля (Id, Name, Description, EffectiveFrom, Rules, CreatedAt, UpdatedAt) — по дому, значения сохраняются: архив консервирует последнюю версию правил (Rules иммутабельны после Active, INV-4). Ставки удержания внутри Rules — sensitive (Masking-правила README.md).

Алгоритм

  1. Контекст и доступ. Операцию выполняет руководитель/администратор агентства (тип компании Agency/Broker/Pin); scope — организация (TenantId из claims); требуется право rewards.commission-policy.manage. Иначе — FORBIDDEN (INV-7 TenantOwnership).
  2. Проверка входа и предусловий.
    • формат: PolicyId непустой, ExpectedVersion >= 1; иначе ошибка валидации;
    • существование: политика с PolicyId в tenant-е агентства существует; если нет (или чужой tenant) → REWARDS_POLICY_NOT_FOUND;
    • допустимость состояния: статус — Draft или Active; повтор из Archived нарушил бы INV-5 → REWARDS_POLICY_INVALID_ARCHIVE_STATE;
    • конкуренция: версия совпадает с ExpectedVersion; иначе REWARDS_CONCURRENCY_CONFLICT;
    • связи: начисления, ссылающиеся на политику (AppliedPolicyId), архив не блокируют — ограничение restrict распространяется только на физическое удаление, которого здесь нет; ссылки остаются валидными.
  3. Переход и soft-delete. Политика переходит * → Archived: одновременно Status = Archived и DeletedAt = now (INV-5 ArchiveConsistency: Status = Archived ⇔ DeletedAt != null), проставляется DeletedByUserId = актор, растёт версия. Физического удаления нет — запись консервируется, ссылки AppliedPolicyId начислений остаются живыми. Если политика была Active, агентство остаётся без активной политики: последующие подтверждения начислений (Confirm) применяют fallback INV-6 (удержание агентства = 0, вся комиссия агенту, AppliedPolicyId = nulldomain/commission-accrual.md).
  4. Фиксация факта. Архивирование сопровождается доменным событием AgencyPolicyArchivedEvent внутри rewards. Интеграционных Kafka-событий нет — политика применяется исключительно внутри rewards, внешним потребителям её архивирование не нужно (domain/agency-commission-policy.md, «Доменные и интеграционные события»).
  5. Результат. Проекция обновлённой политики (Status = Archived, Version = ExpectedVersion + 1) → HTTP 200.

Диаграмма

Побочные эффекты

ТипДетали
Integration eventsnone — AgencyPolicyArchivedEvent остаётся внутри сервиса; Kafka не публикуется
External callsnone
Cache / projections / searchnone; косвенно — при архиве активной политики агентство остаётся без политики → fallback INV-6 при следующих Confirm начислений
Notificationsnone

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
REWARDS_POLICY_NOT_FOUND404Политика не существует / чужой tenant«Политика не найдена»no retry
REWARDS_POLICY_INVALID_ARCHIVE_STATE409Status = Archived (повтор архивации, нарушение INV-5 ArchiveConsistency)«Политика уже в архиве»no retry
REWARDS_CONCURRENCY_CONFLICT409конфликт версии (ExpectedVersion)«Политика изменена другим пользователем»retry после refresh

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

Acceptance criteria

  • AC-1 (архив черновика/активной). Given политика в статусе Draft или Active (Version = N); When руководитель архивирует её с ExpectedVersion = N; Then политика переходит * → Archived, одновременно DeletedAt = now (INV-5 ArchiveConsistency), DeletedByUserId = актор, Version = N+1; физического удаления нет — запись консервируется; ответ 200. Постусловие: ссылки AppliedPolicyId начислений остаются валидными (уже применённые начисления не пересчитываются).
  • AC-2 (архив активной → fallback). Given архивируется единственная Active-политика; When архивирование; Then агентство остаётся без активной политики. Постусловие: следующий Confirm начисления применяет fallback INV-6 (удержание агентства = 0, вся комиссия агенту, AppliedPolicyId = null) — задокументированное поведение, не ошибка.
  • AC-3 (повторный архив). Given политика уже Archived; When архивирование; Then REWARDS_POLICY_INVALID_ARCHIVE_STATE (409, нарушение INV-5) — «Политика уже в архиве».
  • AC-4 (конкуренция). Given ExpectedVersion не совпал с текущей версией (политику параллельно изменили/активировали); When архивирование; Then REWARDS_CONCURRENCY_CONFLICT (409, retry после refresh).
  • AC-5 (чужой tenant / нет). Given политика другого агентства или несуществующая; When архивирование; Then REWARDS_POLICY_NOT_FOUND (404, INV-7 TenantOwnership, без раскрытия).

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

  • Архив терминален — восстановление не проектируется (при необходимости создаётся новая политика через create-agency-policy.md + activate-agency-policy.md). Архивирование активной политики намеренно оставляет агентство без удержания (fallback INV-6) до активации новой — задокументированное поведение, не ошибка.
  • Kafka-контракт не затрагивается (событий нет). Если появится потребитель изменений политики — добавить pin.rewards.agency-policy-changed.v1 через additive-процедуру событийного реестра (дом domain-дока).
  • Logs: TenantId, PolicyId, исходный статус (ставки удержания не логируются — Masking-правила README); metrics: rewards_policy_archive_total{result}; traces: OTel, correlation через traceparent; dashboard rewards-overview; runbook — не применяется.
  • НФТ (ADR-0056): soft-delete одного агрегата (без физического удаления, ссылки AppliedPolicyId консервируются); целевая латентность p95 < 300 мс; частота — редкая (вывод политики из работы, back-office).

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

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

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