Event Contract: pin.rewards.commission-accrual-status-changed (CommissionAccrualStatusChangedKafkaEvent)
Версионируемый контракт интеграционного события «статус начисления комиссии изменился».
Consumer-документы ссылаются сюда, не переописывая payload.
Дом полей — entity: CommissionAccrualEntity.
Назначение
Фиксирует каждый переход Status начисления: подтверждение застройщиком
(PendingConfirmation → Confirmed — «комиссия подтверждена» в ЛК агента), прохождение гейта получения
денег (Confirmed → MoneyReceived — регистрация акта поступления средств,
platform-receipt.md, ADR-0067), выплату (MoneyReceived → Paid,
каскад платёжного поручения) и отмену (→ Cancelled: отмена сделки, отказ застройщика, ручная
корректировка). Producer — rewards; единственный внешний потребитель — notifications (адресные
уведомления агенту и агентству без синхронного запроса в rewards; переход → MoneyReceived для
notifications — no-op: уведомление о получении денег отправляется по
commission-money-received.md). Сводки ЛК события не потребляют —
считаются агрегациями по хранилищу rewards (решение №6 ../README.md, ADR-0056).
Metadata
| Поле | Значение |
|---|---|
| Event type | pin.rewards.commission-accrual-status-changed |
| C# type | CommissionAccrualStatusChangedKafkaEvent |
| Topic | pin.rewards.commission-accrual-status-changed.v1 |
| Partitions / key | 3 / key = Id начисления — строгий порядок переходов per-accrual |
| ContractVersion | 1 |
| Producer | rewards: команды Confirm/Reject/Cancel (admin-API застройщика), consumer crm-deal-cancelled.md, регистрация акта поступления средств (api/platform/register-platform-receipt.md — по конвенции; platform-receipt.md), каскад MarkPaid поручения (payout-order.md, INV-7) |
| Статус | draft |
Публикуется атомарно с переходом статуса начисления (сообщение и мутация фиксируются в одном
согласованном изменении). Ключ Kafka = Id, порядок событий — per-accrual; дедупликация у
потребителя — по идентификатору сообщения, монотонность — по Version.
Payload CommissionAccrualStatusChangedKafkaEvent
Snapshot-поля — из дома commission-accrual.md. Поля AgentUserId
и CommissionAmount — контракт-уровневые дополнения к сводке событий в entity-документе (адресат и
текст уведомления без lookup в rewards); дом самих полей — тот же entity-документ.
| Поле | Тип | Обязательность | Источник (поле агрегата) | Compatibility | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | CommissionAccrualEntity.Id | Immutable | Ключ агрегата (= Kafka key). |
TenantId | Guid | Да | CommissionAccrualEntity.TenantId | Immutable | Агентство-получатель (tenant boundary). |
DealId | Guid | Да | CommissionAccrualEntity.DealId | Immutable | Сделка crm — контекст уведомления. |
AgentUserId | Guid | Да | CommissionAccrualEntity.AgentUserId | Immutable | Агент — адресат уведомления. |
FromStatus | string (enum CommissionAccrualStatus) | Да | статус до перехода | Additive only (новые значения) | PendingConfirmation / Confirmed / MoneyReceived. |
ToStatus | string (enum CommissionAccrualStatus) | Да | CommissionAccrualEntity.Status после перехода | Additive only | Confirmed / MoneyReceived / Paid / Cancelled. |
CancelReason | string? (enum CommissionCancelReason) | Да при ToStatus = Cancelled, иначе null | CommissionAccrualEntity.CancelReason | Additive only | DealCancelled / DeveloperRejected / ManualCorrection. |
CommissionAmount | MoneyModel | Да | CommissionAccrualEntity.CommissionAmount | Additive only | Сумма комиссии (после корректировок, если были — INV-11) — текст уведомления. |
ConfirmedAt | DateTimeOffset? | Да при ToStatus = Confirmed, иначе может быть null | CommissionAccrualEntity.ConfirmedAt | Additive only | Момент подтверждения застройщиком. |
MoneyReceivedAt | DateTimeOffset? | Да при ToStatus = MoneyReceived, иначе может быть null | CommissionAccrualEntity.MoneyReceivedAt | Additive only | Момент подтверждения получения денег (= регистрация акта, platform-receipt.md). |
PaidAt | DateTimeOffset? | Да при ToStatus = Paid, иначе null | CommissionAccrualEntity.PaidAt | Additive only | Момент выплаты (по платёжному поручению). |
Version | long | Да | CommissionAccrualEntity.Version после перехода | Immutable semantics | Доменная версия — монотонный guard потребителя против out-of-order. |
Вложенная модель MoneyModel
Проекция VO Money (commission-accrual.md, раздел «Value objects»).
| Поле | Тип | Обязательность | Ограничения | Описание |
|---|---|---|---|---|
Value | decimal | Да | >= 0, scale 2 | Сумма. |
CurrencyId | short | Да | ссылка на валюту справочника; RUB-only (ADR-0048) | Типизированная ссылка на валюту. |
CurrencySnapshot | CurrencySnapshot | Да | ../../handbook/domain/snapshots.md | Display-снапшот валюты на момент публикации. |
Payload не содержит: InvestorUserId, цены объекта недвижимости, CommissionPercent, snapshot правила,
распределение Platform/Agency/Agent (строки CommissionDistributionEntity этим контрактом не
публикуются — по получению денег они входят в
commission-money-received.md).
Когда публикуется
Каждый переход Status по stateDiagram commission-accrual.md:
| Условие | Переход состояния | Согласованность |
|---|---|---|
| Менеджер застройщика подтвердил начисление (../api/admin/confirm-commission-accrual.md; опционально — с корректировкой суммы, INV-11; строится распределение тремя строками, INV-4/5/9) | PendingConfirmation → Confirmed | Начисление, строки распределения и факт публикации — в одном согласованном изменении |
| Менеджер застройщика отказал (../api/admin/reject-commission-accrual.md; обязательный комментарий) | PendingConfirmation → Cancelled (CancelReason = DeveloperRejected) | Начисление и факт публикации — в одном согласованном изменении |
Consumer pin.crm.deal-cancelled.v1 отменил начисление (crm-deal-cancelled.md) | `PendingConfirmation | Confirmed → Cancelled (CancelReason = DealCancelled`; guard INV-8 — начисление не в поручении и не в не-отменённом акте) |
Ручная корректировка (admin-API, permission rewards.accruals.manage) | `PendingConfirmation | Confirmed → Cancelled (CancelReason = ManualCorrection`; guard INV-8) |
| Оператор ПИН зарегистрировал акт поступления средств (platform-receipt.md, атомарный каскад INV-RC-4) — по событию на каждое начисление акта | Confirmed → MoneyReceived | Акт, переходы всех позиций, авто-Approve связанных реферальных начислений, публикация этого события и commission-money-received — в одном согласованном изменении |
Каскад выплаты платёжного поручения (payout-order.md, INV-7): все Agent-строки начисления выплачены | MoneyReceived → Paid | Поручение, позиции, строки распределения, начисления и факт публикации — в одном согласованном изменении |
Создание начисления этим событием не публикуется — см. commission-accrual-created.md.
Потребители (consumers)
| Потребитель | Эффект (событие → действие) | Идемпотентность | Задержка (SLA) |
|---|---|---|---|
notifications | Уведомления по ToStatus: Confirmed — агенту и руководителю агентства «комиссия подтверждена застройщиком»; MoneyReceived — no-op (уведомление о получении денег отправляется по commission-money-received.md, дубль исключён); Paid — агенту «комиссия выплачена»; Cancelled — агенту и руководителю с причиной (CancelReason маппится в человекочитаемый текст) | MessageId; порядок — key = Id + Version-guard | < 5 мин |
rewards (self, проекция AgentPerformance) | Наполнение user-centric проекции AgentPerformance (ADR-0061 п.5): Paid → CommissionPaidTotal; Cancelled(DealCancelled) → реверс accrued + DealsLostCount; MoneyReceived — no-op (метрики проекции получение денег не меняет, ../consumers/agent-performance-projection.md) | MessageId + факт Id:{ToStatus} | < 5 мин |
Гарантии доставки и порядка
- Доставка — at-least-once; потребитель дедуплицирует по
MessageId(+ бизнес-ключId:{ToStatus}для self-проекции), эффекты не удваиваются. - Порядок внутри топика — строгий per-accrual (key =
Id): переходы одного начисления (Confirmed→MoneyReceived→Paid, либо→ Cancelled) приходят в причинном порядке; дополнительный monotonic-guard — поVersion(out-of-order переход отбрасывается как устаревший). - Между топиками порядок не гарантирован. С
commission-accrual-created.v1— разные топики: переход причинно следует за созданием, но может прийти раньше. Self-проекцияAgentPerformanceприменяет реверсCancelled(DealCancelled)идемпотентно, с floor 0 на счётчиках — временное опережение не даёт отрицательных значений, итог сходится (см. agent-performance.md). - Регистрация акта (два топика). Переход
Confirmed → MoneyReceivedпубликуется атомарно вместе с commission-money-received (и событиями авто-апрува рефералок топикаpin.rewards.referral-accruals.v1); сквозного порядка между топиками нет — кредиты кошелька идут только поcommission-money-received, уведомление о получении денег — тоже (здесьMoneyReceived— no-op у обоих потребителей). - Каскад выплаты.
MoneyReceived → Paidпорождается каскадом поручения (payout-order.md, INV-7) и публикуется атомарно вместе сpayout-order-status-changed.v1; сквозного порядка между этими двумя топиками нет — оба потребителя (notifications) идемпотентны поMessageId; дебет кошелька идёт только по топику поручений.
Версионирование и совместимость
ContractVersion = 1; несовпадение версии контракта в сообщении — warn-and-proceed.- Additive-only без bump (новые optional-поля, новые значения enum).
- Breaking —
ContractVersion = 2+ топикpin.rewards.commission-accrual-status-changed.v2+ dual-publish + deadline миграции + sunset. - Unknown enum
FromStatus/ToStatus/CancelReasonу потребителя — уведомление не отправляется, warning + метрика (fail-safe: лучше пропустить уведомление, чем отправить неверное). - Deprecation — по общему sunset-правилу событийного реестра.
Безопасность
| Поле | Класс данных | Правило |
|---|---|---|
Id, TenantId, DealId, AgentUserId, FromStatus, ToStatus, ConfirmedAt, MoneyReceivedAt, PaidAt, Version | Internal | include |
CommissionAmount | Sensitive (суммы комиссий) | include (внутренний топик, m2m ACL Kafka); в логи потребителей не пишется; в уведомлении видна только адресату |
CancelReason | Internal | include; текст комментария отказа застройщика в payload не входит (остаётся в rewards, выдаётся API по permission) |
Событие НЕ содержит secrets, PII, платёжные реквизиты, распределение долей, unbounded-вложения.
Обратные ссылки
Автоссылки: где используется этот документ.
- Домен (CQRS) (2): Entity / Aggregate: CommissionAccrualEntity, Entity / Aggregate: PlatformReceiptEntity
- API (2): POST /api/v1/agency/payout-orders/{orderId}/mark-paid — Ручная отметка о выплате, POST /api/v1/platform/receipts/{receiptId}/register — Регистрация акта поступления средств
- Use Cases (1): US-реестр: Финансовый блок ПИН (US-FIN)
- События (4): Event Contract: pin.rewards.commission-accrual-created, Event Contract: pin.rewards.commission-money-received (CommissionMoneyReceivedKafkaEvent), Event Contract: pin.rewards.payout-order-status-changed (PayoutOrderStatusChangedKafkaEvent), Event Contract: pin.rewards.referral-accrual-status-changed (ReferralAccrualStatusChangedKafkaEvent)
- Консюмеры (3): Consumer: Agent Performance Projection, Consumer: CRM Deal Cancelled, Consumer: Rewards Events → Notifications (notifications-rewards-events)
- Саги (1): Saga «Commission Settlement» (финансовый контур вознаграждений)
- registries (1): Реестр событий PIN
- index.md (1): Rewards Service
Связанные документы
- Сущность: commission-accrual.md (stateDiagram, INV-3/7/8/11).
- Гейт получения денег: platform-receipt.md (каскад регистрации INV-RC-4) и commission-money-received.md.
- Каскад выплаты: payout-order.md (INV-7) и payout-order-status-changed.md.
- Создание: commission-accrual-created.md; отмена по сделке — consumer crm-deal-cancelled.md.
- Snapshot-модели валюты: ../../handbook/domain/snapshots.md.