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

UC-CRM-011. Лид с несколькими инвесторами: назначение основного инвестора

Назначение

Агент назначает основным инвестором (InvestorPrimary) одного из участников лида — когда лид ведут несколько инвесторов (созаёмщики/соинвесторы — семейная пара, партнёры по покупке), нужен ровно один «главный». Основной инвестор ровно один (INV-LEAD-7): он определяет уникальность активной пары «исполнитель ↔ основной инвестор» (INV-CRM-001) и является «шапкой» лида. Операция «поднимает» выбранного соинвестора (InvestorSecondary) в InvestorPrimary, а прежний основной становится InvestorSecondary; производная лида PrimaryInvestorUserId пересчитывается в том же изменении. Связан с контекстом crm. Дом полей — lead (раздел «Участники лида»). Добавление/удаление самих соинвесторов — UC-CRM-012.

Metadata

ПолеЗначение
IDUC-CRM-011
Контекстcrm (см. docs/06-services/crm/)
Типосновной
Статусdraft

Актор

ПолеЗначение
РольАгент-исполнитель / куратор лида (агентская роль в составе) / администратор-руководитель агентства / Менеджер ПИН
Permissions / scopecrm.leads.manage; scope — организация (агентство), record-level по членству (ADR-0071); руководитель — весь TenantId (ADR-0025); Менеджер ПИН — платформа
TenantTenantId = AgencyCompanyId; lead.TenantId обязан совпасть
Record-levelдоступ = актор ∈ активные участники лида агентской роли (Executor/Responsible) или руководитель компании-владельца или Менеджер ПИН

Предусловие

  • Лид существует в TenantId актора и Status = Active (INV-LEAD-2).
  • Целевой UserId — активный инвестор-участник лида (InvestorPrimary или InvestorSecondary); если он ещё не в составе — сначала добавить его через UC-CRM-012.
  • Актор имеет доступ по членству/руководству/платформе (record-level, ADR-0071).
  • У пары (текущий исполнитель, новый основной) нет другого активного лида (INV-CRM-001).

Триггер

ПолеЗначение
ИсточникUI-действие → вызов API
Триггерящий элементДействие «Сделать основным» у соинвестора в составе лида → POST /api/agent/crm/leads/{leadId}/primary-investor

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

  1. Контекст и доступ. Агент открывает карточку активного лида, где в составе несколько инвесторов (InvestorPrimary + один или несколько InvestorSecondary), и выбирает нового основного.
  2. Ввод. Запрос несёт UserId — целевого инвестора-участника.
  3. Проверка входа. LeadId и UserId заданы (не пусто) — иначе VALIDATION_FAILED.
  4. Проверки состояния, доступа и состава.
    • Лид существует в TenantId — иначе CRM_LEAD_NOT_FOUND.
    • Status = Active (INV-LEAD-2) — иначе CRM_LEAD_NOT_ACTIVE.
    • Record-level (членство/руководство/Менеджер ПИН) — иначе CRM_LEAD_FORBIDDEN.
    • UserId — активный инвестор-участник лида (InvestorPrimary/InvestorSecondary) — иначе CRM_LEAD_PARTICIPANT_NOT_FOUND (не инвестор/не участник → сначала добавить).
  5. Идемпотентность цели. Если UserId уже активный InvestorPrimary — no-op: состав и производные не меняются, событие не публикуется, возвращается текущий состав.
  6. Guard активной пары (INV-CRM-001). Смена основного меняет пару (ExecutorAgentUserId, новый PrimaryInvestorUserId). Если у этой пары уже есть другой активный лид — CRM_LEAD_ACTIVE_PAIR_EXISTS (нельзя задвоить активную воронку пары).
  7. Доменное решение (INV-LEAD-7; ровно один InvestorPrimary). Атомарно: прежний активный InvestorPrimary → роль InvestorSecondary (IsPrimary = false); целевой участник → InvestorPrimary (IsPrimary = true). Пересчитывается производная PrimaryInvestorUserId = UserId. Исполнитель (ExecutorAgentUserId), куратор (ResponsibleAgentUserId) и агентский состав не меняются; статус лида Active → Active; лид ни на миг не остаётся без основного инвестора.
  8. Что меняется. Роли двух инвесторских участий, производная PrimaryInvestorUserId, версия лида — в одном согласованном изменении вместе с записью в outbox. Членство инвесторов в лиде не меняется (менялась только роль).
  9. Публикация. Интеграционное событие pin.crm.lead-participants-changed.v1 (ChangeType = RoleChanged для обоих инвесторов) с полным активным составом и дельтой.
  10. Результат. Возвращается актуальный состав лида с обновлённым основным (PrimaryInvestorUserId = UserId). «Шапка» лида в списках и карточке обновляется сразу.
  11. Асинхронно. messenger реконсилирует состав чата лида (членство инвесторов не меняется — только их роль); notifications уведомляет о смене основного клиента.

Диаграмма

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

A1. Идемпотентный повтор (цель уже основная)
  1. UserId уже активный InvestorPrimary.
  2. Состав и производные не меняются, событие не публикуется; возвращается текущий состав 200.
A2. Целевой пользователь — не инвестор / не участник
  1. UserId не входит в активных инвесторов лида (агент, посторонний либо снятый участник).
  2. CRM_LEAD_PARTICIPANT_NOT_FOUND (404).

Возврат: сначала добавить инвестора в состав (UC-CRM-012), затем повторить.

A3. Конфликт активной пары (INV-CRM-001)
  1. У пары (текущий исполнитель, новый основной) уже есть другой активный лид.
  2. CRM_LEAD_ACTIVE_PAIR_EXISTS (409); состав не меняется.

Возврат: работать с существующим активным лидом пары либо выбрать иного основного.

A4. Лид неактивен
  1. Лид Expired/Archived.
  2. CRM_LEAD_NOT_ACTIVE (409); менять состав нельзя.

Возврат: продлить/реактивировать лид (INV-LEAD-3), затем повторить.

A5. Конкурентная смена состава
  1. Параллельная смена состава/передача ведения изменила версию лида.
  2. CRM_LEAD_CONCURRENCY (409).

Возврат: перечитать лид и повторить.

Постусловие

  • Целевой участник — InvestorPrimary (IsPrimary = true); прежний основной — InvestorSecondary (IsPrimary = false); среди активных ровно один InvestorPrimary (INV-LEAD-7).
  • Производная PrimaryInvestorUserId = UserId; исполнитель и куратор не изменены; версия лида +1; статус остаётся Active.
  • Членство инвесторов в лиде не менялось (менялась только роль); заявки на фиксацию и сделки лида не изменены; Origin-атрибуция не тронута.
  • Опубликован pin.crm.lead-participants-changed.v1 (RoleChanged) с полным активным составом — кроме no-op.

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

КодHTTPУсловиеПоведение клиента
VALIDATION_FAILED400LeadId/UserId пустоПоказать ошибки полей
CRM_LEAD_NOT_FOUND404Лид не найден по LeadId в tenantСообщить, что лид не найден
CRM_LEAD_PARTICIPANT_NOT_FOUND404UserId — не активный инвестор-участник лидаПредложить сначала добавить инвестора в состав
CRM_LEAD_NOT_ACTIVE409Лид Expired/Archived (INV-LEAD-2)Предложить продлить/реактивировать лид
CRM_LEAD_FORBIDDEN403Актор не участник агентской роли / не руководитель / не Менеджер ПИНСкрыть действие «сделать основным»
CRM_LEAD_ACTIVE_PAIR_EXISTS409У пары (исполнитель, новый основной) уже есть активный лид (INV-CRM-001)Сообщить, что для пары уже есть активный лид
CRM_LEAD_CONCURRENCY409Конфликт версии (параллельная смена состава/передача ведения)Перечитать и повторить
примечание
Смежная ошибка CRM_PRIMARY_INVESTOR_REQUIRED (INV-LEAD-7) — здесь не возникает

Эту ошибку операция поддерживает (всегда назначает единственного основного) — она возникает не здесь, а при попытке снять активного InvestorPrimary без замены (UC-CRM-012).

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

СобытиеКогда поднимаетсяSubscribers
LeadParticipantsChangedСмена ролей пары инвесторов (не в no-op)Внутренний диспетчер → сборка LeadParticipantsChangedEvent

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

СобытиеTopicКогда публикуетсяConsumers
LeadParticipantsChangedEventpin.crm.lead-participants-changed.v1Смена основного инвестора (ChangeType = RoleChanged), через outbox атомарноmessenger (реконсиляция состава чата лида ChatKind.Lead — членство инвесторов не меняется), notifications — контракт lead-participants-changed

Acceptance Criteria

#КритерийПроверка
AC1Лид с InvestorPrimary A и InvestorSecondary B; назначают основным B → B — InvestorPrimary, A — InvestorSecondary, PrimaryInvestorUserId = B, версия +1, событие RoleChangedТест POST .../primary-investor → 200; проверить роли и производную
AC2Ровно один активный InvestorPrimary на всём протяжении (INV-LEAD-7)Тест инварианта состава после смены
AC3Повтор с уже основным UserId → 200, состав без изменений, событие не публикуетсяТест идемпотентного no-op
AC4UserId не в составе инвесторов (агент/посторонний) → CRM_LEAD_PARTICIPANT_NOT_FOUND (404)Негативный тест
AC5У пары (исполнитель, новый основной) уже есть активный лид → CRM_LEAD_ACTIVE_PAIR_EXISTS (409), состав без измененийНегативный тест INV-CRM-001
AC6Лид Expired/ArchivedCRM_LEAD_NOT_ACTIVE; чужой актор → CRM_LEAD_FORBIDDENНегативные тесты состояния и доступа
AC7Исполнитель, куратор и членство инвесторов не изменены — менялась только рольТест: ExecutorAgentUserId/ResponsibleAgentUserId без изменений, строки участий те же

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

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

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