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

ADR-0067 — гейт получения денег (акт поступления средств)

Статус

Accepted

Дата

2026-07-15

Контекст

Текущая цепочка вознаграждений rewards заканчивается слишком рано: начисление создаётся по pin.crm.deal-completed.v1 в PendingConfirmation, застройщик подтверждает (Confirmed, строится распределение), и сразу после Confirmed доли агента доступны для включения в платёжное поручение (commission-accrual.md, payout-order.md). Проблемы:

  • Выплата раньше денег. Confirmed — признание обязательства застройщиком, но не факт оплаты. Платформа и агентство могут выплатить вознаграждение агенту и реферальному партнёру из средств, которые от застройщика ещё не поступили. Финансовый риск ложится на ПИН.
  • Пакетный характер поступлений. Операционно застройщик перечисляет комиссию пакетно — одним платежом за несколько сделок по акту/платёжному документу. В модели нет сущности, которая связывает «пришедшую сумму» с конкретным набором начислений и контролирует их сходимость.
  • Нет цивилизованной правки суммы. Сумма начисления считается автоматически по правилу застройщика, но фактическая сумма в акте может отличаться (индивидуальная скидка, перерасчёт базы). Единственный путь исправления сегодня — Cancel (ManualCorrection) + ручное пересоздание начисления: теряется история, рвётся связка DealId (unique), аудит корректировки не первокласcный.
  • Реферальные начисления (вводятся ADR-0066) должны подчиняться тому же правилу: партнёру платим только из фактически полученных денег.
  • Кошелёк (ADR-0065) требует однозначного момента кредитования внутренних балансов — им не может быть ни Confirmed (денег ещё нет), ни Paid (слишком поздно, баланс должен отражать доступное к выплате).

Решение

1. Цепочка подтверждений «сделка → комиссия → деньги → выплаты»

Фиксируется четырёхзвенная цепочка; каждое звено — отдельный акт воли отдельного актора:

#ШагАкторКонтекстСтатус/эффект
1Завершение сделки (существует, не меняется)менеджер застройщикаcrmDeal → Completed; fan-out создаёт начисление в PendingConfirmation
2Подтверждение начисления с опциональной корректировкой суммыменеджер застройщикаrewardsPendingConfirmation → Confirmed; строится распределение
3Подтверждение получения денег (батчево, актом)оператор ПИНrewardsConfirmed → MoneyReceived
4Выплаты — агенту и реферальному партнёруруководитель агентства / оператор ПИНrewardsплатёжные поручения → Paid

Подтверждение застройщика (шаг 2) расширяется опциональной корректировкой: в команде подтверждения передаются AdjustedAmount + обязательная причина; исходная автосчитанная сумма сохраняется в поле начисления OriginalCommissionAmount, факт корректировки фиксируется строкой аудита (п.4). Никакие выплаты (агентские и реферальные) не допускаются до шага 3.

2. Статусная модель начисления: один новый статус MoneyReceived

CommissionAccrualStatus расширяется ровно одним статусом:

PendingConfirmation → Confirmed → MoneyReceived → Paid | Cancelled

MoneyReceived — единый гейт всех денежных следствий:

  • платёжные поручения собирают только Agent-строки распределения начислений в MoneyReceived (ранее — Confirmed); Paid достижим только из MoneyReceived;
  • кредиты кошелька (ADR-0065) выполняются только по факту MoneyReceived — по событию pin.rewards.commission-money-received.v1 (key = Id начисления, payload включает строки распределения);
  • реферальное начисление переходит Pending → Approved автоматически при переходе родительского комиссионного начисления в MoneyReceived; реферальные поручения собирают только Approved-строки;
  • Cancelled по-прежнему достижим из PendingConfirmation и Confirmed; из MoneyReceived отмена запрещена — ошибки после получения денег лечатся компенсирующими операциями (п.3, п.4).

На начислении появляются поля MoneyReceivedAt и ссылка на акт (PlatformReceiptId).

3. Подтверждение получения — батчевое, через агрегат PlatformReceipt

Вводится агрегат PlatformReceipt — «Акт поступления средств», человекочитаемый номер RC-{yyyy}-{seq}:

  • Формирование. Оператор ПИН формирует акт по одному застройщику из начислений в статусе Confirmed; акт — платформенная сущность (владелец процесса — ПИН, ADR-0052), его позиции могут ссылаться на начисления разных агентств одного застройщика.
  • Сходимость сумм. Σ сумм позиций = сумме акта (сумме фактического поступления) — иначе регистрация отклоняется. Частичные оплаты — вне scope v1: если поступило меньше, акт формируется только на полностью покрытые начисления; частичное покрытие одного начисления не моделируется.
  • Эксклюзивность. Начисление состоит максимум в одном не-отменённом акте — двойное подтверждение получения по одному начислению исключено структурно.
  • Статусы. Draft → Registered | Cancelled. Registered — терминальный: регистрация атомарно переводит все позиции Confirmed → MoneyReceived, автоматически апрувит связанные реферальные начисления и публикует события получения денег. Cancelled (из Draft) освобождает начисления для другого акта.
  • Неизменяемость. Registered-акт — финансовый документ: не редактируется и не откатывается. Ошибки лечатся компенсациями (компенсирующее начисление/корректировка следующим периодом), не разворотом статусов.

4. Корректировки — first-class с аудитом

Вводится дочерняя сущность аудита CommissionAccrualAdjustment внутри границы агрегата начисления: Kind (GrossAmount | Distribution), старое/новое значение, обязательная причина, актор, момент.

  • Gross (CommissionAmount). Кто и когда вправе корректировать: застройщик — в момент подтверждения (шаг 2, AdjustedAmount в команде confirm); ПИН-админ (permission rewards.accruals.adjust) — в статусах PendingConfirmation | Confirmed. После MoneyReceived gross неизменяем — только компенсирующее начисление. Корректировка gross в Confirmed пересчитывает распределение по тем же применённым условиям (строка Platform — по снапшоту удержания, ADR-0066).
  • Distribution (доли агент/агентство внутри пула). Руководитель агентства корректирует доли per-deal в статусах Confirmed | MoneyReceived, пока строка Agent не включена в платёжное поручение. Перераспределяется только пул агентства (агент ↔ агентство); строка Platform неприкосновенна — её меняет только gross-пересчёт.
  • Механизм заменяет паттерн cancel+recreate для правки сумм: Cancel (ManualCorrection) остаётся только для полной отмены ошибочного начисления, а не как способ изменить сумму.

5. Границы: CRM не меняется

  • Статусы и операции сделки CRM не меняются: Deal → Completed остаётся терминальным (INV-DEAL-9), deal-completion-saga — без изменений; гейт денег живёт целиком в rewards.
  • PinManager-арбитраж сделок не становится money-confirmer-ом: подтверждение получения денег — отдельное право rewards.receipts.manage (платформенный scope), назначаемое финансовому оператору ПИН независимо от прав арбитража сделок в CRM.

Область Действия

ОбластьВходитНе входит
Сервисыrewards (статус-машина, акт, корректировки), wallet (момент кредита), notifications (уведомления о получении/корректировках)crm (статусы/операции сделки), chessboard, investor-owner
Контрактыстатус MoneyReceived; агрегат PlatformReceipt + API актов; корректировки в confirm/admin/agency API; топик commission-money-receivedконтракт pin.crm.deal-completed.v1 (не меняется этим ADR); банковская интеграция (решение №9 ТЗ)
Данныеполя MoneyReceivedAt/PlatformReceiptId/OriginalCommissionAmount; сущности PlatformReceipt (+позиции), CommissionAccrualAdjustmentчастичные оплаты/переплаты (вне scope v1); валютные операции (ADR-0048, RUB-only)
Эксплуатацияметрика возраста Confirmed-начислений без акта; отчётность по корректировкам

Последствия

КонтекстПоследствие
rewardsСтатус-машина начисления получает гейт MoneyReceived; источники строк поручений переезжают с Confirmed на MoneyReceived/Approved; появляются агрегат PlatformReceipt, аудит-сущность корректировок и операции create/register/cancel акта, adjust gross (ПИН) и adjust distribution (агентство). Дорабатываются commission-accrual.md, payout-order.md, confirm-commission-accrual.md; новые доки — domain/platform-receipt.md, sagas/commission-settlement-saga.md (пути по конвенции).
walletКредиты холдерских счетов — строго по commission-money-received / авто-апруву рефералки (ADR-0065); до MoneyReceived балансы не растут.
notificationsНовые уведомления: «деньги получены» (агенту/агентству/партнёру), «сумма скорректирована» (с причиной).
crmБез изменений модели; в саге завершения сделки появляется только ссылка на продолжение цепочки в rewards.
identityНовые permissions: rewards.receipts.manage, rewards.accruals.adjust (платформенный scope).
Операционный процессВыплаты блокируются до регистрации акта — появляется обязательный ручной шаг финансового оператора ПИН (осознанная плата за снятие риска «выплата раньше денег»).

Затронутые контракты и совместимость

КонтрактВерсияСовместимостьМиграция
CommissionAccrualStatus (+MoneyReceived)additive для потребителей статусаgreenfield, ContractVersion не меняется
pin.rewards.commission-money-received.v11новый топикпотребители: wallet, notifications
pin.rewards.commission-accrual-status-changed.v11additive (новый ToStatus, поле MoneyReceivedAt?)
pin.rewards.payout-order-status-changed.v11additive (OrderKind, позиции при Paid)потребитель wallet добавляется
pin.rewards.referral-accruals.v11новый топик (ADR-0066; авто-Approved — этим ADR)
confirm-commission-accrual (API)additive (AdjustedAmount?, AdjustmentReason?)
API актов и корректировок (create/register/cancel-receipt, adjust gross/distribution)новые операции

Альтернативы (рассмотрено и отклонено)

ВариантОписаниеПричина отклонения
Per-accrual подтверждение получения без актаОператор ПИН отмечает «деньги получены» на каждом начислении отдельной командойОперационно деньги приходят от застройщика пакетно одним платежом за несколько сделок: поштучная отметка — N ручных действий без документа-основания, нет контроля сходимости Σ начислений с фактическим поступлением, легко «подтвердить» больше, чем пришло
Отдельный сервис биллинга/взаиморасчётовВыделенный bounded context для актов, сверок и расчётов с застройщикамиПротиворечит ADR-0056: объёмы малы (десятки тысяч сделок в год), акт — один агрегат с двумя переходами; отдельный сервис добавил бы межсервисную согласованность там, где достаточно локальной транзакции rewards
Гейт денег как статус сделки CRMРасширить DealStatus этапом «деньги получены»Ломает терминальность Completed (INV-DEAL-9) и границы контекстов: сделка — факт продажи (crm), деньги комиссии — обязательство и расчёты (rewards); смешение потребовало бы cross-context статусной машины
Редактируемый/откатываемый Registered-актРазрешить правку или разворот зарегистрированного актаФинансовый документ-основание должен быть неизменяем (аудит, сверка с бухгалтерией); откат каскадно разворачивал бы MoneyReceived, апрувы рефералок и кредиты кошелька — компенсации проще и прозрачнее отката

Риски

РискМитигация
Оператор ПИН — узкое место: без регистрации акта выплаты заблокированы по всей платформеМетрика возраста начислений в Confirmed без акта + алерт; батчевость сводит операцию к «один акт на платёж застройщика»
Расхождение фактического платежа и Σ Confirmed-начислений (частичная оплата, переплата)Вне scope v1 — акт формируется на полностью покрытые начисления; непокрытые остаются в Confirmed; расхождение сумм — корректировкой gross до включения в акт; полноценные частичные оплаты — отдельным решением при подтверждённой потребности
Ошибочно зарегистрированный акт необратимДвухшаговость Draft → Registered с предпросмотром состава и Σ; компенсирующие начисления/корректировки как штатный путь исправления
Корректировки становятся заменой нормального расчёта (злоупотребление)Обязательная причина + актор в каждой строке аудита; отчётность по корректировкам; gross после MoneyReceived неизменяем
Гонка «корректировка распределения ↔ сборка платёжного поручения»Guard «строка Agent не включена в поручение» + оптимистическая блокировка начисления (обе операции мутируют один агрегат)

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

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

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

  • Дорабатываемые доки rewards: commission-accrual.md (статус-машина, поля, аудит корректировок), payout-order.md (источник строк — MoneyReceived/Approved, виды поручений), confirm-commission-accrual.md (корректировка при подтверждении).
  • Новые доки rewards (пути по конвенции): domain/platform-receipt.md, domain/referral-accrual.md, events/commission-money-received.md, api/platform/{create,register,list}-platform-receipt.md, api/platform/adjust-commission-accrual.md, api/agency/adjust-accrual-distribution.md, sagas/commission-settlement-saga.md.
  • Сага-источник цепочки: deal-completion-saga.md (fan-out deal-completed; статусы сделки не меняются).
  • ADR-0065 (кошелёк: кредит по гейту MoneyReceived), ADR-0066 (строка Platform, реферальные ставки и начисления), ADR-0056 (simplicity-first), ADR-0052 (tenancy: акт — платформенный процесс), ADR-0048 (RUB-only), решение №9 ТЗ (исполнение платежей отложено — Paid остаётся ручной отметкой).