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

UC-RWD-016. Застройщик подтверждает начисление после смены исполнителя

Назначение

Описывает частный случай подтверждения начисления застройщиком, когда исполнитель сделки менялся до её завершения: ведение передавали другому агенту (передача ведения) либо прежний исполнитель был уволен с переназначением преемника. Получатель комиссии зафиксирован снимком на момент завершения сделки — CommissionRecipientUserId = ExecutorAgentUserId (Executor на момент Completed), — и именно этот агент, а не изначальный инициатор (Origin), становится получателем Agent-строки распределения при подтверждении. Сценарий подчёркивает разделение двух ролей начисления: получатель (AgentUserId = Executor на завершении) и атрибуция (OriginAgentUserId = агент-основатель сделки), а также то, что после завершения сделки сменить получателя переназначением исполнителя уже нельзя. Связан с контекстом rewards.

Metadata

ПолеЗначение
IDUC-RWD-016
Контекстrewards (см. docs/06-services/rewards/)
Типальтернативный (частный случай подтверждения — UC-RWD-004 при смене исполнителя)
Статусdraft

Актор

ПолеЗначение
РольДевелопер (менеджер застройщика)
Permissions / scoperewards.accruals.confirm в scope Organization(CompanyId застройщика); ручные корректировки суммы — rewards.accruals.manage/rewards.accruals.adjust
TenantАктор в tenant застройщика; начисление живёт в tenant агентства (TenantId = AgencyCompanyId). Доступ — cross-tenant по явному фильтру DeveloperCompanyId = CompanyId актора (ADR-0052)
Record-levelне применяется (фильтр — по DeveloperCompanyId)

Предусловие

  • Начисление существует и находится в Status = PendingConfirmation; создано consumer-ом pin.crm.deal-completed.v1 (UC-RWD-003) по завершённой сделке, у которой исполнитель менялся до завершения: AgentUserId ≠ OriginAgentUserId (получатель — Executor на момент Completed, атрибуция — агент-основатель). Оба поля сняты снимком из события и иммутабельны на начислении (commission-accrual.md).
  • Событие завершения несло CommissionRecipientUserId = Deal.ExecutorAgentUserId на момент Completed (deal-completed.md); дальнейшая передача ведения на завершённой сделке невозможна (reassign-executor.md, гейт CRM_DEAL_ALREADY_TERMINAL).
  • accrual.DeveloperCompanyId = CompanyId актора; снапшот условий платформы снят при создании (UC-RWD-007); активная политика агентства настроена (иначе fallback, как в UC-RWD-004 A2).

Триггер

ПолеЗначение
ИсточникUI button
Триггерящий элементЛК застройщика → «Вознаграждения» → «Начисления на подтверждение» → строка начисления → «Подтвердить»

Основной Поток

  1. Менеджер открывает очередь начислений (status = PendingConfirmation, permission rewards.accruals.confirm, выборка по DeveloperCompanyId = CompanyId актора; InvestorUserId в проекции замаскирован — отдаётся только DealId).
  2. В строке начисления менеджер сверяет сделку по DealId и snapshot правила (AppliedRuleSnapshot). Получатель начисления — текущий исполнитель на момент завершения (AgentUserId), который может отличаться от агента-основателя (OriginAgentUserId); застройщику раскладка получателей не отдаётся — он подтверждает обязательство и сумму, а не выбирает получателя.
  3. Менеджер нажимает «Подтвердить» (при расхождении суммы с договором — «Подтвердить с корректировкой», A4). Система подтверждает начисление:
    • Доступ и проверка. rewards.accruals.confirm; accrual.DeveloperCompanyId == actor.CompanyId (иначе 404, fail closed); guard состояния Status = PendingConfirmation (иначе REWARDS_ACCRUAL_INVALID_STATUS_TRANSITION); rowVersion актуален (иначе 409); строк распределения ещё нет (иначе REWARDS_ACCRUAL_DISTRIBUTION_EXISTS).
    • Переход и распределение (три строки, INV-4/5/9/10). Начисление переходит PendingConfirmation → Confirmed; строятся ровно три строки: Platform (= PlatformFeeAmount), Agency и Agent. Строка Agent ссылается на AgentUserId начисления — то есть на исполнителя на момент завершения сделки (получатель), а не на OriginAgentUserId (атрибуция). Пул агентства (CommissionAmount − PlatformFeeAmount) делится по активной политике агентства-владельца.
    • Иммутабельность получателя. AgentUserId и OriginAgentUserId при подтверждении не пересчитываются и не редактируются: смена получателя после завершения сделки невозможна — сделка терминальна, передача ведения на ней заблокирована. Ошибочный получатель лечится только компенсирующей корректировкой в rewards (A5), а не разворотом атрибуции в CRM.
    • Поднимается доменное событие CommissionConfirmedEvent; атомарно публикуется CommissionAccrualStatusChangedKafkaEvent (FromStatus = PendingConfirmation, ToStatus = Confirmed).
  4. Фронт убирает начисление из очереди; notifications уведомляет получателя (AgentUserId — исполнитель на завершении) и руководителя его агентства о подтверждении. Выплата станет доступна только после регистрации акта поступления средств (MoneyReceived, UC-RWD-010).

Альтернативные Потоки

A1. Исполнитель не менялся (получатель = основатель)

  1. AgentUserId == OriginAgentUserId — типовой случай (UC-RWD-004): исполнитель на завершении совпадает с агентом-основателем сделки. Подтверждение и распределение идентичны основному потоку; строка Agent ссылается на того же агента. Различие получатель/атрибуция вырождается. Завершается.

A2. Прежний исполнитель — участник состава на момент завершения

  1. При передаче ведения прежнего исполнителя могли оставить в составе ролью CoAgent (reassign-executor.md, DemotePriorToCoAgent). Это не влияет на начисление: получатель — единственный финансовый Executor на момент завершения (ADR-0068), распределение строго трёхстрочное с одной Agent-строкой (ADR-0069, co-broking — v-next). Прочие агентские участники нефинансовы. Далее — как в основном потоке.

A3. Исполнитель-получатель уволен между завершением и подтверждением

  1. Агент-получатель (AgentUserId) был уволен из агентства после завершения сделки, но до подтверждения (ADR-0072, UC-RWD-017). Начисление не пересчитывается: AgentUserId иммутабелен, право на заработанное сохраняется. Застройщик подтверждает начисление в обычном порядке; строка Agent ссылается на UserId уволенного агента; при последующем MoneyReceived кредитуется его user-centric счёт кошелька (переживает смену/утрату агентства, ADR-0061 п.5). Не ошибка. Завершается по основному потоку.

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

  1. Менеджер передаёт скорректированную сумму с обязательной причиной (INV-11, как UC-RWD-004 A8): CommissionAmount заменяется, исходная фиксируется в OriginalCommissionAmount, создаётся аудит-строка GrossAmount, пул пересчитывается, PlatformFeeAmount неизменен. Получатель Agent-строки не меняется — корректировка касается суммы, а не носителя роли Executor. Без причины — 400; сумма ≤ PlatformFeeAmount409. Далее — как в основном потоке.

A5. Обнаружен ошибочный получатель

  1. После завершения выясняется, что комиссию должен был получить другой агент (ошибка передачи ведения до завершения). Переназначить получателя нельзя: сделка терминальна, AgentUserId иммутабелен. Исправление — только средствами rewards: отмена ошибочного начисления (ManualCorrection, пока не пройден MoneyReceived и строки свободны, INV-8) и создание ручного начисления на верного получателя, либо компенсирующее начисление после получения денег (ADR-0067). Разворота атрибуции в CRM не происходит.

A6. Конкурентная отмена по расторжению сделки

  1. Между открытием очереди и подтверждением сделка расторгнута — consumer deal-cancelled отменил начисление (CancelReason = DealCancelled, UC-RWD-003 A4): guard статуса → 409 REWARDS_ACCRUAL_INVALID_STATUS_TRANSITION, либо несовпадение rowVersion409. Фронт перечитывает начисление. Завершается.

Постусловие

  • Начисление в Status = Confirmed; построены ровно три строки распределения; строка Agent ссылается на AgentUserId = исполнитель на момент завершения сделки (получатель), не на OriginAgentUserId (атрибуция).
  • AgentUserId и OriginAgentUserId не изменены (иммутабельны); при корректировке (A4) изменена только сумма, получатель тот же.
  • CommissionAccrualStatusChangedKafkaEvent (→ Confirmed) опубликован атомарно; выплата недоступна до MoneyReceived.

Согласованность и межсервисное взаимодействие

  • Ожидает на входе. Начисление создано из pin.crm.deal-completed.v1 с уже зафиксированным получателем (CommissionRecipientUserId → AgentUserId) и атрибуцией (OriginAgentUserId). Момент фиксации получателя — завершение сделки в crm; rewards его не выбирает и не меняет.
  • Предоставляет на выходе. Ровно одно CommissionAccrualStatusChangedKafkaEvent; потребители — notifications (уведомление получателю и агентству) и self-consume проекции AgentPerformance (реагирует на → Paid/→ Cancelled, не на → Confirmed).
  • Граница ответственности. Кто получит комиссию — решается в crm до завершения (передача ведения); в rewards получатель уже иммутабелен. Ошибочный получатель — компенсацией в rewards, а не правкой CRM.

Возможные Ошибки

КодHTTPУсловиеПоведение клиента
403 FORBIDDEN403Нет rewards.accruals.confirmСкрыть кнопки подтверждения
404 NOT FOUND404Начисление не существует или DeveloperCompanyId != actor.CompanyId (fail closed)Вернуться к списку
REWARDS_ACCRUAL_INVALID_STATUS_TRANSITION409Status != PendingConfirmation (уже подтверждено/отменено, A6)Перечитать начисление
REWARDS_ACCRUAL_DISTRIBUTION_EXISTS409Строки распределения уже существуют (INV-4)Перечитать начисление
REWARDS_ACCRUAL_ADJUSTMENT_REASON_REQUIRED400Корректировка суммы без причины (A4)Подсветить поле причины
REWARDS_ACCRUAL_ADJUSTMENT_FORBIDDEN409Скорректированная сумма ≤ PlatformFeeAmount (A4)Показать минимально допустимую сумму
409 CONFLICT (RowVersion)409Конкурентная мутация (A6)Перечитать и повторить

Доменные События

СобытиеКогда поднимаетсяSubscribers
CommissionConfirmedEventPendingConfirmation → Confirmed (шаг 3)in-proc аудит

Kafka Интеграционные События

СобытиеTopicКогда публикуетсяConsumers
CommissionAccrualStatusChangedKafkaEvent.v1pin.rewards.commission-accrual-status-changed.v1Подтверждение (шаг 3)notifications — уведомление получателю/агентству

Acceptance Criteria

#КритерийПроверка
AC1Получатель Agent-строки — AgentUserId (Executor на момент завершения сделки), а не OriginAgentUserIdтест: сделка со сменой исполнителя → строка Agent ссылается на исполнителя завершения
AC2AgentUserId/OriginAgentUserId при подтверждении не пересчитываются (иммутабельны)тест: до/после Confirm оба поля неизменны
AC3Сменить получателя переназначением исполнителя после завершения нельзя (сделка терминальна)тест передачи ведения на CompletedCRM_DEAL_ALREADY_TERMINAL
AC4Исполнитель без смены (A1): получатель = основатель, распределение идентично типовомутест AgentUserId == OriginAgentUserId
AC5Уволенный исполнитель-получатель (A3): начисление подтверждается, строка Agent на его UserId, право сохраненотест увольнения между завершением и подтверждением
AC6Корректировка суммы (A4) не меняет получателятест Confirm с adjustedAmount → строка Agent на том же AgentUserId
AC7Ошибочный получатель лечится компенсацией в rewards, не правкой CRM (A5)тест: отмена/ручное начисление вместо разворота атрибуции

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

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

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

  • Базовый сценарий подтверждения: UC-RWD-004; создание начисления и снимки: UC-RWD-003; гейт денег: UC-RWD-010; корректировки: UC-RWD-011; уволенный агент: UC-RWD-017.
  • Domain entity: ../domain/commission-accrual.md (AgentUserId получатель / OriginAgentUserId атрибуция, INV-3/4/5/9).
  • Смена исполнителя и фиксация получателя: reassign-executor.md, deal-completed.md (CommissionRecipientUserId).
  • ADR: 0068 (единственный получатель = Executor), 0069 (co-broking — v-next), 0071 (единственный активный Executor, атрибуция), 0072 (увольнение), 0052 (cross-tenant доступ).