POST /api/v1/platform/commission-accruals/{accrualId}/adjust — Корректировка суммы начисления (gross)
First-class корректировка суммы CommissionAmount комиссионного начисления ПИН-админом
(domain/commission-accrual.md, INV-11,
ADR-0067 п.4): новая сумма +
обязательная причина. Исходная автосчитанная сумма фиксируется в OriginalCommissionAmount; каждая
корректировка оставляет append-only аудит-строку CommissionAccrualAdjustmentEntity
(Kind = GrossAmount). В Confirmed распределение пересобирается: строка Platform неизменна, пул
агентства пересчитывается и делится по применённой политике.
Назначение
ПИН-админ выравнивает сумму начисления с реальностью (договорённость с застройщиком, расхождение с
платёжкой при сверке акта, ошибка данных сделки) до получения денег — вместо отмены начисления целиком.
После MoneyReceived gross неизменяем: ошибки лечатся только компенсирующим начислением. Feature group —
accrual-adjustments.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | rewards |
| API area | platform |
| Feature group | accrual-adjustments |
| Статус | draft |
| Документ | docs/06-services/rewards/api/platform/adjust-commission-accrual.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (ПИН-админ) |
| Auth policy / Permissions | rewards.accruals.adjust (каталог — permission.md) |
| Scope (RBAC) | платформа |
| Tenant isolation | cross-tenant: начисление принадлежит tenant-у агентства; платформенный scope читает и корректирует вне tenant-изоляции по permission (отдельный путь доступа, domain/commission-accrual.md «Безопасность») |
| Audit | append-only строка CommissionAccrualAdjustmentEntity (Kind = GrossAmount, OldAmount/NewAmount, обязательная Reason, ActorUserId); UpdatedByUserId, Version + 1 |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | POST |
| Route | /api/v1/platform/commission-accruals/{accrualId}/adjust |
| Success status | 200 |
| Idempotency header | не применяется (повтор с тем же ExpectedVersion → REWARDS_CONCURRENCY_CONFLICT; каждая применённая корректировка — отдельная аудит-строка) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
ActorUserId | identity claims (sub) | Guid | Да | Required | actor-stamping (ActorUserId аудит-строки) |
AccrualId | route {accrualId} | Guid | Да | Required, непустой | — |
NewAmount | body | MoneyModel | Да | Value > 0, scale 2; CurrencyId = валюте начисления (INV-6) | Sensitive |
Reason | body | string | Да | trim, 1..1000; пустой/whitespace → REWARDS_ACCRUAL_ADJUSTMENT_REASON_REQUIRED (INV-11) | не содержит PII (договорённость UI) |
ExpectedVersion | body | long | Да | >= 1; сверка с Version начисления (конкуренты — Confirm/Cancel/включение в акт или поручение) | optimistic concurrency |
Модель ответа CommissionAccrualModel
Полная проекция начисления — дом README.md («CommissionAccrualModel»). Дельта операции:
CommissionAmount = NewAmount; OriginalCommissionAmount заполнено (исходная сумма — фиксируется первой
корректировкой и далее не меняется); в Confirmed — пересобранные Distributions (три строки); новая
Version.
Алгоритм
- Контекст и доступ. Операцию выполняет ПИН-админ в платформенном scope; требуется право
rewards.accruals.adjust(не совпадает с правом подтвержденияrewards.accruals.confirmзастройщика). - Проверка входа и предусловий.
- формат:
AccrualIdнепустой,NewAmount.Value > 0,Reasonнепустой после trim (иначеREWARDS_ACCRUAL_ADJUSTMENT_REASON_REQUIRED),ExpectedVersion >= 1; иначеValidationError; - существование: начисление с
AccrualIdсуществует; иначеREWARDS_ACCRUAL_NOT_FOUND; - валюта:
NewAmount.CurrencyId=CurrencyIdначисления (INV-6); иначеREWARDS_CURRENCY_MISMATCH; - конкуренция: версия совпадает с
ExpectedVersion; иначеREWARDS_CONCURRENCY_CONFLICT. Начисление читается под блокировкой вместе со строками распределения.
- формат:
- Guard-ы корректировки (INV-11). Все нарушения →
REWARDS_ACCRUAL_ADJUSTMENT_FORBIDDEN:- статус —
PendingConfirmationилиConfirmed; послеMoneyReceivedgross неизменяем (только компенсирующее начисление),Paid/Cancelled— терминальны; - ни одна строка распределения не включена в не-отменённое платёжное поручение
(
PayoutOrderItemId is nullу всех строк); - начисление не входит в не-отменённый акт поступления (
Draft/Registered, domain/platform-receipt.md) — сначала исключить позицию изDraft-акта, затем корректировать; NewAmount.Value > PlatformFeeAmount.Value— пул агентства строго положителен (PlatformFeeAmountсчитается от базы цены и при корректировке gross не меняется, INV-10).
- статус —
- Применение корректировки. В одном согласованном изменении:
- при первой корректировке —
OriginalCommissionAmount := CommissionAmount(фиксируется один раз); CommissionAmount := NewAmount;- в
Confirmed— пересборка распределения (INV-4/5/9/10): строкаPlatform=PlatformFeeAmount(неизменна), пул =NewAmount − PlatformFeeAmountделится междуAgency/Agentпо применённой политике (AppliedPolicyId); Σ трёх строк =NewAmount(INV-5); вPendingConfirmationстрок ещё нет — пересборка не требуется, распределение построится при подтверждении от актуальной суммы; - append аудит-строки
CommissionAccrualAdjustmentEntity(Kind = GrossAmount,OldAmount= сумма до корректировки,NewAmount,Reason,ActorUserId); Version + 1. Интеграционные события не публикуются: статус начисления не меняется; скорректированная сумма видна в проекцияхrewardsсразу (строгая согласованность чтения). Если начисление уже включено в чей-то расчёт внеrewards— расхождение поймает перепроверка при регистрации акта (REWARDS_RECEIPT_ITEM_STALE, register-platform-receipt.md).
- при первой корректировке —
- Результат. Проекция начисления (
CommissionAmount = NewAmount,OriginalCommissionAmount, пересобранныеDistributionsвConfirmed, новая версия) → HTTP200.
Диаграмма
Ошибки и failure modes
Общие коды (ValidationError, FORBIDDEN, UNAUTHORIZED, REWARDS_CONCURRENCY_CONFLICT) — дом
README.md «Общие ошибки». Специфичные для операции:
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
REWARDS_ACCRUAL_NOT_FOUND | 404 | Начисление не существует | «Начисление не найдено» | no retry |
REWARDS_ACCRUAL_ADJUSTMENT_REASON_REQUIRED | 400 | Reason пуст/whitespace (INV-11) | «Укажите причину корректировки» | no retry |
REWARDS_ACCRUAL_ADJUSTMENT_FORBIDDEN | 409 | Статус вне PendingConfirmation/Confirmed; строка в неотменённом поручении; начисление в не-отменённом акте; NewAmount <= PlatformFeeAmount (пул ≤ 0) — INV-11 | «Корректировка недоступна для этого начисления» | no retry |
REWARDS_CURRENCY_MISMATCH | 400 | NewAmount.CurrencyId ≠ валюте начисления (INV-6) | «Валюта корректировки не совпадает с валютой начисления» | no retry |
Acceptance criteria
- AC-1 (корректировка в Confirmed с пересборкой). Given начисление
Confirmed(CommissionAmount = 100 000,PlatformFeeAmount = 10 000, строки свободны, не в акте,Version = N); When ПИН-админ корректирует доNewAmount = 90 000с причиной; Then200:OriginalCommissionAmount = 100 000,CommissionAmount = 90 000; строкаPlatform= 10 000 (неизменна), пул 80 000 переделён междуAgency/Agentпо применённой политике, Σ трёх строк = 90 000 (INV-5); создана аудит-строка (GrossAmount, 100 000 → 90 000,Reason, актор);Version = N+1. - AC-2 (корректировка в PendingConfirmation). Given начисление
PendingConfirmation(строк нет); When корректировка; Then200— сумма изменена, аудит-строка создана; распределение построится при подтверждении застройщиком уже от новой суммы. - AC-3 (повторная корректировка). Given начисление уже корректировалось
(
OriginalCommissionAmountзаполнен); When вторая корректировка; Then200—OriginalCommissionAmountне перезаписывается, добавлена вторая аудит-строка (OldAmount= сумма после первой корректировки). - AC-4 (после получения денег). Given начисление
MoneyReceived(илиPaid/Cancelled); When корректировка; ThenREWARDS_ACCRUAL_ADJUSTMENT_FORBIDDEN(409) — после гейта gross неизменяем, исправление — компенсирующим начислением (ADR-0067). - AC-5 (строка в поручении / начисление в акте). Given
Agent-строка включена в неотменённое поручение или начисление входит вDraft/Registered-акт; When корректировка; ThenREWARDS_ACCRUAL_ADJUSTMENT_FORBIDDEN(409) — сначала освободить строку (Reject/Cancelпоручения) или исключить позицию изDraft-акта. - AC-6 (пул ≤ 0). Given
PlatformFeeAmount = 10 000; WhenNewAmount = 10 000(или меньше); ThenREWARDS_ACCRUAL_ADJUSTMENT_FORBIDDEN(409) — удержание ПИН не пересчитывается от gross, пул обязан остаться положительным (INV-10/11). - AC-7 (пустая причина). Given
Reasonиз пробелов; When корректировка; ThenREWARDS_ACCRUAL_ADJUSTMENT_REASON_REQUIRED(400) — без причины финансовая корректировка запрещена. - AC-8 (гонка с включением в акт). Given параллельные
adjustи включение начисления вDraft-акт; When обе операции; Then обе мутируют один агрегат — блокировка и сверка версии пропускают одну; если корректировка успела раньше включения, расхождение суммы позиции поймает регистрация акта (REWARDS_RECEIPT_ITEM_STALE). - AC-9 (доступ). Given актор без
rewards.accruals.adjust(в т.ч. менеджер застройщика, руководитель агентства); When корректировка; ThenFORBIDDEN(403) — застройщик корректирует только в составе подтверждения (../admin/confirm-commission-accrual.md), агентство правит только доли пула.
Совместимость и наблюдаемость
- Контракт additive-only. Аудит-строки append-only — история корректировок не редактируется и не
удаляется; отчётность по корректировкам строится по
CommissionAccrualAdjustmentEntity(ix_commission_accrual_adjustments_created_at). - Logs:
AccrualId,DealId,ActorUserId, факт корректировки без сумм (суммы — Sensitive); metrics:rewards_accrual_adjust_total{result}; traces: OTel, correlation черезtraceparent; dashboardrewards-overview. - НФТ (ADR-0056): мутация одного агрегата (пересборка ≤ 3 строк + аудит-строка); целевая латентность p95 < 300 мс; частота — единичные корректировки в неделю (ручная операция ПИН-админа).
Обратные ссылки
Автоссылки: где используется этот документ.
- API (4): POST /api/v1/platform/receipts — Создание акта поступления средств (черновик), POST /api/v1/platform/receipts/{receiptId}/register — Регистрация акта поступления средств, POST /api/v1/platform/referral-accruals/{accrualId}/adjust — Корректировка суммы реферального начисления, Rewards API — реестр операций и общие контрактные модели
- Use Cases (2): UC-RWD-011. Корректировки начисления комиссии, US-реестр: Финансовый блок ПИН (US-FIN)
- registries (1): Реестр API-методов PIN
- index.md (1): Rewards Service
Связанные документы
- Сущность, INV-10/11,
CommissionAccrualAdjustmentEntityи enumCommissionAdjustmentKind: domain/commission-accrual.md. - Удержание ПИН (строка
Platform,PlatformFeeAmount): domain/platform-commission-terms.md; деление пула: domain/agency-commission-policy.md. - Корректировка застройщиком при подтверждении:
../admin/confirm-commission-accrual.md; влияние на акт:
register-platform-receipt.md (
REWARDS_RECEIPT_ITEM_STALE). - Модель ответа: README.md («
CommissionAccrualModel»,MoneyModel). - ADR-0067 п.4 (корректировки), ADR-0066 (три строки распределения), ADR-0052 (cross-tenant доступ платформы).