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

UC-CRM-017. Flow B: агент предлагает объект → Draft-сделка → подтверждение инвестором → вовлечение застройщика

Назначение

Сквозной сценарий Flow B: агент предлагает инвестору объект, которого тот сам не запрашивал, заводит по нему сделку в статусе Draft (агент + инвестор, застройщик ещё не вовлечён), затем инвестор подтверждает намерение, а после одобренной фиксации для пары «инвестор × застройщик» агент вовлекает застройщика — сделка переходит Draft → Application. Объединяет четыре шага (предложение объекта, создание Draft, подтверждение инвестором, вовлечение застройщика) и показывает гейт INV-DEAL-11. Дома полей — lead (намерения) и deal (сделка, гейт).

Metadata

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

Актор

ПолеЗначение
РольАгент-исполнитель лида (Executor) — предлагает объект, создаёт Draft, вовлекает застройщика; Инвестор (InvestorPrimary) — подтверждает намерение
Permissions / scopeАгент: crm.leads.manage + crm.deals.manage, scope — организация (агентство), record-level по членству (ADR-0071); Инвестор: crm.leads.participate, кросс-tenant участник (ADR-0052)
TenantЛид/намерение TenantId = AgencyCompanyId; создаваемая сделка TenantId = DeveloperCompanyId объекта (set-once, immutable — INV-DEAL-1)
Record-levelАгент — активный участник лида/сделки (или руководитель/Менеджер ПИН); инвестор — lead.PrimaryInvestorUserId = actor

Предусловие

  • Лид существует и Status = Active (INV-INTENT-2 / INV-LEAD-2).
  • Объект существует в chessboard, доступен («Свободен»), принадлежит застройщику DeveloperCompanyId.
  • По паре (LeadId, PropertyObjectId) нет активного намерения (INV-INTENT-1).
  • Для вовлечения застройщика позже потребуется одобренная фиксация для пары «инвестор × застройщик» (fixation-request); если её нет — агент отправляет заявку на фиксацию (UC-FIX-001) и ждёт одобрения (UC-FIX-002).

Триггер

ПолеЗначение
ИсточникUI-действия агента и инвестора → вызовы API
Триггерящий элементАгент: «Предложить объект» → POST /api/agent/crm/leads/{leadId}/booking-intents, «Создать сделку» → POST /api/agent/crm/deals, «Вовлечь застройщика» → POST /api/agent/crm/deals/{dealId}/engage-developer; Инвестор: «Подтвердить» → POST /api/investor/crm/leads/{leadId}/booking-intents/{intentId}/confirm

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

  1. Агент предлагает объект. Агент в активном лиде предлагает инвестору объект: заводится намерение Source = AgentProposed, ConfirmedByInvestor = false, Status = Active, снапшоты ProjectId/DeveloperCompanyId из chessboard. В ленту лида — BookingIntentAdded (ConfirmedByInvestor = false); публикуется pin.crm.lead-booking-intent-changed.v1 (ChangeKind = Added); notifications уведомляет инвестора «агент предложил объект».
  2. Агент создаёт Draft-сделку. По предложенному объекту агент создаёт сделку. Гейт вовлечения застройщика не выполнен (намерение не подтверждено и/или нет одобренной фиксации) → сделка заводится в Draft: TenantId = DeveloperCompanyId объекта (set-once), состав Executor + InvestorPrimary + авто-PinManager (без SellManager, INV-DEAL-2), IntentId = намерение, IntentConfirmedAt = null, PaymentMethod необязателен (INV-DEAL-1). Сделка скрыта от застройщика (developer-facing проекции фильтруют Status != Draft). Публикуется pin.crm.deal-created.v1 (Status = Draft) → messenger создаёт чат агент+инвестор, notifications уведомляет участников; developer-facing консюмеры не реагируют.
  3. Инвестор подтверждает намерение. Инвестор соглашается работать по объекту: намерение переходит ConfirmedByInvestor: false → true, ConfirmedAt = now (INV-INTENT-4). В ленту лида — BookingIntentConfirmed; публикуется pin.crm.lead-booking-intent-changed.v1 (ChangeKind = Confirmed). Подтверждение само по себе сделку не переводит — снимает лишь одно из двух условий гейта.
  4. Фиксация. Для пары «инвестор × застройщик» должна быть одобренная фиксация. Если её ещё нет — агент отправляет заявку (UC-FIX-001), застройщик одобряет (UC-FIX-002). Одобрение фиксации авторизует работу по объектам застройщика, но само по себе сделку в Application не переводит.
  5. Агент вовлекает застройщика (гейт INV-DEAL-11). Агент инициирует переход Draft → Application. Гейт проверяет одновременно:
    • связанное намерение подтверждено инвестором (ConfirmedByInvestor = true) — иначе CRM_DEAL_INTENT_NOT_CONFIRMED;
    • для пары «инвестор × застройщик» есть одобренная фиксация — иначе CRM_DEAL_FIXATION_REQUIRED;
    • задан PaymentMethod — иначе CRM_DEAL_PAYMENT_METHOD_REQUIRED.
  6. Доменное решение. При выполнении гейта: Status = Application, в состав добавляется SellManager (INV-DEAL-2), проставляется IntentConfirmedAt (snapshot ConfirmedAt намерения). Обратного перехода Application → Draft нет.
  7. Что меняется. Статус сделки, состав участников (+SellManager), IntentConfirmedAt, таймлайн сделки, версия — в одном согласованном изменении вместе с записью в outbox.
  8. Публикация. pin.crm.deal-stage-changed.v1 (Draft → Application) и pin.crm.deal-members-changed.v1 (появился SellManager, полный активный состав). С этого момента сделка видна застройщику и попадает на его канбан; developer-facing консюмеры реагируют начиная с Application.
  9. Результат. Сделка Application с застройщиком в составе; на канбане /crm застройщика появляется карточка. Дальше — стандартный жизненный цикл сделки (бронь, продажа).

Диаграмма

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

A1. Инвестор не подтверждает — вовлечение блокируется
  1. Намерение остаётся ConfirmedByInvestor = false. Агент пытается вовлечь застройщика.
  2. CRM_DEAL_INTENT_NOT_CONFIRMED (409); сделка остаётся Draft.

Возврат: дождаться подтверждения инвестором (шаг 3 / .../confirm).

A2. Нет одобренной фиксации
  1. Намерение подтверждено, но для пары «инвестор × застройщик» нет одобренной фиксации.
  2. CRM_DEAL_FIXATION_REQUIRED (409); сделка остаётся Draft.

Возврат: отправить заявку на фиксацию (UC-FIX-001) и дождаться одобрения.

A3. Способ оплаты не задан при вовлечении
  1. Гейт по намерению и фиксации выполнен, но PaymentMethod пуст (при Draft он необязателен).
  2. CRM_DEAL_PAYMENT_METHOD_REQUIRED (422).

Возврат: агент указывает способ оплаты (и пресет для рассрочки) и повторяет вовлечение.

A4. Инвестор отказался от объекта
  1. Инвестор снимает намерение (remove-booking-intent): Status = Cancelled, в ленту — BookingIntentRemoved.
  2. Агент отменяет связанную Draft-сделку (Cancelled, CancelReason); застройщик о ней не знал.
A5. Лид неактивен
  1. Лид Expired/Archived на любом шаге.
  2. CRM_LEAD_NOT_ACTIVE (409). Возврат: продлить/реактивировать лид (INV-LEAD-3).

Постусловие

  • После шага 2: сделка Draft (TenantId = DeveloperCompanyId, состав без SellManager, IntentId заполнен, IntentConfirmedAt = null); невидима застройщику; опубликован pin.crm.deal-created.v1 (Status = Draft).
  • После шага 3: намерение ConfirmedByInvestor = true, ConfirmedAt заполнен; в ленте BookingIntentConfirmed.
  • После шага 6 (гейт выполнен): сделка Application, в составе SellManager, IntentConfirmedAt проставлен; опубликованы pin.crm.deal-stage-changed.v1 (Draft → Application) и pin.crm.deal-members-changed.v1; сделка видна застройщику. Обратного перехода в Draft нет.
  • Origin-атрибуция сделки (OriginAgentUserId/OriginAgencyCompanyId) — из лида, иммутабельна (INV-DEAL-10).

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

КодHTTPУсловиеПоведение клиента
CRM_DEAL_INTENT_NOT_CONFIRMED409Вовлечение застройщика при ConfirmedByInvestor = false связанного намерения (INV-DEAL-11)Дождаться подтверждения инвестором
CRM_DEAL_FIXATION_REQUIRED409Нет одобренной фиксации для пары «инвестор × застройщик» (INV-DEAL-11)Отправить заявку на фиксацию и дождаться одобрения
CRM_DEAL_PAYMENT_METHOD_REQUIRED422При вовлечении PaymentMethod не задан (для Application, INV-DEAL-1)Форма требует выбрать способ оплаты (и пресет рассрочки)
CRM_LEAD_NOT_ACTIVE409Лид Expired/Archived (INV-INTENT-2 / INV-LEAD-2)Предложить продлить/реактивировать лид
CRM_INTENT_CONFIRM_FORBIDDEN403Подтверждение намерения не инвестором пары (INV-INTENT-4)Скрыть действие подтверждения
CRM_DEAL_ALREADY_TERMINAL409Вовлечение застройщика для отменённой Draft-сделки (INV-DEAL-9)Сообщить, что сделка прекращена

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

СобытиеКогда поднимаетсяSubscribers
LeadBookingIntentChangedПредложение (Added) и подтверждение (Confirmed) намеренияЗапись LeadEventEntity(BookingIntentAdded/Confirmed), сборка интеграционного события
DealCreated / DealMemberAddedСоздание Draft-сделки и авто-состав без SellManagerТаймлайн сделки, событие в ленту лида DealCreated
DealStatusChanged / DealMemberAddedГейт Draft → Application: смена статуса и добавление SellManagerТаймлайн сделки, сборка DealStageChanged/DealMembersChanged

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

СобытиеTopicКогда публикуетсяConsumers
LeadBookingIntentChangedEventpin.crm.lead-booking-intent-changed.v1Предложение (Added) и подтверждение (Confirmed) намеренияnotifications (инвестору — предложение, исполнителю — подтверждение), messenger (по необходимости)
DealCreatedEventpin.crm.deal-created.v1Создание Draft-сделки (Status = Draft, без SellManager)messenger (чат агент+инвестор), notifications; developer-facing консюмеры фильтруют Status != Draft — контракт deal-created
DealStageChangedEventpin.crm.deal-stage-changed.v1Гейт Draft → Application (вовлечение застройщика)messenger, notifications; момент, с которого сделка видима застройщику — контракт deal-stage-changed
DealMembersChangedEventpin.crm.deal-members-changed.v1Добавление SellManager на гейтеmessenger (синк участников чата), notifications — контракт deal-members-changed

Acceptance Criteria

#КритерийПроверка
AC1Агент предлагает объект → намерение AgentProposed, ConfirmedByInvestor = false, в ленте BookingIntentAddedТест POST /api/agent/crm/leads/{id}/booking-intents
AC2Агент создаёт сделку по предложенному объекту (гейт не выполнен) → сделка Draft, состав без SellManager, невидима застройщикуТест POST /api/agent/crm/dealsStatus = Draft; developer-канбан не содержит сделку
AC3Инвестор подтверждает намерение → ConfirmedByInvestor = true, ConfirmedAt заполнен, в ленте BookingIntentConfirmedТест .../booking-intents/{intentId}/confirm
AC4Вовлечение при неподтверждённом намерении → CRM_DEAL_INTENT_NOT_CONFIRMED, статус остаётся DraftНегативный тест INV-DEAL-11
AC5Вовлечение без одобренной фиксации → CRM_DEAL_FIXATION_REQUIRED, статус остаётся DraftНегативный тест INV-DEAL-11
AC6Гейт выполнен (подтверждено + фиксация + PaymentMethod) → сделка Application, +SellManager, IntentConfirmedAt проставлен, опубликованы deal-stage-changed/deal-members-changed, сделка видна застройщикуИнтеграционный тест .../engage-developer
AC7Подтверждение намерения не инвестором пары → CRM_DEAL_INTENT_NOT_CONFIRMED-путь не пройден: CRM_INTENT_CONFIRM_FORBIDDEN на подтвержденииНегативный тест INV-INTENT-4

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

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

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