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

GET /api/v1/agency/commission-policies — Список комиссионных политик агентства

Список политик распределения комиссии агентства (активная, черновики, архив-история) — экран «Комиссионная политика» ЛК руководителя агентства (матрица «Агенство» 5.2).

Назначение

Руководитель агентства видит действующую политику и историю версий (история = цепочка архивированных записей, дом domain/agency-commission-policy.md «Audit»). Feature group — agency-policy.

Metadata

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

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

ПроверкаЗначение
Actor typeUser (руководитель/администратор агентства)
Auth policy / Permissionsrewards.commission-policy.view
Scope (RBAC)организация: компания Type ∈ {Agency, Broker, Pin}
Record-levelне применяется
Tenant isolationTenantId = CompanyId агентства из claims; tenant-интерсептор
Auditnone (read; сами ставки удержания — Internal-класс внутри агентства)

HTTP-контракт

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

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
TenantIdidentity claims (tenant_id)GuidДаRequiredTenant isolation
StatusqueryAgencyCommissionPolicyStatus?Нетenum Draft/Active/Archived
Page / PageSizequeryintНетpaging-конвенция — README.md

Модель ответа — постраничный список AgencyPolicyModel

Постраничный ответ (Items, Page, PageSize, TotalCount — paging-конвенция README.md). Полный документ правил (DefaultRule + AgentOverrides со ставками удержания) отдаётся только ролям с rewards.commission-policy.view/rewards.commission-policy.manage (руководитель/администратор агентства); агент такой роли не имеет и на этот эндпоинт не попадает — свою персональную ставку он видит лишь в проекции «моя комиссия» (get-my-commission-summary.md, Masking-правила README.md).

AgencyPolicyModel (проекция AgencyCommissionPolicyEntity)

ПолеТипОбязательностьИсточникMaskingОписание
IdGuidДаIdId политики.
NamestringДаNameНазвание («Стандарт 30/70»).
Descriptionstring?НетDescriptionКомментарий.
Statusstring (enum AgencyCommissionPolicyStatus)ДаStatusDraft/Active/Archived.
EffectiveFromDateTimeOffset?НетEffectiveFromМомент активации.
DefaultRuleAgencyRetentionRuleModelДаRules.DefaultRuleполный документ — только роли с rewards.commission-policy.viewПравило по умолчанию.
AgentOverridesIReadOnlyList<AgentRetentionOverrideModel>Да (может быть пустым)Rules.AgentOverridesПерсональные оверрайды.
CreatedAt / UpdatedAtDateTimeOffsetДаaudit-поляАудит.
VersionlongДаVersionДля ExpectedVersion мутаций.

AgencyRetentionRuleModel (проекция AgencyRetentionRule из jsonb-дома)

ПолеТипОбязательностьОграничения
Kindstring (enum AgencyRetentionKind)ДаPercent/FixedAmount (удержание агентства) / AgentPercentOfPool/AgentPercentOfPrice (прямая доля агента)
ValuedecimalДапроцентные Kind (Percent/AgentPercentOfPool/AgentPercentOfPrice): 0..100; FixedAmount: 0..100000000, scale 2
CurrencyIdshort?Да при FixedAmount, иначе nullссылка на валюту справочника (RUB-only — ADR-0048); display-код валюты клиент берёт из справочника (../../../handbook/api/public/lookup-references.md)

AgentRetentionOverrideModel

ПолеТипОбязательностьОграничения
AgentUserIdGuidДапользователь агентства
RuleAgencyRetentionRuleModelДа

Алгоритм

  1. Контекст и доступ. Операцию выполняет руководитель/администратор агентства; scope — организация (TenantId из claims); требуется право rewards.commission-policy.view.
  2. Проверка входа. Status — допустимое значение enum AgencyCommissionPolicyStatus, если задан; Page/PageSize — по конвенции; иначе ошибка валидации.
  3. Что читаем. Страница политик агентства-tenant-а: опц. фильтр по Status; сортировка — детерминированная: Active-политика (не более одной, INV-1) всегда первой, затем остальные (Draft/Archived) по CreatedAt DESC — свежие черновики и последние архивы выше (архив = история версий, дом domain/agency-commission-policy.md «Audit»). Фильтр Status = Active вернёт 0 или 1 запись; выборка ведётся по ix_agency_policies_tenant_status. У агентства, ещё не заводившего политику, страница пуста (Items = [], TotalCount = 0) — это не ошибка (означает fallback INV-6 при подтверждении начислений).
  4. Проекция. Каждая политика разворачивается в AgencyPolicyModel; документ правил Rules разворачивается в DefaultRule + AgentOverrides (ContractVersion наружу не отдаётся).
  5. Результат. Постраничный список AgencyPolicyModel (Items, Page, PageSize, TotalCount) → HTTP 200. Данные строго согласованы внутри rewards: список отражает последний коммит create/update/activate/archive немедленно (агрегатное чтение OLTP, не проекция) — задержки eventual-consistency нет.

Диаграмма

Ошибки и failure modes

Специфичных REWARDS_*-кодов у read-операции нет (нет мутаций и concurrency). Применяются общие коды — дом README.md «Общие ошибки»:

КодHTTP statusУсловиеСообщениеRetry
ValidationError400Status вне enum AgencyCommissionPolicyStatus; PageSize вне 1..100«Проверьте параметры фильтра»no retry
UNAUTHORIZED401нет/просрочен JWT«Требуется авторизация»no retry
FORBIDDEN403нет rewards.commission-policy.view / тип компании вне {Agency, Broker, Pin} / чужой tenant«Недостаточно прав»no retry

Acceptance criteria

  • AC-1 (список политик). Given у агентства есть активная политика и несколько черновиков/архивных; When руководитель запрашивает список без фильтра; Then 200, Active первой, далее Draft/Archived по CreatedAt DESC; для каждой политики отданы DefaultRule и AgentOverrides; ContractVersion в проекции отсутствует.
  • AC-2 (фильтр по статусу). Given те же данные; When запрос с Status = Active; Then возвращается ровно одна запись (или ноль, если политики нет) — INV-1; Status = Archived возвращает историю версий по CreatedAt DESC.
  • AC-3 (нет политики → пусто). Given агентство ещё не заводило политику; When запрос списка; Then 200, Items = [], TotalCount = 0 (не 404); UI трактует это как «нет удержания» (fallback INV-6 при подтверждении начислений).
  • AC-4 (пагинация за пределами). Given TotalCount = 3; When запрошена Page за последней; Then 200 с пустым Items и корректным TotalCount (без ошибки).
  • AC-5 (недостаточно прав). Given актор без rewards.commission-policy.view или не роль агентства; When запрос; Then FORBIDDEN (403), список не раскрывается.
  • AC-6 (изоляция tenant). Given актор агентства A; When запрос; Then в выдаче только политики A — TenantId берётся из claims, не из query; политики агентства B недоступны.
  • AC-7 (свежесть). Given только что выполнена активация новой политики; When сразу запрошен список; Then новая Active уже первой, предыдущая — Archived (строгая согласованность внутри rewards, без окна eventual-consistency).

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

  • Новые поля правил (например, будущие условия по типу сделки) — additive nullable; удаление/переименование/ смена типа — breaking. Новые значения enum AgencyCommissionPolicyStatus в query — по unknown-enum-правилу cross-cutting (ValidationError).
  • Logs: TenantId, применённый Status, TotalCount (ставки удержания не логируются — Internal-класс); metrics: rewards_api_request_duration_seconds{route}; traces: OTel, correlation через traceparent; dashboard rewards-overview.
  • НФТ (ADR-0056): единицы политик на агентство, десятки строк оверрайдов — выборка тривиальна; целевая латентность p95 < 200 мс; операция back-office (руководители агентств — низкая частота, подмножество ≤20k менеджеров/агентов платформы).

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

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

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