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.
| Поле | Значение |
|---|
| Сервис/контекст | rewards |
| API area | agency |
| Feature group | agency-policy |
| Статус | draft |
| Документ | docs/06-services/rewards/api/agency/archive-agency-policy.md |
Актор и доступ
| Проверка | Значение |
|---|
| Actor type | User (руководитель/администратор агентства) |
| Auth policy / Permissions | rewards.commission-policy.manage |
| Scope (RBAC) | организация: компания Type ∈ {Agency, Broker, Pin} |
| Record-level | не применяется (tenant-интерсептор ограничивает выборку своим агентством) |
| Tenant isolation | TenantId = CompanyId агентства из claims (tenant-интерсептор); чужая политика → 404 (INV-7 TenantOwnership) |
| Audit | delete (soft): DeletedAt, DeletedByUserId = ActorUserId, UpdatedByUserId, Version + 1; доменное событие AgencyPolicyArchivedEvent |
HTTP-контракт
| Поле | Значение |
|---|
| Method | POST |
| Route | /api/v1/agency/commission-policies/{policyId}/archive |
| Success status | 200 |
| Idempotency header | не применяется (повтор из Archived → REWARDS_POLICY_INVALID_ARCHIVE_STATE) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|
TenantId | identity claims (tenant_id) | Guid | Да | Required | Tenant isolation |
ActorUserId | identity claims (sub) | Guid | Да | Required | — |
PolicyId | route {policyId} | Guid | Да | Required, != Guid.Empty | — |
ExpectedVersion | body | long | Да | >= 1; сверка с Version агрегата | optimistic concurrency |
Модель ответа AgencyCommissionPolicyModel
Полная проекция AgencyCommissionPolicyEntity — дом формы: create-agency-policy.md
(AgencyCommissionPolicyModel + вложенная AgencyDistributionRulesModel); дом полей сущности —
domain/agency-commission-policy.md. Здесь — только дельта
результата архивирования.
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|
Status | string (enum AgencyCommissionPolicyStatus) | Да | Status | — | Всегда Archived. |
Version | long | Да | Version | — | Новая версия (ExpectedVersion + 1). |
Остальные поля (Id, Name, Description, EffectiveFrom, Rules, CreatedAt, UpdatedAt) — по дому,
значения сохраняются: архив консервирует последнюю версию правил (Rules иммутабельны после Active, INV-4).
Ставки удержания внутри Rules — sensitive (Masking-правила README.md).
Алгоритм
- Контекст и доступ. Операцию выполняет руководитель/администратор агентства (тип компании
Agency/Broker/Pin); scope — организация (TenantId из claims); требуется право
rewards.commission-policy.manage. Иначе — FORBIDDEN (INV-7 TenantOwnership).
- Проверка входа и предусловий.
- формат:
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 распространяется только на физическое удаление, которого здесь нет; ссылки остаются валидными.
- Переход и soft-delete. Политика переходит
* → Archived: одновременно Status = Archived и
DeletedAt = now (INV-5 ArchiveConsistency: Status = Archived ⇔ DeletedAt != null), проставляется
DeletedByUserId = актор, растёт версия. Физического удаления нет — запись консервируется, ссылки
AppliedPolicyId начислений остаются живыми. Если политика была Active, агентство остаётся без
активной политики: последующие подтверждения начислений (Confirm) применяют fallback INV-6 (удержание
агентства = 0, вся комиссия агенту, AppliedPolicyId = null —
domain/commission-accrual.md).
- Фиксация факта. Архивирование сопровождается доменным событием
AgencyPolicyArchivedEvent внутри
rewards. Интеграционных Kafka-событий нет — политика применяется исключительно внутри rewards,
внешним потребителям её архивирование не нужно
(domain/agency-commission-policy.md, «Доменные и
интеграционные события»).
- Результат. Проекция обновлённой политики (
Status = Archived, Version = ExpectedVersion + 1) →
HTTP 200.
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|
| Integration events | none — AgencyPolicyArchivedEvent остаётся внутри сервиса; Kafka не публикуется |
| External calls | none |
| Cache / projections / search | none; косвенно — при архиве активной политики агентство остаётся без политики → fallback INV-6 при следующих Confirm начислений |
| Notifications | none |
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|
REWARDS_POLICY_NOT_FOUND | 404 | Политика не существует / чужой tenant | «Политика не найдена» | no retry |
REWARDS_POLICY_INVALID_ARCHIVE_STATE | 409 | Status = Archived (повтор архивации, нарушение INV-5 ArchiveConsistency) | «Политика уже в архиве» | no retry |
REWARDS_CONCURRENCY_CONFLICT | 409 | конфликт версии (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).
Обратные ссылки
Автоссылки: где используется этот документ.
Связанные документы