UC-RWD-010. Акт поступления средств — гейт получения денег
Назначение
Описывает сценарий подтверждения фактического получения комиссионных денег от застройщика: финансовый
оператор ПИН заводит «Акт поступления средств» (RC-{yyyy}-{seq}) по пакетному платежу застройщика,
подбирает покрываемые подтверждённые начисления, сверяет сходимость (Σ позиций = сумме поступления) и
регистрирует акт. Регистрация — единый гейт всех денежных следствий (ADR-0067): начисления атомарно
переходят Confirmed → MoneyReceived, реферальные начисления — Pending → Approved, кошельки
получателей кредитуются, доли открываются платёжным поручениям. Связан с контекстом rewards.
Metadata
| Поле | Значение |
|---|---|
| ID | UC-RWD-010 |
| Контекст | rewards (см. docs/06-services/rewards/) |
| Тип | основной |
| Статус | draft |
Актор
| Поле | Значение |
|---|---|
| Роль | Финансовый оператор ПИН |
| Permissions / scope | rewards.receipts.manage — платформенный scope; право независимо от прав арбитража сделок CRM (ADR-0067 п.5) |
| Tenant | вне tenant-модели — платформенная сущность (ADR-0052); позиции акта могут ссылаться на начисления разных агентств одного застройщика |
| Record-level | не применяется |
Предусловие
- Существуют начисления
Status = Confirmedзастройщика-плательщика (UC-RWD-004), не включённые в другой не-отменённый акт (INV-RC-3). - Застройщик перечислил комиссию (пакетный платёж — одна платёжка за несколько сделок); у оператора — платёжный документ с суммой и реквизитом.
- Частичные оплаты — вне scope v1: акт формируется только на полностью покрытые начисления (../domain/platform-receipt.md).
Триггер
| Поле | Значение |
|---|---|
| Источник | UI button |
| Триггерящий элемент | Админ-панель ПИН → «Финансы» → «Акты поступления средств» → «Создать акт» / «Зарегистрировать» |
Основной Поток
- Оператор сверяет платёж застройщика с реестром
Confirmed-начислений (../api/platform/list-platform-receipts.md — акты; кандидаты в позиции — свободныеConfirmedзастройщика) и нажимает «Создать акт». - Фронт вызывает
POST /api/v1/platform/receiptsс телом{ developerCompanyId, amount (сумма фактического поступления), paymentReference? (реквизит платёжки), comment?, accrualIds? (явный состав, 1..500) | periodFrom?/periodTo? (автоподбор по DealCompletedAt) }(../api/platform/create-platform-receipt.md). - Система создаёт черновик: подбор позиций — каждое начисление
Confirmed, того же застройщика (INV-RC-1), не в другом не-отменённом акте (INV-RC-3); на позицию фиксируетсяAmount = CommissionAmountначисления на момент включения; номерRC-{yyyy}-{seq}; пустой состав —REWARDS_RECEIPT_EMPTY. Ответ — актDraftс предпросмотром сходимости:ItemsTotal(Σ позиций) противAmount; расхождение вDraftдопустимо. - Оператор выравнивает расхождение (если есть) до регистрации: правит состав, корректирует gross начисления (UC-RWD-011) или исправляет сумму акта (A1); затем нажимает «Зарегистрировать».
- Фронт вызывает
POST /api/v1/platform/receipts/{receiptId}/register { expectedVersion }(../api/platform/register-platform-receipt.md). - Система перепроверяет состав по актуальному состоянию (позиции могли устареть): каждое начисление —
Confirmedтого же застройщика (нарушение — A3), сумма позиции равна текущемуCommissionAmount(расхождение — A2),ItemsCount >= 1; сходимость Σ позиций =Amountакта (INV-RC-2, иначе — A1). - Атомарный каскад (INV-RC-4) — в одном согласованном изменении, «либо всё, либо ничего»:
- акт →
Registered(RegisteredAt, актор) — терминальный, неизменяемый, не откатывается; - каждое начисление-позиция →
Confirmed → MoneyReceived(MoneyReceivedAt = RegisteredAt,PlatformReceiptId); - каждая
Pending-рефералка, чей родитель вошёл в акт, →Approved(INV-RA-3, UC-RWD-009); - публикуются события:
commission-money-received— по одному на начисление (payload включаетDistributions[]Platform/Agency/Agent),commission-accrual-status-changed(→ MoneyReceived) — по каждому, статусные события рефералок (→ Approved) — по каждой.
- акт →
- Кредиты кошелька (асинхронно, окно — секунды).
walletпо событиям кредитует счета: доля агента →AgentCommission, удержание агентства →AgencyCommission, строкаPlatform→PlatformRevenue, апрувнутая рефералка →ReferralCommissionпартнёра; идемпотентность —(OperationKind, SourceId)(ADR-0065, ../../wallet/README.md). - С этого момента
Agent-строки распределений доступны платёжным поручениям агентств (UC-RWD-005),Approved-рефералки — реферальным поручениям (UC-RWD-009);notificationsуведомляет агентов/агентства («деньги получены — доля доступна к выплате») и партнёров.
Альтернативные Потоки
A1. Сумма не сходится (INV-RC-2)
- При регистрации Σ позиций ≠
Amountакта →REWARDS_RECEIPT_SUM_MISMATCH(409), акт остаётсяDraft, каскад не выполняется. Оператор лечит расхождение: исключает непокрытые позиции (они остаютсяConfirmedдо следующего акта), корректирует gross начисления (UC-RWD-011) или исправляет сумму акта по платёжке. Возвращается к шагу 5.
A2. Позиция устарела (gross скорректирован после включения)
- Сумма позиции разошлась с актуальным
CommissionAmount(корректировка ПИН-админа между включением и регистрацией) →REWARDS_RECEIPT_ITEM_STALE(409). Оператор пересобирает состав по актуальным суммам. Возвращается к шагу 4.
A3. Начисление-позиция стало недействительным
- Начисление отменено из
Confirmed(расторжение сделки) до регистрации →REWARDS_RECEIPT_ITEM_INVALID(409). Оператор исключает позицию и корректирует сумму акта. Возвращается к шагу 4.
A4. Гонка двух актов за одно начисление
- Начисление уже в составе другого не-отменённого акта (
Draft/Registered) — уникальный индекс по начислению (INV-RC-3): победитель включает позицию, проигравший получаетREWARDS_RECEIPT_ACCRUAL_ALREADY_INCLUDED(409) и пересобирает состав. Двойное подтверждение получения денег исключено структурно.
A5. Отмена черновика
Draft-акт отменяется (Draft → Cancelled): позиции физически удаляются — начисления освобождаются и доступны другому акту.Registered-акт не отменяется (REWARDS_RECEIPT_REGISTERED_IMMUTABLE). Завершается.
A6. Идемпотентный повтор регистрации
- Повторный
registerужеRegistered-акта (сетевой ретрай клиента) → no-op:200с текущей проекцией, каскад и события не повторяются — двойногоMoneyReceived/Approvedне бывает. Потребители событий дополнительно дедуплицируют по id начисления + переходу.
A7. Ошибочно зарегистрированный акт
Registered-акт не разворачивается (INV-RC-4) — каскад уже разошёлся по кошелькам и апрувам. Исправление — компенсирующее ручное начисление / корректировка следующим периодом (../sagas/commission-settlement-saga.md, «Компенсации»); двухшаговостьDraft → Registeredс предпросмотром Σ снижает риск. Завершается.
Постусловие
- Акт в
Registered(терминальный, неизменяемый финансовый документ; сам себе аудит-след); Σ позиций = сумме фактического поступления. - Все начисления-позиции — в
MoneyReceived(MoneyReceivedAt,PlatformReceiptId); ихAgent-строки доступны поручениям; всеPending-рефералки родителей — вApproved. - События опубликованы атомарно с каскадом; кошельки получателей кредитованы асинхронно (окно —
секунды; в окне статусы в
rewardsуже новые, баланс — ещё прежний, это лаг, не ошибка). - Агентства и застройщики акты не видят — им виден эффект:
Status = MoneyReceived,MoneyReceivedAt/PlatformReceiptIdсвоих начислений.
Возможные Ошибки
| Код | HTTP | Условие | Поведение клиента |
|---|---|---|---|
403 FORBIDDEN | 403 | Нет rewards.receipts.manage | Скрыть работу с актами |
REWARDS_RECEIPT_NOT_FOUND | 404 | Акт не существует | Вернуться к реестру |
REWARDS_RECEIPT_EMPTY | 409 | Подбор позиций пуст (нет свободных Confirmed застройщика) | Проверить фильтры/период |
REWARDS_RECEIPT_ITEM_INVALID | 409 | Позиция не Confirmed / чужого застройщика (INV-RC-1, A3) | Исключить позицию, поправить сумму |
REWARDS_RECEIPT_ACCRUAL_ALREADY_INCLUDED | 409 | Начисление уже в другом не-отменённом акте (INV-RC-3, A4) | Пересобрать состав |
REWARDS_RECEIPT_ITEM_STALE | 409 | Сумма позиции устарела после корректировки gross (A2) | Пересобрать состав |
REWARDS_RECEIPT_SUM_MISMATCH | 409 | Σ позиций ≠ сумме акта при регистрации (INV-RC-2, A1) | Выровнять суммы до регистрации |
REWARDS_RECEIPT_REGISTERED_IMMUTABLE | 409 | Правка/отмена Registered-акта (INV-RC-4, A5/A7) | Компенсация отдельным начислением |
REWARDS_RECEIPT_COMPOSITION_LOCKED | 409 | Правка состава вне Draft (INV-RC-4) | Перечитать акт |
REWARDS_RECEIPT_INVALID_STATUS_TRANSITION | 409 | Регистрация из Cancelled и другие недопустимые переходы | Перечитать акт |
REWARDS_CURRENCY_MISMATCH | 400 | Валюта позиции/акта не совпадает | Проверить данные |
REWARDS_CONCURRENCY_CONFLICT | 409 | Параллельные register/cancel/правка состава | Перечитать и повторить |
Доменные События
| Событие | Когда поднимается | Subscribers |
|---|---|---|
PlatformReceiptCreatedEvent | Создание черновика (шаг 3) | in-proc аудит |
PlatformReceiptRegisteredEvent | Draft → Registered (шаг 7) | in-proc аудит |
Kafka Интеграционные События
| Событие | Topic | Когда публикуется | Consumers |
|---|---|---|---|
CommissionMoneyReceivedKafkaEvent.v1 | pin.rewards.commission-money-received.v1 | Регистрация акта — по одному на каждое начисление-позицию (key = id начисления; payload: PlatformReceiptId, MoneyReceivedAt, Distributions[] {RecipientType, RecipientUserId?, Amount}) | wallet (кредиты счетов по строкам, идемпотентность (CommissionCredit, AccrualId)), notifications |
CommissionAccrualStatusChangedKafkaEvent.v1 | pin.rewards.commission-accrual-status-changed.v1 | Confirmed → MoneyReceived — по каждому начислению акта | notifications; self-consume AgentPerformance (переход — no-op для метрик, ../consumers/agent-performance-projection.md) |
ReferralAccrualStatusChangedKafkaEvent.v1 | pin.rewards.referral-accruals.v1 | Pending → Approved — по каждой апрувнутой рефералке | notifications; wallet (кредит ReferralCommission) |
Acceptance Criteria
| # | Критерий | Проверка |
|---|---|---|
| AC1 | Регистрация атомарна: акт Registered + все позиции MoneyReceived + рефералки Approved + все события — одно согласованное изменение; при отказе любого шага акт целиком остаётся Draft | интеграционный тест каскада + инъекция сбоя |
| AC2 | Σ позиций ≠ сумме акта блокирует регистрацию (INV-RC-2, A1) | тест расхождения → 409, статусы не изменены |
| AC3 | Начисление — максимум в одном не-отменённом акте (INV-RC-3, A4), включая гонку | параллельное включение в два акта → один победитель |
| AC4 | Корректировка gross после включения ловится при регистрации (ITEM_STALE, A2) | adjust между включением и register → 409 |
| AC5 | Повторный register — идемпотентный no-op: без второго каскада и событий (A6) | двойной вызов → одна публикация каждого события |
| AC6 | Registered-акт неизменяем и не отменяется (INV-RC-4, A7) | попытки правки/отмены → 409 |
| AC7 | Отмена черновика освобождает начисления для другого акта (A5) | cancel → повторное включение в новый акт успешно |
| AC8 | Кредиты кошелька идемпотентны: повторная доставка события не двоит баланс | replay commission-money-received → один кредит на строку |
| AC9 | После регистрации Agent-строки доступны поручению, до — нет (гейт ADR-0067) | автосборка поручения до/после акта |
| AC10 | Возраст старейшего Confirmed без акта мониторится (rewards_accrual_awaiting_receipt_max_age_minutes) | метрика растёт до регистрации, сбрасывается после |
Обратные ссылки
Автоссылки: где используется этот документ.
- Use Cases (4): UC-RWD-016. Застройщик подтверждает начисление после смены исполнителя, UC-RWD-017. Уволенный агент: судьба заработанных комиссий, US-реестр: Реферальный партнёр (US-PRT), US-реестр: Финансовый блок ПИН (US-FIN)
- scenarios (1): UC-OVR-010. От завершённой сделки до выплаты комиссии и реферального вознаграждения
- registries (1): Реестр use cases и user stories PIN
Связанные Документы
- Domain entity: ../domain/platform-receipt.md (поля, INV-RC-1..4, «Регистрация (атомарный каскад)», stateDiagram).
- API методы: ../api/platform/create-platform-receipt.md, ../api/platform/register-platform-receipt.md, ../api/platform/list-platform-receipts.md.
- Гейт и статусы: ../domain/commission-accrual.md (
MoneyReceived, INV-7/8), ../domain/referral-accrual.md (INV-RA-3). - Событие: ../events/commission-money-received.md; кошелёк — ../../wallet/README.md, ../../wallet/consumers/rewards-commission-money-received.md.
- Сквозной процесс: ../sagas/commission-settlement-saga.md.
- Связанные UC: UC-RWD-004 (источник
Confirmed-позиций), UC-RWD-005 (поручения после гейта), UC-RWD-009 (авто-апрув рефералок), UC-RWD-011 (корректировки до гейта). - ADR: 0067 (гейт, батчевость, неизменяемость), 0065 (кредиты кошелька), 0066 (три строки распределения), ADR-0056 (акт вместо отдельного биллинг-сервиса).