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

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 typepin.rewards.commission-accrual-status-changed
C# typeCommissionAccrualStatusChangedKafkaEvent
Topicpin.rewards.commission-accrual-status-changed.v1
Partitions / key3 / key = Id начисления — строгий порядок переходов per-accrual
ContractVersion1
Producerrewards: команды 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Описание
IdGuidДаCommissionAccrualEntity.IdImmutableКлюч агрегата (= Kafka key).
TenantIdGuidДаCommissionAccrualEntity.TenantIdImmutableАгентство-получатель (tenant boundary).
DealIdGuidДаCommissionAccrualEntity.DealIdImmutableСделка crm — контекст уведомления.
AgentUserIdGuidДаCommissionAccrualEntity.AgentUserIdImmutableАгент — адресат уведомления.
FromStatusstring (enum CommissionAccrualStatus)Дастатус до переходаAdditive only (новые значения)PendingConfirmation / Confirmed / MoneyReceived.
ToStatusstring (enum CommissionAccrualStatus)ДаCommissionAccrualEntity.Status после переходаAdditive onlyConfirmed / MoneyReceived / Paid / Cancelled.
CancelReasonstring? (enum CommissionCancelReason)Да при ToStatus = Cancelled, иначе nullCommissionAccrualEntity.CancelReasonAdditive onlyDealCancelled / DeveloperRejected / ManualCorrection.
CommissionAmountMoneyModelДаCommissionAccrualEntity.CommissionAmountAdditive onlyСумма комиссии (после корректировок, если были — INV-11) — текст уведомления.
ConfirmedAtDateTimeOffset?Да при ToStatus = Confirmed, иначе может быть nullCommissionAccrualEntity.ConfirmedAtAdditive onlyМомент подтверждения застройщиком.
MoneyReceivedAtDateTimeOffset?Да при ToStatus = MoneyReceived, иначе может быть nullCommissionAccrualEntity.MoneyReceivedAtAdditive onlyМомент подтверждения получения денег (= регистрация акта, platform-receipt.md).
PaidAtDateTimeOffset?Да при ToStatus = Paid, иначе nullCommissionAccrualEntity.PaidAtAdditive onlyМомент выплаты (по платёжному поручению).
VersionlongДаCommissionAccrualEntity.Version после переходаImmutable semanticsДоменная версия — монотонный guard потребителя против out-of-order.

Вложенная модель MoneyModel

Проекция VO Money (commission-accrual.md, раздел «Value objects»).

ПолеТипОбязательностьОграниченияОписание
ValuedecimalДа>= 0, scale 2Сумма.
CurrencyIdshortДассылка на валюту справочника; RUB-only (ADR-0048)Типизированная ссылка на валюту.
CurrencySnapshotCurrencySnapshotДа../../handbook/domain/snapshots.mdDisplay-снапшот валюты на момент публикации.

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)`PendingConfirmationConfirmed → Cancelled (CancelReason = DealCancelled`; guard INV-8 — начисление не в поручении и не в не-отменённом акте)
Ручная корректировка (admin-API, permission rewards.accruals.manage)`PendingConfirmationConfirmed → 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 — агенту и руководителю агентства «комиссия подтверждена застройщиком»; MoneyReceivedno-op (уведомление о получении денег отправляется по commission-money-received.md, дубль исключён); Paid — агенту «комиссия выплачена»; Cancelled — агенту и руководителю с причиной (CancelReason маппится в человекочитаемый текст)MessageId; порядок — key = Id + Version-guard< 5 мин
rewards (self, проекция AgentPerformance)Наполнение user-centric проекции AgentPerformance (ADR-0061 п.5): PaidCommissionPaidTotal; Cancelled(DealCancelled) → реверс accrued + DealsLostCount; MoneyReceivedno-op (метрики проекции получение денег не меняет, ../consumers/agent-performance-projection.md)MessageId + факт Id:{ToStatus}< 5 мин

Гарантии доставки и порядка

  • Доставка — at-least-once; потребитель дедуплицирует по MessageId (+ бизнес-ключ Id:{ToStatus} для self-проекции), эффекты не удваиваются.
  • Порядок внутри топика — строгий per-accrual (key = Id): переходы одного начисления (ConfirmedMoneyReceivedPaid, либо → 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, VersionInternalinclude
CommissionAmountSensitive (суммы комиссий)include (внутренний топик, m2m ACL Kafka); в логи потребителей не пишется; в уведомлении видна только адресату
CancelReasonInternalinclude; текст комментария отказа застройщика в payload не входит (остаётся в rewards, выдаётся API по permission)

Событие НЕ содержит secrets, PII, платёжные реквизиты, распределение долей, unbounded-вложения.

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

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

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