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

UC-CRM-007. Добавление участника в сделку

Назначение

Описывает сценарий, в котором агентская сторона (агент-исполнитель, руководитель агентства-владельца) или Менеджер ПИН подключает к сделке дополнительного участника — со-агента, ассистента, юриста сопровождения или соинвестора. Операция расширяет состав сделки, не меняя исполнителя (получателя комиссии) и основного инвестора: новый участник получает доступ к сделке по членству и приглашается в чат сделки. Агрегат — DealEntity; дом полей состава и инвариантов — DealMemberEntity, ParticipantRole, INV-DEAL-3. Связан с контекстом crm.

Metadata

ПолеЗначение
IDUC-CRM-007
Контекстcrm (см. docs/06-services/crm/)
Типосновной (+ альтернативные потоки A1–A4)
Статусdraft

Актор

ПолеЗначение
РольАгент (активный участник сделки), Руководитель/администратор агентства-владельца, Менеджер ПИН
Permissions / scopecrm.deals.manage — организация (агентство-владелец); Менеджер ПИН — platform-scope (кросс-tenant, ADR-0052)
TenantСделка TenantId = DeveloperCompanyId (владелец — застройщик, ADR-0052); агентская сторона идёт по членству, а не по авто-tenant-фильтру; Менеджер ПИН — null (сквозной доступ)
Record-levelдоступ = актор ∈ активные участники сделки (любая роль ParticipantRole) ИЛИ руководитель/администратор компании-владельца (агентство активного Executor по scope-иерархии ADR-0025; застройщик — свой TenantId) ИЛИ Менеджер ПИН (обход record-level)

Предусловие

  • Актор аутентифицирован и несёт crm.deals.manage (агентская сторона) либо platform-scope Менеджера ПИН.
  • Целевая сделка существует (DealEntity) и доступна актору по членству/руководству/ПИН.
  • Сделка нетерминальна (Status ∉ {Completed, Cancelled}, INV-DEAL-9).
  • Добавляемый UserId существует в реплике identity и ещё не является активным участником сделки.
  • Добавляемая роль — одна из подключаемых через агентский эндпоинт: CoAgent / Assistant / Lawyer / InvestorSecondary.

Триггер

ПолеЗначение
ИсточникUI-действие / вызов API
Триггерящий элементКарточка сделки, блок «Участники» → «Добавить участника»; POST /api/agent/crm/deals/{dealId}/members (add-deal-participant.md)

Основной Поток

  1. Актор открывает карточку сделки и в блоке «Участники» выбирает «Добавить участника»: указывает пользователя (UserId) и его роль в сделке (CoAgent / Assistant / Lawyer / InvestorSecondary).
  2. Интерфейс отправляет запрос на добавление участника с телом { UserId, Role } в маршруте сделки.
  3. Система определяет контекст и доступ актора: активный участник сделки, руководитель компании-владельца или Менеджер ПИН.
  4. Система проверяет вход и предусловия: формат идентификаторов; роль входит в набор, допустимый для добавления агентом; сделка существует, доступна и нетерминальна; пользователь есть в реплике identity и не является активным участником сделки (уникальность UserId на сделку).
  5. Система применяет бизнес-правило состава (INV-DEAL-3): роли CoAgent/Assistant/Lawyer/InvestorSecondary допускают произвольное число активных участников — singleton-инварианты Executor/InvestorPrimary не затрагиваются; денормализованные скаляры сделки (ExecutorAgentUserId/PrimaryInvestorUserId) не меняются.
  6. Система заводит нового активного участника (участие с RemovedAt = null, отметкой «кем добавлен» и моментом добавления), инкрементирует версию сделки и, при желании, пишет строку таймлайна «участник добавлен» — в одном согласованном изменении.
  7. Система фиксирует факт: публикует deal-members-changed.md (pin.crm.deal-members-changed.v1, key DealId) с дельтой (ChangeType = Added) и полным активным составом после изменения.
  8. Система возвращает карточку добавленного участника (HTTP 201); интерфейс обновляет список участников сделки.

Альтернативные Потоки

A1. Добавляется соинвестор (InvestorSecondary)

  1. Актор добавляет второго/третьего инвестора ролью InvestorSecondary (созаёмщик/соинвестор).
  2. Singleton-инвариант основного инвестора (InvestorPrimary, ровно один) не нарушается — число InvestorSecondary не ограничено.
Возврат к основному потоку

Возвращается к основному потоку на шаг 6.

A2. Недопустимая для агента роль

  1. Актор указывает роль Executor / Responsible / InvestorPrimary (смена исполнителя, назначение куратора, смена основного инвестора) либо SellManager / PinManager (стороны застройщика/ПИН).
  2. Система отклоняет запрос CRM_PARTICIPANT_ROLE_FORBIDDEN (422): эти роли назначаются иными операциями — передача ведения (UC-CRM-009), смена роли/основного инвестора, либо системно.
Роль недопустима для агентского добавления

Завершается ошибкой; состав не меняется.

A3. Пользователь уже участвует в сделке

  1. Указанный UserId уже является активным участником сделки.
  2. Система отклоняет запрос CRM_DUPLICATE_PARTICIPANT (409): один и тот же пользователь не дублируется в составе (уникальность UserId на сделку, INV-DEAL-3).
Дубликат участника

Завершается ошибкой; повторного участия не создаётся.

A4. Сделка недоступна актору

  1. Актор не участник сделки и не руководитель/ПИН.
  2. Система отвечает CRM_DEAL_NOT_FOUND (404) — существование чужих сделок не раскрывается.
Сделка недоступна актору

Завершается ошибкой.

Постусловие

  • В состав сделки добавлено новое активное участие (RemovedAt = null) с ролью CoAgent/Assistant/Lawyer/InvestorSecondary; версия сделки инкрементирована; денормализованные скаляры исполнителя/основного инвестора не изменены.
  • Опубликован DealMembersChangedEvent (ChangeType = Added, полный активный состав).
  • Окно eventual-consistency: участник виден в сделке сразу; в чате сделки (messenger) и в уведомлениях (notifications) появляется с лагом консюмеров (< 1 мин).
  • При отказе — ни участия, ни изменения версии, ни события.

Возможные Ошибки

КодHTTPУсловиеПоведение клиента
VALIDATION_FAILED400DealId/UserId = Guid.Empty или роль вне enum ParticipantRoleПодсветить поля формы
CRM_PARTICIPANT_ROLE_FORBIDDEN422роль не добавляется через агента (Executor/Responsible/InvestorPrimary/SellManager/PinManager)Показать «эта роль назначается другим способом»
CRM_DEAL_NOT_FOUND404сделки нет или она недоступна акторуОбновить список сделок
CRM_DEAL_ALREADY_TERMINAL409сделка Completed/Cancelled (INV-DEAL-9)Показать «сделка завершена и не редактируется»
CRM_USER_NOT_FOUND404UserId не найден в реплике identityПоказать «пользователь не найден»
CRM_DUPLICATE_PARTICIPANT409UserId уже активный участник (INV-DEAL-3)Показать «пользователь уже в сделке»

Доменные События

СобытиеКогда поднимаетсяSubscribers
DealMemberAddedДобавление активного участникаподписчик таймлайна (MemberAdded), сборщик DealMembersChangedEvent

Kafka Интеграционные События

СобытиеTopicКогда публикуетсяConsumers
DealMembersChangedEventpin.crm.deal-members-changed.v1Добавление участника — контрактmessenger (приглашение в чат сделки, синк состава), notifications (уведомление добавленному участнику)

Acceptance Criteria

#КритерийПроверка
AC1Добавление CoAgent в нетерминальную доступную сделку → 201, новое активное участие, версия сделки инкрементирована, событие deal-members-changed (Added, полный состав)Тест операции + проверка outbox
AC2Добавление InvestorSecondary при существующем InvestorPrimary201; singleton-инвариант основного инвестора не нарушенТест: несколько InvestorSecondary без конфликта
AC3Роль Executor/InvestorPrimary/SellManager/PinManager422 CRM_PARTICIPANT_ROLE_FORBIDDEN, состав не меняетсяGuard-тест роли
AC4Повторное добавление того же UserId409 CRM_DUPLICATE_PARTICIPANTТест уникальности участника
AC5Добавление в терминальную сделку → 409 CRM_DEAL_ALREADY_TERMINALGuard-тест INV-DEAL-9
AC6Актор не участник/руководитель/ПИН → 404 CRM_DEAL_NOT_FOUND (существование не раскрывается)Record-level тест
AC7Через < 1 мин после успеха участник появляется в чате сделки и получает уведомлениеDownstream (eventual): проверка синка messenger/notifications

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

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

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