UC-RWD-016. Застройщик подтверждает начисление после смены исполнителя
Назначение
Описывает частный случай подтверждения начисления застройщиком, когда исполнитель сделки менялся до её
завершения: ведение передавали другому агенту (передача ведения) либо прежний исполнитель был уволен с
переназначением преемника. Получатель комиссии зафиксирован снимком на момент завершения сделки —
CommissionRecipientUserId = ExecutorAgentUserId (Executor на момент Completed), — и именно этот
агент, а не изначальный инициатор (Origin), становится получателем Agent-строки распределения при
подтверждении. Сценарий подчёркивает разделение двух ролей начисления: получатель (AgentUserId =
Executor на завершении) и атрибуция (OriginAgentUserId = агент-основатель сделки), а также то, что
после завершения сделки сменить получателя переназначением исполнителя уже нельзя. Связан с контекстом
rewards.
Metadata
| Поле | Значение |
|---|---|
| ID | UC-RWD-016 |
| Контекст | rewards (см. docs/06-services/rewards/) |
| Тип | альтернативный (частный случай подтверждения — UC-RWD-004 при смене исполнителя) |
| Статус | draft |
Актор
| Поле | Значение |
|---|---|
| Роль | Девелопер (менеджер застройщика) |
| Permissions / scope | rewards.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 |
| Триггерящий элемент | ЛК застройщика → «Вознаграждения» → «Начисления на подтверждение» → строка начисления → «Подтвердить» |
Основной Поток
- Менеджер открывает очередь начислений (
status = PendingConfirmation, permissionrewards.accruals.confirm, выборка поDeveloperCompanyId = CompanyIdактора;InvestorUserIdв проекции замаскирован — отдаётся толькоDealId). - В строке начисления менеджер сверяет сделку по
DealIdи snapshot правила (AppliedRuleSnapshot). Получатель начисления — текущий исполнитель на момент завершения (AgentUserId), который может отличаться от агента-основателя (OriginAgentUserId); застройщику раскладка получателей не отдаётся — он подтверждает обязательство и сумму, а не выбирает получателя. - Менеджер нажимает «Подтвердить» (при расхождении суммы с договором — «Подтвердить с корректировкой»,
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).
- Доступ и проверка.
- Фронт убирает начисление из очереди;
notificationsуведомляет получателя (AgentUserId— исполнитель на завершении) и руководителя его агентства о подтверждении. Выплата станет доступна только после регистрации акта поступления средств (MoneyReceived, UC-RWD-010).
Альтернативные Потоки
A1. Исполнитель не менялся (получатель = основатель)
AgentUserId == OriginAgentUserId— типовой случай (UC-RWD-004): исполнитель на завершении совпадает с агентом-основателем сделки. Подтверждение и распределение идентичны основному потоку; строкаAgentссылается на того же агента. Различие получатель/атрибуция вырождается. Завершается.
A2. Прежний исполнитель — участник состава на момент завершения
- При передаче ведения прежнего исполнителя могли оставить в составе ролью
CoAgent(reassign-executor.md,DemotePriorToCoAgent). Это не влияет на начисление: получатель — единственный финансовыйExecutorна момент завершения (ADR-0068), распределение строго трёхстрочное с однойAgent-строкой (ADR-0069, co-broking — v-next). Прочие агентские участники нефинансовы. Далее — как в основном потоке.
A3. Исполнитель-получатель уволен между завершением и подтверждением
- Агент-получатель (
AgentUserId) был уволен из агентства после завершения сделки, но до подтверждения (ADR-0072, UC-RWD-017). Начисление не пересчитывается:AgentUserIdиммутабелен, право на заработанное сохраняется. Застройщик подтверждает начисление в обычном порядке; строкаAgentссылается наUserIdуволенного агента; при последующемMoneyReceivedкредитуется его user-centric счёт кошелька (переживает смену/утрату агентства, ADR-0061 п.5). Не ошибка. Завершается по основному потоку.
A4. Подтверждение с корректировкой суммы
- Менеджер передаёт скорректированную сумму с обязательной причиной (INV-11, как UC-RWD-004 A8):
CommissionAmountзаменяется, исходная фиксируется вOriginalCommissionAmount, создаётся аудит-строкаGrossAmount, пул пересчитывается,PlatformFeeAmountнеизменен. ПолучательAgent-строки не меняется — корректировка касается суммы, а не носителя ролиExecutor. Без причины —400; сумма ≤PlatformFeeAmount—409. Далее — как в основном потоке.
A5. Обнаружен ошибочный получатель
- После завершения выясняется, что комиссию должен был получить другой агент (ошибка передачи ведения до
завершения). Переназначить получателя нельзя: сделка терминальна,
AgentUserIdиммутабелен. Исправление — только средствамиrewards: отмена ошибочного начисления (ManualCorrection, пока не пройденMoneyReceivedи строки свободны, INV-8) и создание ручного начисления на верного получателя, либо компенсирующее начисление после получения денег (ADR-0067). Разворота атрибуции в CRM не происходит.
A6. Конкурентная отмена по расторжению сделки
- Между открытием очереди и подтверждением сделка расторгнута — consumer
deal-cancelledотменил начисление (CancelReason = DealCancelled, UC-RWD-003 A4): guard статуса →409REWARDS_ACCRUAL_INVALID_STATUS_TRANSITION, либо несовпадениеrowVersion→409. Фронт перечитывает начисление. Завершается.
Постусловие
- Начисление в
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 FORBIDDEN | 403 | Нет rewards.accruals.confirm | Скрыть кнопки подтверждения |
404 NOT FOUND | 404 | Начисление не существует или DeveloperCompanyId != actor.CompanyId (fail closed) | Вернуться к списку |
REWARDS_ACCRUAL_INVALID_STATUS_TRANSITION | 409 | Status != PendingConfirmation (уже подтверждено/отменено, A6) | Перечитать начисление |
REWARDS_ACCRUAL_DISTRIBUTION_EXISTS | 409 | Строки распределения уже существуют (INV-4) | Перечитать начисление |
REWARDS_ACCRUAL_ADJUSTMENT_REASON_REQUIRED | 400 | Корректировка суммы без причины (A4) | Подсветить поле причины |
REWARDS_ACCRUAL_ADJUSTMENT_FORBIDDEN | 409 | Скорректированная сумма ≤ PlatformFeeAmount (A4) | Показать минимально допустимую сумму |
409 CONFLICT (RowVersion) | 409 | Конкурентная мутация (A6) | Перечитать и повторить |
Доменные События
| Событие | Когда поднимается | Subscribers |
|---|---|---|
CommissionConfirmedEvent | PendingConfirmation → Confirmed (шаг 3) | in-proc аудит |
Kafka Интеграционные События
| Событие | Topic | Когда публикуется | Consumers |
|---|---|---|---|
CommissionAccrualStatusChangedKafkaEvent.v1 | pin.rewards.commission-accrual-status-changed.v1 | Подтверждение (шаг 3) | notifications — уведомление получателю/агентству |
Acceptance Criteria
| # | Критерий | Проверка |
|---|---|---|
| AC1 | Получатель Agent-строки — AgentUserId (Executor на момент завершения сделки), а не OriginAgentUserId | тест: сделка со сменой исполнителя → строка Agent ссылается на исполнителя завершения |
| AC2 | AgentUserId/OriginAgentUserId при подтверждении не пересчитываются (иммутабельны) | тест: до/после Confirm оба поля неизменны |
| AC3 | Сменить получателя переназначением исполнителя после завершения нельзя (сделка терминальна) | тест передачи ведения на Completed → CRM_DEAL_ALREADY_TERMINAL |
| AC4 | Исполнитель без смены (A1): получатель = основатель, распределение идентично типовому | тест AgentUserId == OriginAgentUserId |
| AC5 | Уволенный исполнитель-получатель (A3): начисление подтверждается, строка Agent на его UserId, право сохранено | тест увольнения между завершением и подтверждением |
| AC6 | Корректировка суммы (A4) не меняет получателя | тест Confirm с adjustedAmount → строка Agent на том же AgentUserId |
| AC7 | Ошибочный получатель лечится компенсацией в rewards, не правкой CRM (A5) | тест: отмена/ручное начисление вместо разворота атрибуции |
Обратные ссылки
Автоссылки: где используется этот документ.
- Use Cases (1): UC-RWD-017. Уволенный агент: судьба заработанных комиссий
- registries (1): Реестр use cases и user stories PIN
Связанные Документы
- Базовый сценарий подтверждения: 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 доступ).