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 | Завершение сделки (существует, не меняется) | менеджер застройщика | crm | Deal → Completed; fan-out создаёт начисление в PendingConfirmation |
| 2 | Подтверждение начисления с опциональной корректировкой суммы | менеджер застройщика | rewards | PendingConfirmation → Confirmed; строится распределение |
| 3 | Подтверждение получения денег (батчево, актом) | оператор ПИН | rewards | Confirmed → 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); ПИН-админ (permissionrewards.accruals.adjust) — в статусахPendingConfirmation|Confirmed. ПослеMoneyReceivedgross неизменяем — только компенсирующее начисление. Корректировка 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.v1 | 1 | новый топик | потребители: wallet, notifications |
pin.rewards.commission-accrual-status-changed.v1 | 1 | additive (новый ToStatus, поле MoneyReceivedAt?) | — |
pin.rewards.payout-order-status-changed.v1 | 1 | additive (OrderKind, позиции при Paid) | потребитель wallet добавляется |
pin.rewards.referral-accruals.v1 | 1 | новый топик (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 не включена в поручение» + оптимистическая блокировка начисления (обе операции мутируют один агрегат) |
Обратные ссылки
Автоссылки: где используется этот документ.
- Домен (CQRS) (3): Entity / Aggregate: PlatformCommissionTermsEntity, Entity / Aggregate: PlatformReceiptEntity, Entity / Aggregate: ReferralAccrualEntity
- API (7): GET /api/v1/platform/receipts — Реестр актов поступления средств, POST /api/v1/platform/commission-accruals/{accrualId}/adjust — Корректировка суммы начисления (gross), POST /api/v1/platform/receipts — Создание акта поступления средств (черновик), POST /api/v1/platform/receipts/{receiptId}/register — Регистрация акта поступления средств, POST /api/v1/platform/referral-accruals/{accrualId}/adjust — Корректировка суммы реферального начисления, POST /api/v1/platform/referral-payout-orders — Создание реферального платёжного поручения, POST /api/v1/platform/referral-payout-orders/{orderId}/mark-paid — Отметка о выплате реферального поручения
- Use Cases (7): UC-RWD-010. Акт поступления средств — гейт получения денег, UC-RWD-011. Корректировки начисления комиссии, UC-RWD-013. Агент предлагает корректировку своей доли, руководитель одобряет, UC-RWD-014. ПИН корректирует размер реферального вознаграждения партнёра, UC-RWD-017. Уволенный агент: судьба заработанных комиссий, UC-WLT-001. Получение денег кредитует балансы получателей, UC-WLT-006. Компенсирующее начисление отражается в балансе после MoneyReceived
- События (4): Event Contract: pin.rewards.commission-distribution-adjusted (CommissionDistributionAdjustedKafkaEvent), Event Contract: pin.rewards.commission-money-received (CommissionMoneyReceivedKafkaEvent), Event Contract: pin.rewards.referral-accrual-created (ReferralAccrualCreatedKafkaEvent), Event Contract: pin.rewards.referral-accrual-status-changed (ReferralAccrualStatusChangedKafkaEvent)
- Консюмеры (5): Consumer: CRM Deal Cancelled, Consumer: CRM Deal Completed, Consumer: Rewards Commission Distribution Adjusted, Consumer: Rewards Commission Money Received, Consumer: Rewards Referral Accrual Status Changed
- Саги (1): Saga «Commission Settlement» (финансовый контур вознаграждений)
- adr (3): ADR-0065 — контекст wallet (внутренние балансы и ledger), ADR-0066 — удержание ПИН и реферальные ставки, ADR-0068 — Scoped и time-bounded конфигурация комиссионных и реферальных ставок
- scenarios (1): UC-OVR-010. От завершённой сделки до выплаты комиссии и реферального вознаграждения
- index.md (2): Rewards Service, Wallet Service
Связанные документы
- Дорабатываемые доки
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остаётся ручной отметкой).