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

POST /api/v1/my/commission-accruals/{accrualId}/adjustment-requests — Заявка агента на корректировку своей доли

Агент предлагает изменить размер собственной доли (Agent-строки) в пуле сделки, не правя распределение напрямую: подаёт заявку CommissionAdjustmentRequestEntity в статусе Pending с предлагаемой суммой ProposedAgentAmount и обязательным обоснованием. Само перераспределение пула (AgencyAgent) выполнит только руководитель агентства при одобрении заявки (../agency/approve-distribution-adjustment.md, INV-12 domain/commission-accrual.md). Схема «агент запрашивает → руководитель одобряет»: право распоряжаться пулом остаётся у руководителя, агент получает голос без прямого доступа к строкам распределения.

Назначение

Дать агенту-исполнителю сделки инструмент инициировать пересмотр своей доли (индивидуальная договорённость, вклад в сложную сделку, замеченная ошибка политики) через формальную заявку с обоснованием — вместо внесистемных переговоров. Заявка не меняет деньги: она лишь фиксирует предложение, которое руководитель одобряет или отклоняет. Feature group — my-commission.

Metadata

ПолеЗначение
Сервис/контекстrewards
API areaagent
Feature groupmy-commission
Статусdraft
Документdocs/06-services/rewards/api/agent/request-distribution-adjustment.md

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

ПроверкаЗначение
Actor typeUser (агент — исполнитель сделки)
Auth policy / Permissionsrewards.commission.adjust-request (право предложить корректировку своей доли — только заявка, не прямая правка)
Scope (RBAC)организация: собственное агентство агента (Type ∈ {Agency, Broker, Pin})
Record-levelRequestedByAgentUserId = ActorUserId из токена (sub); заявка допустима только по начислению, где AgentUserId = ActorUserId (агент правит только свою долю)
Tenant isolationIn-tenant: TenantId из claims; чужое начисление невидимо → 404
Auditinsert: заявка CommissionAdjustmentRequestEntity (Pending, заявитель, ProposedAgentAmount, Reason); Version начисления не инкрементируется (распределение не меняется)

HTTP-контракт

ПолеЗначение
MethodPOST
Route/api/v1/my/commission-accruals/{accrualId}/adjustment-requests
Success status201
Idempotency headerне применяется; защита от повторной подачи — partial unique «одна Pending-заявка на начисление» (INV-12)
Correlationсквозной trace через заголовок traceparent

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
TenantIdidentity claims (tenant_id)GuidДаRequired; не принимается из bodyTenant isolation
ActorUserIdidentity claims (sub)GuidДаRequired; не принимается из bodyrecord-level RequestedByAgentUserId
AccrualIdroute {accrualId}GuidДаRequired, непустой
ProposedAgentAmountbodyMoneyModelДаValue >= 0, scale 2; CurrencyId = валюте начисления (INV-6); Value <= пул агентства (CommissionAmount − PlatformFeeAmount)Sensitive — не логируется
ReasonbodystringДа1..1000, не пусто после trim — обязательное обоснование (INV-11/INV-12)

Модель ответа CommissionAdjustmentRequestModel

Проекция созданной заявки — дом модели: README.md («Общие контрактные модели»). Возвращается созданная заявка со Status = Pending, ProposedAgentAmount, Reason, RequestedByAgentUserId, AccrualId, CreatedAt; поля разрешения (ResolvedByUserId/ResolvedAt/ResolutionComment/ AppliedAdjustmentId) — null.

Предусловия и постусловия

  • Предусловия. Начисление существует в tenant-е агентства, принадлежит агенту (AgentUserId = ActorUserId) и находится в статусе Confirmed или MoneyReceived (распределение построено — три строки INV-4/9); собственная Agent-строка не включена в неотменённое платёжное поручение (PayoutOrderItemId is null); по начислению нет другой заявки в статусе Pending. Актор имеет rewards.commission.adjust-request.
  • Постусловия (успех). Создана заявка CommissionAdjustmentRequestEntity (Pending); распределение, суммы и Version начисления не изменены. Опубликовано commission-adjustment-request-status-changed (Pending) — руководителю уходит уведомление о новой заявке.
  • Постусловия (отказ). Заявка не создана; состояние начисления не изменено.

Алгоритм

  1. Контекст и доступ. Операцию выполняет агент; scope — организация (TenantId из claims); требуется право rewards.commission.adjust-request. Начисление ищется в tenant-е агентства.
  2. Проверка входа. AccrualId непустой; ProposedAgentAmount.Value >= 0, scale 2; Reason непустой после trim, <= 1000 — иначе REWARDS_ACCRUAL_ADJUSTMENT_REASON_REQUIRED. Начисление существует и принадлежит агенту (AgentUserId = ActorUserId) — иначе REWARDS_ACCRUAL_NOT_FOUND (чужое/несуществующее не различаются, fail-closed). CurrencyId совпадает с валютой начисления (INV-6) — иначе REWARDS_CURRENCY_MISMATCH.
  3. Guard заявки (INV-12). Статус начисления ∈ {Confirmed, MoneyReceived} и Agent-строка свободна (PayoutOrderItemId is null) — иначе REWARDS_ADJUSTMENT_REQUEST_FORBIDDEN. По начислению нет Pending-заявки — иначе REWARDS_ADJUSTMENT_REQUEST_ALREADY_PENDING (partial unique ux_commission_adjustment_requests_one_pending). ProposedAgentAmount.Value <= пул агентства (CommissionAmount − PlatformFeeAmount, INV-10) — иначе REWARDS_ADJUSTMENT_REQUEST_AMOUNT_INVALID (строка Platform неприкосновенна: агент не может предложить долю больше пула).
  4. Создание. Заявка CommissionAdjustmentRequestEntity сохраняется в статусе Pending (RequestedByAgentUserId = ActorUserId, ProposedAgentAmount, Reason). В том же изменении публикуется commission-adjustment-request-status-changed (Pending) для уведомления руководителя.
  5. Результат. Проекция созданной заявки (CommissionAdjustmentRequestModel, Status = Pending) → 201.

Диаграмма

Побочные эффекты

  • Событие статуса начисления не публикуется (распределение и Version не меняются). Деньги и балансы wallet не затрагиваются — заявка лишь предложение.
  • commission-adjustment-request-status-changed (Pending) уходит в notifications — руководитель агентства получает уведомление о заявке, требующей решения.

Ошибки и failure modes

Общие коды (ValidationError, FORBIDDEN, UNAUTHORIZED) — дом README.md «Общие ошибки». Дельта операции:

КодHTTP statusУсловиеСообщениеRetry
REWARDS_ACCRUAL_NOT_FOUND404Начисление не существует, принадлежит чужому tenant или чужому агенту«Начисление не найдено»no retry
REWARDS_ACCRUAL_ADJUSTMENT_REASON_REQUIRED400Reason пустой/отсутствует (INV-12)«Укажите обоснование заявки»no retry
REWARDS_ADJUSTMENT_REQUEST_FORBIDDEN409Статус вне {Confirmed, MoneyReceived} или Agent-строка уже в неотменённом поручении (INV-12)«Заявка на корректировку недоступна в текущем состоянии начисления»no retry
REWARDS_ADJUSTMENT_REQUEST_ALREADY_PENDING409По начислению уже есть заявка в статусе Pending«По начислению уже есть открытая заявка»no retry (дождаться решения по текущей)
REWARDS_ADJUSTMENT_REQUEST_AMOUNT_INVALID409ProposedAgentAmount отрицателен или превышает пул агентства (INV-10)«Предлагаемая доля превышает пул агентства»no retry (пересчитать по актуальному пулу)
REWARDS_CURRENCY_MISMATCH400CurrencyId суммы не совпадает с валютой начисления (INV-6)«Валюта доли не совпадает с валютой начисления»no retry

Acceptance criteria

  • AC-1 (подача заявки). Given начисление Confirmed, принадлежащее агенту, распределение Platform = 10 000, Agency = 27 000, Agent = 63 000 (пул 90 000), Agent-строка свободна; When агент подаёт ProposedAgentAmount = 70 000, Reason; Then 201: создана заявка Pending (ProposedAgentAmount = 70 000, RequestedByAgentUserId = агент); распределение и Version начисления не изменены; опубликовано commission-adjustment-request-status-changed (Pending).
  • AC-2 (только своя доля). Given начисление, где AgentUserId ≠ ActorUserId; When подача; Then REWARDS_ACCRUAL_NOT_FOUND (404) — агент не видит и не может править чужую долю.
  • AC-3 (одна открытая заявка). Given по начислению уже есть Pending-заявка; When агент подаёт вторую; Then REWARDS_ADJUSTMENT_REQUEST_ALREADY_PENDING (409).
  • AC-4 (предложение больше пула). Given пул 90 000; When ProposedAgentAmount = 95 000; Then REWARDS_ADJUSTMENT_REQUEST_AMOUNT_INVALID (409) — строка Platform и пул неприкосновенны.
  • AC-5 (граничная доля). Given ProposedAgentAmount = 0 (или = пулу); When подача; Then 201 — нулевая/полная доля пула допустима как предложение.
  • AC-6 (без обоснования). Given пустой Reason; When подача; Then REWARDS_ACCRUAL_ADJUSTMENT_REASON_REQUIRED (400), заявка не создана.
  • AC-7 (недопустимый статус). Given начисление в PendingConfirmation (строк ещё нет) / Paid / Cancelled, либо Agent-строка уже в неотменённом поручении; When подача; Then REWARDS_ADJUSTMENT_REQUEST_FORBIDDEN (409).
  • AC-8 (нет права). Given у агента нет rewards.commission.adjust-request; When подача; Then 403 FORBIDDEN.

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

  • CommissionAdjustmentRequestModel — дом README.md; контракт additive-only.
  • Logs: AccrualId, TenantId, RequestId, result (суммы не логируются — Sensitive); metrics: rewards_adjustment_request_created_total{result}; traces: OTel, correlation через traceparent; dashboard rewards-overview.
  • НФТ (ADR-0056): вставка одной дочерней записи; целевая латентность p95 < 300 мс; частота — единичные заявки агентов.

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

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

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