UC-CRM-017. Flow B: агент предлагает объект → Draft-сделка → подтверждение инвестором → вовлечение застройщика
Назначение
Сквозной сценарий Flow B: агент предлагает инвестору объект, которого тот сам не запрашивал, заводит по нему
сделку в статусе Draft (агент + инвестор, застройщик ещё не вовлечён), затем инвестор подтверждает
намерение, а после одобренной фиксации для пары «инвестор × застройщик» агент вовлекает застройщика —
сделка переходит Draft → Application. Объединяет четыре шага (предложение объекта, создание Draft,
подтверждение инвестором, вовлечение застройщика) и показывает гейт INV-DEAL-11. Дома полей —
lead (намерения) и deal (сделка, гейт).
Metadata
| Поле | Значение |
|---|---|
| ID | UC-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 |
Основной Поток
- Агент предлагает объект. Агент в активном лиде предлагает инвестору объект: заводится намерение
Source = AgentProposed,ConfirmedByInvestor = false,Status = Active, снапшотыProjectId/DeveloperCompanyIdизchessboard. В ленту лида —BookingIntentAdded(ConfirmedByInvestor = false); публикуетсяpin.crm.lead-booking-intent-changed.v1(ChangeKind = Added);notificationsуведомляет инвестора «агент предложил объект». - Агент создаёт 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 консюмеры не реагируют. - Инвестор подтверждает намерение. Инвестор соглашается работать по объекту: намерение переходит
ConfirmedByInvestor: false → true,ConfirmedAt = now(INV-INTENT-4). В ленту лида —BookingIntentConfirmed; публикуетсяpin.crm.lead-booking-intent-changed.v1(ChangeKind = Confirmed). Подтверждение само по себе сделку не переводит — снимает лишь одно из двух условий гейта. - Фиксация. Для пары «инвестор × застройщик» должна быть одобренная фиксация. Если её ещё нет — агент
отправляет заявку (UC-FIX-001), застройщик одобряет
(UC-FIX-002). Одобрение фиксации авторизует работу по
объектам застройщика, но само по себе сделку в
Applicationне переводит. - Агент вовлекает застройщика (гейт INV-DEAL-11). Агент инициирует переход
Draft → Application. Гейт проверяет одновременно:- связанное намерение подтверждено инвестором (
ConfirmedByInvestor = true) — иначеCRM_DEAL_INTENT_NOT_CONFIRMED; - для пары «инвестор × застройщик» есть одобренная фиксация — иначе
CRM_DEAL_FIXATION_REQUIRED; - задан
PaymentMethod— иначеCRM_DEAL_PAYMENT_METHOD_REQUIRED.
- связанное намерение подтверждено инвестором (
- Доменное решение. При выполнении гейта:
Status = Application, в состав добавляетсяSellManager(INV-DEAL-2), проставляетсяIntentConfirmedAt(snapshotConfirmedAtнамерения). Обратного переходаApplication → Draftнет. - Что меняется. Статус сделки, состав участников (+
SellManager),IntentConfirmedAt, таймлайн сделки, версия — в одном согласованном изменении вместе с записью в outbox. - Публикация.
pin.crm.deal-stage-changed.v1(Draft → Application) иpin.crm.deal-members-changed.v1(появилсяSellManager, полный активный состав). С этого момента сделка видна застройщику и попадает на его канбан; developer-facing консюмеры реагируют начиная сApplication. - Результат. Сделка
Applicationс застройщиком в составе; на канбане/crmзастройщика появляется карточка. Дальше — стандартный жизненный цикл сделки (бронь, продажа).
Диаграмма
Альтернативные Потоки
- Намерение остаётся
ConfirmedByInvestor = false. Агент пытается вовлечь застройщика. CRM_DEAL_INTENT_NOT_CONFIRMED(409); сделка остаётсяDraft.
Возврат: дождаться подтверждения инвестором (шаг 3 / .../confirm).
- Намерение подтверждено, но для пары «инвестор × застройщик» нет одобренной фиксации.
CRM_DEAL_FIXATION_REQUIRED(409); сделка остаётсяDraft.
Возврат: отправить заявку на фиксацию (UC-FIX-001) и дождаться одобрения.
- Гейт по намерению и фиксации выполнен, но
PaymentMethodпуст (приDraftон необязателен). CRM_DEAL_PAYMENT_METHOD_REQUIRED(422).
Возврат: агент указывает способ оплаты (и пресет для рассрочки) и повторяет вовлечение.
- Инвестор снимает намерение (remove-booking-intent):
Status = Cancelled, в ленту —BookingIntentRemoved. - Агент отменяет связанную
Draft-сделку (Cancelled,CancelReason); застройщик о ней не знал.
- Лид
Expired/Archivedна любом шаге. 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_CONFIRMED | 409 | Вовлечение застройщика при ConfirmedByInvestor = false связанного намерения (INV-DEAL-11) | Дождаться подтверждения инвестором |
CRM_DEAL_FIXATION_REQUIRED | 409 | Нет одобренной фиксации для пары «инвестор × застройщик» (INV-DEAL-11) | Отправить заявку на фиксацию и дождаться одобрения |
CRM_DEAL_PAYMENT_METHOD_REQUIRED | 422 | При вовлечении PaymentMethod не задан (для Application, INV-DEAL-1) | Форма требует выбрать способ оплаты (и пресет рассрочки) |
CRM_LEAD_NOT_ACTIVE | 409 | Лид Expired/Archived (INV-INTENT-2 / INV-LEAD-2) | Предложить продлить/реактивировать лид |
CRM_INTENT_CONFIRM_FORBIDDEN | 403 | Подтверждение намерения не инвестором пары (INV-INTENT-4) | Скрыть действие подтверждения |
CRM_DEAL_ALREADY_TERMINAL | 409 | Вовлечение застройщика для отменённой 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 |
|---|---|---|---|
LeadBookingIntentChangedEvent | pin.crm.lead-booking-intent-changed.v1 | Предложение (Added) и подтверждение (Confirmed) намерения | notifications (инвестору — предложение, исполнителю — подтверждение), messenger (по необходимости) |
DealCreatedEvent | pin.crm.deal-created.v1 | Создание Draft-сделки (Status = Draft, без SellManager) | messenger (чат агент+инвестор), notifications; developer-facing консюмеры фильтруют Status != Draft — контракт deal-created |
DealStageChangedEvent | pin.crm.deal-stage-changed.v1 | Гейт Draft → Application (вовлечение застройщика) | messenger, notifications; момент, с которого сделка видима застройщику — контракт deal-stage-changed |
DealMembersChangedEvent | pin.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/deals → Status = 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 |
Обратные ссылки
Автоссылки: где используется этот документ.
- Use Cases (2): UC-CRM-016. Инвестор добавляет намерения бронирования (много объектов), UC-CRM-018. Несколько сделок из одной одобренной фиксации (по разным объектам застройщика)
- registries (1): Реестр use cases и user stories PIN
Связанные Документы
- API методы: propose-booking-intent, create-deal, confirm-booking-intent, engage-developer.
- Domain entity: deal (
DealStatus.Draft, гейт INV-DEAL-11, INV-DEAL-1/2), lead (LeadBookingIntentEntity, INV-INTENT-3/4), fixation-request. - Kafka события: deal-created, deal-stage-changed, deal-members-changed, lead-booking-intent-changed.
- Смежные UC: UC-CRM-016 (Flow A — инвестор сам добавляет), UC-CRM-018, UC-CRM-002, UC-FIX-001, UC-FIX-002.
- ADR: 0052 tenancy, 0055 мини-мессенджер, 0071 crm-ownership-and-participant-model.