POST /api/v1/agency/commission-adjustment-requests/{requestId}/resolve — Решение по заявке агента на корректировку доли
Руководитель агентства разрешает заявку агента (CommissionAdjustmentRequestEntity, Pending) на
корректировку его доли: одобряет — и тогда предложение исполняется как Distribution-корректировка
(перераспределение пула Agency ↔ Agent той же логикой, что прямая правка
adjust-accrual-distribution.md), либо отклоняет — распределение не
меняется. Строка Platform неприкосновенна, сумма и валюта начисления не меняются (INV-11/INV-12
domain/commission-accrual.md). Завершающее звено схемы «агент
запрашивает → руководитель одобряет».
Назначение
Дать руководителю/администратору агентства решение по инициативе агента: одобрить пересмотр доли (исполнив
корректировку с обоснованием из заявки) или отклонить с комментарием. Одобрение — единственный путь, которым
предложение агента превращается в фактическое изменение строк распределения: право распоряжаться пулом
остаётся у руководителя. Feature group — agency-commission.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | rewards |
| API area | agency |
| Feature group | agency-commission |
| Статус | draft |
| Документ | docs/06-services/rewards/api/agency/approve-distribution-adjustment.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (руководитель/администратор агентства) |
| Auth policy / Permissions | rewards.commission.adjust-approve (одобрение заявок и распоряжение пулом агентства) |
| Scope (RBAC) | организация: компания Type ∈ {Agency, Broker, Pin} |
| Record-level | не применяется (доступ на уровне tenant: заявка своего агентства) |
| Tenant isolation | In-tenant: TenantId из claims; чужая заявка невидима → 404 |
| Audit | update заявки: Status = Approved/Rejected, ResolvedByUserId = ActorUserId, ResolvedAt, ResolutionComment. При Approved — исполнение Distribution-корректировки: append-only строка CommissionAccrualAdjustmentEntity (Kind = Distribution, Reason из заявки, ActorUserId = ActorUserId руководителя), Version начисления + 1, AppliedAdjustmentId заявки ← id строки |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | POST |
| Route | /api/v1/agency/commission-adjustment-requests/{requestId}/resolve |
| Success status | 200 |
| Idempotency header | не применяется; повтор по уже разрешённой заявке — REWARDS_ADJUSTMENT_REQUEST_INVALID_STATUS (заявка не в Pending); защита от гонок — ExpectedVersion начисления при одобрении |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
TenantId | identity claims (tenant_id) | Guid | Да | Required | Tenant isolation |
ActorUserId | identity claims (sub) | Guid | Да | Required | actor-stamping (резолвер) |
RequestId | route {requestId} | Guid | Да | Required, непустой | — |
Decision | body | AdjustmentRequestDecision | Да | enum-строка Approved/Rejected | — |
ExpectedVersion | body | long | При Decision = Approved | >= 1; сверка с Version начисления (optimistic concurrency) | — |
ResolutionComment | body | string? | При Decision = Rejected — обязателен; при Approved — опционален | 1..1000, не пусто после trim (для Rejected) — обоснование отказа | — |
Модель ответа CommissionAdjustmentRequestModel
Проекция разрешённой заявки — дом модели: README.md. Возвращается заявка с новым Status
(Approved/Rejected), ResolvedByUserId, ResolvedAt, ResolutionComment; при Approved —
AppliedAdjustmentId (id созданной аудит-строки Distribution). Обновлённое распределение начисления
(при одобрении) читается через adjust-accrual-distribution.md-модель
CommissionAccrualModel / list-agency-accruals.md.
Предусловия и постусловия
- Предусловия. Заявка существует в tenant-е агентства и находится в статусе
Pending. Актор — руководитель/администратор сrewards.commission.adjust-approve. ДляApproved: связанное начисление ∈ {Confirmed,MoneyReceived},Agent-строка свободна (PayoutOrderItemId is null),ProposedAgentAmountзаявки<=актуальному пулу (пул мог измениться gross-корректировкой после подачи),ExpectedVersion=Versionначисления. - Постусловия (одобрение). Строки
Agency/Agentначисления заменены:Agent = ProposedAgentAmount,Agency = пул − ProposedAgentAmount; строкаPlatform,CommissionAmount,PlatformFeeAmountи статус не изменились; Σ трёх строк =CommissionAmount(INV-5). Создана аудит-строкаKind = Distribution(OldAmount= прежняя доля агента,NewAmount = ProposedAgentAmount,Reasonиз заявки);Version + 1; заявка →Approved,AppliedAdjustmentIdзаполнен. ПриMoneyReceived— публикуется компенсирующееpin.rewards.commission-distribution-adjusted.v1(перенос дельты между счетамиwallet, см. «Побочные эффекты»). - Постусловия (отклонение). Заявка →
RejectedсResolutionComment; распределение,Versionи суммы начисления не изменены. - Постусловия (отказ). Заявка и начисление не изменены.
Алгоритм
- Контекст и доступ. Операцию выполняет руководитель/администратор агентства; scope — организация
(
TenantIdиз claims); требуется правоrewards.commission.adjust-approve. - Проверка входа.
RequestIdнепустой;Decision∈ {Approved,Rejected}. ДляRejected—ResolutionCommentнепустой после trim,<= 1000(иначеValidationError). Заявка существует в tenant-е агентства — иначеREWARDS_ADJUSTMENT_REQUEST_NOT_FOUND. Заявка в статусеPending— иначеREWARDS_ADJUSTMENT_REQUEST_INVALID_STATUS(уже разрешена/повторное решение). - Ветка
Rejected. Заявка переводится вRejected(ResolvedByUserId,ResolvedAt,ResolutionComment). Распределение начисления не трогается; событие статуса начисления не публикуется. Публикуетсяcommission-adjustment-request-status-changed(Rejected) — уведомление агенту. - Ветка
Approved— исполнение корректировки. Связанное начисление читается под блокировкой на запись. Guard INV-11/INV-12: статус ∈ {Confirmed,MoneyReceived} иAgent-строка свободна (PayoutOrderItemId is null) — иначеREWARDS_ACCRUAL_ADJUSTMENT_FORBIDDEN;ExpectedVersion=Version— иначеREWARDS_CONCURRENCY_CONFLICT;ProposedAgentAmountзаявки<=актуальному пулу (CommissionAmount − PlatformFeeAmount, INV-10) — иначеREWARDS_ACCRUAL_DISTRIBUTION_SUM_INVALID(пул изменился с момента подачи; заявку следует переподать). В одном согласованном изменении: строкаAgent←ProposedAgentAmount, строкаAgency←пул − ProposedAgentAmount(строкаPlatformне меняется); создаётся аудит-строкаCommissionAccrualAdjustmentEntity(Kind = Distribution,OldAmount= прежняя доля агента,NewAmount = ProposedAgentAmount,Reasonиз заявки,ActorUserId= руководитель);Version + 1; заявка →Approved,AppliedAdjustmentId← id строки. Статус начисления не меняется. - Компенсация кошелька. Если начисление в
MoneyReceived(счетаwalletкредитованы по прежнему делению), в том же изменении публикуетсяpin.rewards.commission-distribution-adjusted.v1с дельтой долейAgency/Agent—walletавто-проводкойAdjustmentпереносит разницу между счетами получателей (идентично adjust-accrual-distribution.md). ВConfirmedкошелёк ещё не кредитован — событие не публикуется. Публикуетсяcommission-adjustment-request-status-changed(Approved) — уведомление агенту. - Результат. Проекция разрешённой заявки (
CommissionAdjustmentRequestModel) →200.
Диаграмма
Побочные эффекты
- Отклонение. Никаких изменений распределения/балансов;
notificationsуведомляет агента об отказе (ResolutionComment). - Одобрение до получения денег (
Confirmed). Строки распределения переписаны, но интеграционных событий кошелька нет — балансыwalletначисление ещё не отражают. Рекомендованный порядок — разрешать заявки до регистрации акта. - Одобрение после получения денег (
MoneyReceived). Публикуетсяpin.rewards.commission-distribution-adjusted.v1(../../events/commission-distribution-adjusted.md, key =AccrualId);walletавто-проводкойAdjustmentпереносит дельту между счетамиAgentCommissionиAgencyCommissionбез ручного вмешательства (ADR-0065). СтрокаPlatformи Σ балансов получателей не меняются. Частота учитывается метрикойrewards_distribution_adjust_after_receipt_total.
Ошибки и failure modes
Общие коды (ValidationError, FORBIDDEN, UNAUTHORIZED) — дом README.md «Общие ошибки».
Дельта операции:
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
REWARDS_ADJUSTMENT_REQUEST_NOT_FOUND | 404 | Заявка не существует или принадлежит чужому tenant | «Заявка не найдена» | no retry |
REWARDS_ADJUSTMENT_REQUEST_INVALID_STATUS | 409 | Заявка не в статусе Pending (уже одобрена/отклонена) | «Заявка уже разрешена» | no retry |
REWARDS_ACCRUAL_ADJUSTMENT_FORBIDDEN | 409 | При одобрении: статус начисления вне {Confirmed, MoneyReceived} или Agent-строка в неотменённом поручении (INV-11) | «Корректировка недоступна в текущем состоянии начисления» | no retry |
REWARDS_ACCRUAL_DISTRIBUTION_SUM_INVALID | 409 | При одобрении: ProposedAgentAmount заявки превышает актуальный пул (пул изменился после подачи, INV-5/10) | «Предлагаемая доля превышает пул агентства» | no retry (переподать заявку) |
REWARDS_CONCURRENCY_CONFLICT | 409 | При одобрении: ExpectedVersion не совпал с Version начисления | «Начисление изменено — обновите данные» | retry после refresh |
Acceptance criteria
- AC-1 (одобрение исполняет корректировку). Given заявка
Pending(ProposedAgentAmount = 70 000) по начислениюConfirmedсPlatform = 10 000,Agency = 27 000,Agent = 63 000(пул 90 000); When руководительDecision = Approved,ExpectedVersionсовпадает; Then200:Agent = 70 000,Agency = 20 000,Platformне изменён, Σ =CommissionAmount(INV-5); аудит-строкаDistribution(OldAmount = 63 000,NewAmount = 70 000,Reasonиз заявки,ActorUserId= руководитель);Version + 1; заявкаApproved,AppliedAdjustmentIdзаполнен. - AC-2 (отклонение не меняет распределение). Given заявка
Pending; WhenDecision = Rejected,ResolutionComment; Then200: заявкаRejectedс комментарием; строки распределения,Versionи суммы начисления не изменены; уведомление агенту. - AC-3 (отказ без комментария). Given
Decision = Rejected, пустойResolutionComment; When решение; Then400ValidationError— комментарий обязателен при отклонении. - AC-4 (повторное решение). Given заявка уже
Approved/Rejected; When повторныйresolve; ThenREWARDS_ADJUSTMENT_REQUEST_INVALID_STATUS(409). - AC-5 (пул изменился после подачи). Given заявка
ProposedAgentAmount = 70 000, но пул уменьшился gross-корректировкой ниже 70 000; When одобрение; ThenREWARDS_ACCRUAL_DISTRIBUTION_SUM_INVALID(409) — заявка не исполняется, требуется переподача. - AC-6 (строка в поручении). Given
Agent-строка начисления включена в неотменённое поручение; When одобрение; ThenREWARDS_ACCRUAL_ADJUSTMENT_FORBIDDEN(409). - AC-7 (гонка одобрения со сборкой поручения). Given параллельно собирается поручение,
захватывающее
Agent-строку; When обе операции; Then обе мутируют начисление под блокировкой — проходит одна; проигравшее одобрение получаетREWARDS_CONCURRENCY_CONFLICTилиREWARDS_ACCRUAL_ADJUSTMENT_FORBIDDENи перечитывает состояние (заявка остаётсяPending). - AC-8 (одобрение после получения денег). Given заявка по начислению
MoneyReceived, строка свободна, счетаwalletкредитованы по прежнему делению; When одобрение; Then200, инкрементrewards_distribution_adjust_after_receipt_total, публикуетсяpin.rewards.commission-distribution-adjusted.v1;walletавто-проводкой переносит дельту (см. «Побочные эффекты»). - AC-9 (нет права). Given у актора нет
rewards.commission.adjust-approve; When решение; Then403FORBIDDEN.
Совместимость и наблюдаемость
CommissionAdjustmentRequestModelиCommissionAccrualModel— дом README.md; контракт additive-only.- Logs:
RequestId,AccrualId,TenantId,Decision,Status,result(суммы не логируются — Sensitive); metrics:rewards_adjustment_request_resolved_total{decision,result},rewards_distribution_adjust_after_receipt_total; traces: OTel, correlation черезtraceparent; dashboardrewards-overview. - НФТ (ADR-0056): мутация одного агрегата; целевая латентность p95 < 300 мс; частота — единичные решения руководителя.
Обратные ссылки
Автоссылки: где используется этот документ.
- Домен (CQRS) (1): Entity / Aggregate: CommissionAccrualEntity
- API (2): POST /api/v1/my/commission-accruals/{accrualId}/adjustment-requests — Заявка агента на корректировку своей доли, Rewards API — реестр операций и общие контрактные модели
- Use Cases (2): UC-RWD-013. Агент предлагает корректировку своей доли, руководитель одобряет, US-реестр: Владелец агентства (US-OWN-AG)
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущность и guard-ы: domain/commission-accrual.md
(
CommissionAdjustmentRequestEntity, INV-11/INV-12,CommissionAccrualAdjustmentEntityKind = Distribution). - Заявка агента (вход схемы): ../agent/request-distribution-adjustment.md.
- Логика исполнения корректировки и компенсация кошелька: adjust-accrual-distribution.md, ../../events/commission-distribution-adjusted.md, ../../../wallet/consumers/rewards-commission-distribution-adjusted.md.
- Общие модели/ошибки: README.md; ADR-0066 (три строки распределения, пул), ADR-0067
(корректировки, гейт денег), ADR-0069 (co-broking — мультистрочный
Agentв v-next), ADR-0065 (кошелёк), ADR-0052 (tenancy).