GET /api/v1/agency/commission-summary — Сводка комиссий агентства
Денежная сводка ЛК агентства «сколько комиссии получим/получили × подтверждено/ожидает» одним объектом: четыре статусные корзины (сумма+количество) плюс разбивка удержания агентства и доли агентов (матрица «Агенство» 5.3/5.6). Агрегация считается на лету по начислениям и строкам распределения агентства (ADR-0056, без ClickHouse): одна выборка по статус-корзинам, одна — по распределению.
Назначение
Руководитель/администратор агентства на дашборде ЛК видит финансовую картину по своим начислениям за
период: сколько ожидает подтверждения застройщиком, сколько подтверждено (но не выплачено), сколько уже
выплачено, сколько отменено; и в разрезе распределения — сколько удержано агентством и сколько причитается
агентам. Owning-сервис — rewards, area agency, feature group agency-commission. Точечная сводка по
одному агенту — get-agent-commission-summary.md; сводка агента о себе —
get-my-commission-summary.md.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | rewards |
| API area | agency |
| Feature group | agency-commission |
| Статус | draft |
| Документ | docs/06-services/rewards/api/agency/get-agency-commission-summary.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (руководитель/администратор агентства) |
| Auth policy / Permissions | rewards.commission.view-all (op19) |
| Scope (RBAC) | организация: компания Type ∈ {Agency, Broker, Pin} актора |
| Record-level | не применяется — сводка по всем начислениям агентства-tenant-а |
| Tenant isolation | TenantId = CompanyId агентства из identity claims; выборка начислений и распределения автоматически ограничена tenant-ом агентства (ADR-0052) |
| Audit | read-sensitive (суммы комиссий): в trace — actor + период/фильтры; суммы не логируются |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/v1/agency/commission-summary |
| Success status | 200 |
| Idempotency header | не применяется (read) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
TenantId | identity claims | Guid | Да | Required (tenant агентства); не из query | tenant isolation |
DealCompletedFrom | query | DateTimeOffset? | Нет | нижняя граница периода по DealCompletedAt (включительно) | — |
DealCompletedTo | query | DateTimeOffset? | Нет | > DealCompletedFrom, если обе заданы; верхняя граница (исключительно) | — |
DeveloperCompanyId | query | Guid? | Нет | опц. сужение до одного застройщика-плательщика | — |
Период необязателен: без границ считается вся история агентства. Границы применяются к
CommissionAccrualEntity.DealCompletedAt (индекс ix_commission_accruals_deal_completed_at).
Модель ответа AgencyCommissionSummaryModel
Расширяет CommissionSummaryModel (README.md) двумя дельта-полями распределения. Каждая
статусная метрика — пара сумма (MoneyModel) + количество; база суммирования агентства —
CommissionAccrualEntity.CommissionAmount (gross). Дельта-поля суммируют
CommissionDistributionEntity.Amount по RecipientType (строки существуют только у Confirmed/Paid
начислений — commission-accrual.md, INV-4).
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
PendingConfirmationTotal / PendingConfirmationCount | MoneyModel / int | Да | Σ/count CommissionAmount где Status = PendingConfirmation | «получим, ожидает подтверждения» |
ConfirmedTotal / ConfirmedCount | MoneyModel / int | Да | Status = Confirmed | «получим, подтверждено, не выплачено» |
PaidTotal / PaidCount | MoneyModel / int | Да | Status = Paid | «получили» |
CancelledTotal / CancelledCount | MoneyModel / int | Да | Status = Cancelled | справочно |
AgencyRetainedTotal | MoneyModel | Да | Σ Amount строк RecipientType = Agency в периоде | Δ: удержано агентством (доход агентства) |
AgentsShareTotal | MoneyModel | Да | Σ Amount строк RecipientType = Agent в периоде | Δ: причитается агентам |
Валюта всех MoneyModel — платформенная RUB (INV-6, мультивалютность не проектируется);
InvestorUserId в сводке отсутствует (Masking-правила README.md).
Алгоритм
- Контекст и доступ. Операцию выполняет руководитель/администратор агентства (тип компании
Agency/Broker/Pin); scope — организация (TenantIdиз claims, не из query); требуется правоrewards.commission.view-all. Иначе —FORBIDDEN. - Проверка входа. Если заданы обе границы периода —
DealCompletedTo > DealCompletedFrom; иначе ошибка валидации. Существование сущностей по id не проверяется (агрегатный read). - Что читаем. Две агрегации по данным агентства (обе ограничены tenant-ом):
- по начислениям: группировка по
Status→ сумма и количествоCommissionAmountв разрезе статуса; фильтр — период поDealCompletedAt(если задан) и опц.DeveloperCompanyId. Опорные индексыix_commission_accruals_tenant_status,ix_commission_accruals_deal_completed_at; - по строкам распределения начислений
Confirmed/Paid: группировка поRecipientType→ суммаAmountдляAgency(удержано) иAgent(причитается). Индексix_commission_distributions_recipient.
- по начислениям: группировка по
- Сборка модели. Развернуть агрегации в
AgencyCommissionSummaryModel: каждой статусной корзине — пара сумма/количество (отсутствующая корзина →0/0);AgencyRetainedTotal/AgentsShareTotal— из строкAgency/Agent. Все суммы —MoneyModelв валютеRUB(INV-6). Если в выборке встретились суммы в разных валютах (нарушение INV-6) →REWARDS_CURRENCY_MISMATCH. - Результат.
AgencyCommissionSummaryModel→ HTTP200. Пустой период → все суммы0 RUB, счётчики0(не404).
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|---|
| Integration events | none |
| External calls | none |
| Cache / projections / search | none (агрегация на лету по индексам PostgreSQL) |
| Notifications | none |
Ошибки и failure modes
Общие коды (ValidationError, FORBIDDEN, UNAUTHORIZED) — README.md.
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
ValidationError | 400 | DealCompletedTo <= DealCompletedFrom | «Проверьте границы периода» | no retry |
REWARDS_CURRENCY_MISMATCH | 409 | в выборке встретились суммы в разных валютах (нарушение INV-6; не ожидается на RUB-платформе) | «Несогласованная валюта в сводке» | no retry |
Кросс-сервисная свежесть и согласованность корзин
Сводка агрегируется на лету из OLTP rewards, поэтому статусные корзины строго согласованы между собой на
момент запроса: сумма по всем корзинам равна сумме CommissionAmount начислений за период, а
AgencyRetainedTotal + AgentsShareTotal по Confirmed+Paid равно сумме CommissionAmount этих же
начислений (INV-5 распределения). Новые начисления приходят асинхронно консюмером
pin.crm.deal-completed.v1 — только что завершённая в CRM сделка может ещё не попасть в
PendingConfirmationTotal (окно обработки события — секунды). Изменения статусов (Confirm/MarkPaid/
Cancel) отражаются в корзинах немедленно после коммита.
Acceptance criteria
- AC-1 (полная сводка). Given у агентства есть начисления во всех статусах; When руководитель
запрашивает сводку без периода; Then
200с четырьмя парами сумма/количество (PendingConfirmation,Confirmed,Paid,Cancelled) плюсAgencyRetainedTotalиAgentsShareTotal; все суммы —MoneyModelвRUB. - AC-2 (согласованность распределения). Given есть
Confirmed/Paidначисления; ThenAgencyRetainedTotal + AgentsShareTotal(по строкам этих начислений) равно сумме ихCommissionAmount(INV-5) — сводка внутренне непротиворечива. - AC-3 (фильтр по периоду и застройщику). Given заданы
DealCompletedFrom/DealCompletedToи/илиDeveloperCompanyId; When запрос; Then агрегируются только начисления сDealCompletedAtв[From, To)и указанного застройщика-плательщика;Toисключителен. - AC-4 (пустой период). Given за период нет начислений; When запрос; Then
200, все суммы0.00 RUB, все counts0(не404). - AC-5 (валидация периода). Given обе границы заданы и
DealCompletedTo <= DealCompletedFrom; When запрос; ThenValidationError(400). - AC-6 (доступ и изоляция). Given актор без
rewards.commission.view-allили не роль агентства —FORBIDDEN(403); Given актор агентства A — сводка считается только по начислениям A (TenantIdиз claims, не из query); суммы в теле есть, в логи не пишутся. - AC-7 (кросс-сервисное окно). Given сделка завершена в CRM только что; When сводка запрошена до
обработки события консюмером; Then её сумма ещё не в
PendingConfirmationTotal— появится после обработкиdeal-completed; это ожидаемое поведение.
Совместимость и наблюдаемость
- Модель расширяет общий
CommissionSummaryModel: новые поля — additive; удаление/переименование/смена типа — breaking (migration + rollout). - Logs:
TenantId, период/DeveloperCompanyId, итоговые counts (суммы не логируются — Sensitive); metrics:rewards_api_request_duration_seconds{route}; traces — OTel, correlation черезtraceparent; dashboardrewards-overview. - НФТ (ADR-0056): две индексные агрегации (
SUM/COUNT) по данным одного агентства — объём = число сделок агентства (сотни-тысячи), без ClickHouse; целевая латентность p95 < 300 мс; операция дашборда — вызывается при открытии ЛК (низкая частота, back-office).
Обратные ссылки
Автоссылки: где используется этот документ.
- API (4): GET /api/v1/agency/agents/{agentUserId}/commission-summary — Сводка по конкретному агенту, GET /api/v1/agency/commission-accruals — Список начислений агентства, GET /api/v1/my/commission-summary — Моя сводка комиссий (агент), Rewards API — реестр операций и общие контрактные модели
- Use Cases (1): US-реестр: Владелец агентства (US-OWN-AG)
- Отчёты (1): Отчет/Export: Сводка вознаграждений агентства за период (agency-rewards-period-summary)
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущности: domain/commission-accrual.md (
CommissionAmount, распределение, INV-4/INV-6), domain/agency-commission-policy.md (как формируются долиAgency/Agent). - Общие модели (
CommissionSummaryModel,MoneyModel, Masking) и реестр операций — README.md. - Смежные сводки: get-agent-commission-summary.md, get-my-commission-summary.md; список начислений — list-agency-accruals.md.
- Решения №2/№6 — ../README.md.