GET /api/v1/agency/commission-accruals — Список начислений агентства
Постраничный список всех начислений комиссий tenant-агентства с полными строками распределения — экран «Комиссии агентства» ЛК руководителя (матрица «Агенство» 5.1, 5.3: «какую комиссию получим/получили — подтверждено / ожидает подтверждения»).
Назначение
Руководитель/администратор агентства видит начисления по завершённым сделкам своих агентов: суммы,
статусы подтверждения/выплаты и — в отличие от застройщика (list-developer-accruals.md) —
полное распределение комиссии на долю агента и удержание агентства (Distributions отдаются только
agency-area — Masking-правила README.md). Служит источником данных для формирования платёжных
поручений (create-payout-order.md) и сверки со сводкой
(get-agency-commission-summary.md). Feature group — agency-commission.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | rewards |
| API area | agency |
| Feature group | agency-commission |
| Статус | draft |
| Документ | docs/06-services/rewards/api/agency/list-agency-accruals.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (руководитель/администратор агентства) |
| Auth policy / Permissions | rewards.commission.view-all (op18 — реестр README.md) |
| Scope (RBAC) | организация: компания Type ∈ {Agency, Broker, Pin} |
| Record-level | не применяется — view-all даёт видимость всех начислений tenant-а (фильтр AgentUserId — не ограничение доступа, а сужение выборки) |
| Tenant isolation | TenantId = CompanyId агентства из claims; выборка обязательно ограничена tenant-ом агентства; не принимается из query |
| Audit | read-sensitive (суммы комиссий/распределения): в trace фиксируются actor + фильтры + TotalCount; суммы не логируются |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/v1/agency/commission-accruals |
| Success status | 200 |
| Idempotency header | не применяется (read) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
TenantId | identity claims (tenant_id) | Guid | Да | Required; не принимается из query | Tenant isolation |
Status | query | CommissionAccrualStatus? | Нет | enum-строка PendingConfirmation/Confirmed/Paid/Cancelled | — |
AgentUserId | query | Guid? | Нет | если задан — != Guid.Empty | сужение по агенту |
DeveloperCompanyId | query | Guid? | Нет | если задан — != Guid.Empty | фильтр по застройщику-плательщику |
DealCompletedFrom | query | DateTimeOffset? | Нет | нижняя граница DealCompletedAt (включительно) | — |
DealCompletedTo | query | DateTimeOffset? | Нет | верхняя граница DealCompletedAt (исключительно); > DealCompletedFrom, если обе заданы | — |
Page / PageSize | query | int | Нет | paging-конвенция — README.md (Page >= 1, PageSize 1..100, default 20) | — |
Модель ответа — постраничный список CommissionAccrualModel
Постраничный ответ: Items (список CommissionAccrualModel), Page, PageSize, TotalCount
(paging-конвенция — README.md). Дом проекции — CommissionAccrualModel
(README.md, полная проекция агрегата). Отдельная
list-item-модель не вводится: agency-area возвращает полный CommissionAccrualModel как есть (в отличие
от developer-area, где Distributions/AppliedPolicyId вырезаются). Дельта area:
| Аспект | Значение |
|---|---|
Distributions | Отдаются полностью (IReadOnlyList<CommissionDistributionModel>): доли Agent/Agency с суммами и PayoutOrderItemId. Пусто до статуса Confirmed (строки создаются при Confirm, INV-4). Правило area — README.md «Masking-правила» |
InvestorUserId | Маскируется — в CommissionAccrualModel не проецируется (трассировка инвестора только через DealId в crm) |
Суммы (*Amount) | Sensitive-класс: в теле ответа отдаются, в logs не пишутся |
Алгоритм
- Контекст и доступ. Операцию выполняет руководитель/администратор агентства (тип компании
Agency/Broker/Pin); scope — организация (TenantIdиз claims, не из query); требуется правоrewards.commission.view-all. Иначе —FORBIDDEN. - Проверка входа. Формат/диапазоны:
Status— допустимое значение enumCommissionAccrualStatus;AgentUserId/DeveloperCompanyIdнепусты, если заданы;DealCompletedTo > DealCompletedFrom, если обе заданы;Page/PageSizeв границах конвенции; иначе ошибка валидации.AgentUserId/DeveloperCompanyId— фильтры выборки, их существование не проверяется. - Что читаем. Страница начислений агентства-tenant-а по фильтрам: опц.
Status,AgentUserId,DeveloperCompanyId, границыDealCompletedAt; сортировка — поDealCompletedAtDESC (свежие сделки первыми); пагинация — по конвенции. Ведущий индекс —ix_commission_accruals_tenant_status; при заданномDeveloperCompanyIdбезStatusможет использоватьсяix_commission_accruals_developer. Вместе с начислениями загружаются их строки распределения (Distributions) — agency-area отдаёт распределение полностью. - Проекция. Каждое начисление разворачивается в
CommissionAccrualModelполе-в-поле (дом README.md):AgencyCompanyId ← TenantId,AppliedRule.RuleName ← AppliedRuleSnapshot.RuleName("manual"для ручных); суммы (CommissionAmount,BasePriceAmount,FinalPriceAmount) →MoneyModel;Distributions— долиAgent/Agencyцеликом (agency-area);InvestorUserIdне проецируется (masking). - Результат. Постраничный список (
Items,Page,PageSize,TotalCount) → HTTP200; суммы в логах маскируются, в теле доступны роли с permission.
Диаграмма
Ошибки и failure modes
Специфичных REWARDS_*-кодов у read-операции нет (без мутаций и concurrency). Применяются общие коды —
дом README.md «Общие ошибки»:
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
ValidationError | 400 | Status вне enum; AgentUserId/DeveloperCompanyId = Guid.Empty; DealCompletedTo <= DealCompletedFrom; PageSize вне 1..100 | «Проверьте параметры фильтра» | no retry |
UNAUTHORIZED | 401 | нет/просрочен JWT | «Требуется авторизация» | no retry |
FORBIDDEN | 403 | нет rewards.commission.view-all / тип компании вне {Agency, Broker, Pin} / чужой tenant | «Недостаточно прав» | no retry |
Кросс-сервисная свежесть
Начисления создаются асинхронно консюмером pin.crm.deal-completed.v1
(crm-deal-completed.md): между фактом завершения сделки в
crm и появлением начисления в этом списке есть окно обработки события (обычно секунды, при отставании
консюмера — дольше). Поэтому список отражает все уже созданные начисления агентства строго согласованно
(OLTP-чтение), но только что завершённая в CRM сделка может ещё не иметь начисления. Смена статуса начисления
(Confirm/Cancel/MarkPaid) видна в списке немедленно после соответствующего коммита — статусные переходы
происходят внутри rewards, не через реплики.
Acceptance criteria
- AC-1 (список начислений с распределением). Given у агентства есть начисления в разных статусах;
When руководитель запрашивает список без фильтра; Then
200, начисления поDealCompletedAtDESC, уConfirmed/Paid— полныеDistributions(долиAgent/Agency,PayoutOrderItemId), уPendingConfirmation—Distributionsпусты (строки создаются приConfirm, INV-4);InvestorUserIdне проецируется. - AC-2 (фильтры). Given те же данные; When заданы
Status,AgentUserId,DeveloperCompanyIdи/или период поDealCompletedAt; Then остаются только удовлетворяющие всем условиям записи; фильтры — сужение выборки, существованиеAgentUserId/DeveloperCompanyIdне проверяется (пустой результат — не ошибка). - AC-3 (источник для поручения). Given фильтр
Status = Confirmed; When запрос; Then выдача совпадает с множеством, из которого create-payout-order.md собирает свободныеAgent-доли (свободные — те, у чьих строкPayoutOrderItemId = null). - AC-4 (кросс-сервисное окно). Given сделка завершена в CRM только что; When список запрошен до
обработки события консюмером; Then начисления по ней ещё нет — оно появится после обработки
deal-completed(окно — секунды); это ожидаемое поведение, не потеря данных. - AC-5 (пусто / за пределами страницы). Given нет начислений под фильтр или
Pageза последней; Then200,Items = [], корректныйTotalCount(не404). - AC-6 (валидация). Given
Statusвне enum,AgentUserId/DeveloperCompanyId = Guid.Empty,DealCompletedTo <= DealCompletedFromилиPageSizeвне1..100; ThenValidationError(400). - AC-7 (доступ, изоляция, masking). Given актор без
rewards.commission.view-allили не роль агентства —FORBIDDEN(403); Given актор агентства A — только начисления A (TenantIdиз claims); суммы и распределение в теле доступны роли, но в логи не пишутся; developer-area той же сущности (list-developer-accruals.md) распределение не отдаёт.
Совместимость и наблюдаемость
- Модель — общий
CommissionAccrualModel: новые поля additive nullable; удаление/переименование/смена типа — breaking (migration + rollout). Unknown-значение enumStatusв query →ValidationError(unknown-enum-правило cross-cutting). - Logs:
TenantId, применённые фильтры (Status,AgentUserId,DeveloperCompanyId, границы дат),TotalCount(суммы и распределение не логируются); metrics:rewards_api_request_duration_seconds{route}; traces: OTel, correlation черезtraceparent; dashboardrewards-overview; runbook при деградации выборки —docs/06-services/rewards/runbooks/(по конвенции). - НФТ (ADR-0056): объём начислений = число завершённых сделок (десятки тысяч в год на платформу, на одно
агентство меньше); выборка страницами по
ix_commission_accruals_tenant_statusс догрузкойDistributionsпоix_commission_distributions_accrual; целевая латентность p95 < 300 мс на страницеPageSize <= 100.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (4): GET /api/v1/agency/commission-summary — Сводка комиссий агентства, GET /api/v1/my/commission-accruals — Мои начисления (агент), POST /api/v1/agency/commission-adjustment-requests/{requestId}/resolve — Решение по заявке агента на корректировку доли, Rewards API — реестр операций и общие контрактные модели
- Use Cases (1): US-реестр: Владелец агентства (US-OWN-AG)
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущность и инварианты распределения: domain/commission-accrual.md
(индекс
ix_commission_accruals_tenant_status;Distributionsсоздаются приConfirm— INV-4; maskingInvestorUserId/Distributions— раздел «Безопасность»). - Модели
CommissionAccrualModel/CommissionDistributionModel/MoneyModel, paging и Masking-правила — README.md. - Смежные операции: list-developer-accruals.md (та же сущность, developer-area
без распределения), get-agency-commission-summary.md (агрегаты по тем
же данным), create-payout-order.md (потребитель
Agent-строк начислений вMoneyReceived). - Permissions — каталог ../../identity/domain/permission.md; решение №2 (tenancy) — ../README.md; ADR-0052.