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

UC-OVR-003. Бронирование объекта у агента (+3a)

Назначение

Cross-service сценарий: инвестор с витрины отправляет заявку на бронирование конкретного объекта; заявка превращается в лид/бронь в воронке агента, а объект в шахматке переходит в статус «Резерв». Вариант 3a — инвестор-инициатор отправляет заявку, а саму бронь в сделке создаёт агент (или менеджер ПИН); таймеры резерва/ брони — параметры программы бронирования.

Metadata

ПолеЗначение
Статусdraft
Owning teamcrm
Primary capabilitybooking-intake
Risk levelhigh

Scope И Участники

УчастникРоль
Инвестор / ГостьАктор: форма брони по объекту (PropertyObjectId)
catalogВитрина объекта, откуда пришла заявка
crm (public → agent)Оркестратор: заявка → лид → бронь в сделке (создаёт агент)
identity (internal)Lazy-регистрация инвестора
chessboardВладелец статуса объекта: перевод в «Резерв»/«Бронь» по событиям crm
calendarConsumer booking-событий: дедлайн-события «Резерв»/«Бронь» в календаре ответственного агента + напоминания (UC-CAL-007)
notificationsУведомления агенту (новая заявка) и инвестору (бронь/резерв)

Preconditions

  • Объект опубликован и доступен к бронированию (chessboard); у застройщика настроена программа бронирования (таймеры резерва 1 ч и брони 4 ч / 10 дней — параметры программы).
  • Привязка агента определяется как в UC-OVR-001.

Основной Сценарий

  1. Инвестор на карточке объекта жмёт «Забронировать», заполняет ФИО + телефон (+опц. SelectionId).
  2. Фронт → POST /api/public/crm/booking-requests (create-booking-request.md). Дубль-детект, lazy-регистрация, привязка агента (как UC-OVR-001).
  3. crm заводит Client/Lead с событием BookingRequested (payload LeadBookingRequestedPayload) и pin.crm.lead-created.v1; агент получает уведомление.
  4. Агент открывает лид и создаёт бронь в сделке: POST /api/agent/crm/bookings (create-booking.md) — «Резерв 1 ч» по программе; см. UC-BKG-002.
  5. crm публикует pin.crm.booking-created.v1; chessboard (consumer) переводит объект в «Резерв» — UC-CHS-006pin.chessboard.property-object-status-changed.v1. По тому же событию calendar (consumer crm-booking-events) создаёт в календаре ответственного агента дедлайн-событие фазы «Резерв» (Kind = BookingDeadline, Origin = CrmBooking, StartAt = ReserveExpiresAt) с напоминаниями — UC-CAL-007.
  6. Агент подтверждает бронь: confirm-bookingpin.crm.booking-confirmed.v1; объект «Бронь», таймер брони стартует по программе. В календаре агента дедлайн резерва закрывается (Completed) и создаётся дедлайн-событие фазы «Бронь» (StartAt = BookingExpiresAt); о наступающих дедлайнах агента предупреждают напоминания (UC-CAL-008).
  7. Дальнейшее (оплата брони, конвертация в сделку) — UC-OVR-006.

Sequence

Постусловие

После полного прохода сценария гарантированно:

  • есть Client/активный Lead пары агент↔инвестор с событием ленты BookingRequested (Source = BookingRequest) и опубликованным pin.crm.lead-created.v1;
  • создан Booking в статусе Reserved с дедлайном ReserveExpiresAt (снапшот ProgramTerms из программы бронирования), сделка переведена Application → Booking; опубликовано pin.crm.booking-created.v1 (key = BookingId);
  • после подтверждения — Booking в статусе Confirmed, опубликовано pin.crm.booking-confirmed.v1, стартует таймер фазы «Бронь» из программы;
  • chessboard перевёл объект недвижимости Свободен → Резерв → Бронь (по событиям crm, с задержкой < 1 мин на каждый переход) и выставил соответствующий таймер;
  • в календаре ответственного агента существует не более одного открытого дедлайн-события актуальной фазы брони (Kind = BookingDeadline, Origin = CrmBooking, StartAt = дедлайн фазы) с материализованными напоминаниями (UC-CAL-007, UC-CAL-008); закрытие фазы/брони закрывает дедлайн (Completed — исполнен, Cancelled — потерял смысл);
  • инвестор получил уведомление о резерве/брони; в чате сделки появились системные карточки.

Промежуточные состояния, наблюдаемые пользователем: (1) заявка отправлена — лид у агента, объект ещё «Свободен»; (2) агент создал бронь — объект «Резерв» с таймером 1 ч; (3) агент подтвердил — объект «Бронь» с таймером программы.

Альтернативные Сценарии И Отказы

СитуацияПоведение
Объект уже в «Резерв»/«Бронь» на момент create-bookingcrm отклоняет создание брони (INV-BKG-001 объект должен быть свободен); chessboard — источник истины статуса
Гонка двух броней одного объектаПобеждает первая зафиксированная; второй create-booking отклоняется (объект уже не свободен); оптимистическая блокировка статуса в chessboard
Резерв истёк (1 ч) без подтвержденияpin.crm.booking-expired.v1chessboard возвращает объект в «Свободен» (UC-BKG-003); calendar отменяет открытое дедлайн-событие (Cancelled, напоминания гасятся); таймер — из программы, истечение публикуется событием, не клиентом
Программа бронирования не настроена у застройщикаcreate-booking невозможен (нет ProgramTerms); заявка остаётся лидом без брони
Заявку инициирует не агент, а менеджер ПИНТот же flow; бронь создаёт сотрудник ПИН (двойная точка входа), InitiatedBy = PinManager
Повторная заявка тем же телефоном409 DUPLICATE_PHONE
Событие booking-created доставлено chessboard повторноИдемпотентно по MessageId + per-property-object guard — статус не меняется дважды

Согласованность И Идемпотентность

  • Двухфазность статуса. crm и chessboard синхронизируются только событиями: booking-created → «Резерв», booking-confirmed → «Бронь», booking-expired → «Свободен». Между публикацией и применением — окно eventual-consistency (< 1 мин для статуса объекта).
  • Единый владелец статуса. Статус объекта пишет только chessboard; crm его не правит — это защита от двойной брони и рассинхрона.
  • Идемпотентность потребителя. chessboard дедуплицирует booking-* по MessageId + per-property-object guard; messenger — по MessageId (карточка в чате не дублируется).
  • Порядок. Ключ BookingId гарантирует порядок событий одной брони (создание → подтверждение → истечение) на одной партиции.
  • Таймеры — только из программы. ReserveExpiresAt/срок брони берутся из снапшота ProgramTerms; истечение инициирует crm событием, клиент таймеры не двигает.
  • Дедлайны в календаре — производные. calendar строит дедлайн-события только по booking-*-событиям (business-идемпотентность «BookingId + фаза»: повтор/replay — no-op, дублей нет); бронью календарь не управляет — прямая правка дедлайна агентом отклоняется (CAL_EVENT_MANAGED_BY_CRM_BOOKING), продление брони переносит открытый дедлайн (event-rescheduled).

Acceptance Criteria (Given/When/Then)

  • Happy (заявка → резерв → бронь). Дано: объект опубликован и свободен, у застройщика настроена программа бронирования, привязка к агенту определена. Когда: инвестор отправляет заявку, агент создаёт и подтверждает бронь. Тогда: создан лид, Booking прошёл Reserved → Confirmed, опубликованы booking-created и booking-confirmed, chessboard провёл объект Свободен → Резерв → Бронь, инвестор уведомлён.
  • Объект занят. Дано: объект уже «Резерв»/«Бронь». Когда: агент пытается создать бронь. Тогда: create-booking отклонён (INV-BKG-001), статус объекта не меняется.
  • Резерв истёк. Дано: Booking в «Резерв», прошёл 1 ч без подтверждения. Когда: срабатывает истечение. Тогда: pin.crm.booking-expired.v1, объект возвращён в «Свободен».
  • Гонка броней. Дано: две заявки на один объект почти одновременно. Когда: обе доходят до create-booking. Тогда: первая фиксируется, вторая отклоняется; двойного «Резерв» не возникает.
  • Повтор заявки. Дано: тот же телефон в окне. Когда: повторная публичная заявка. Тогда: 409 DUPLICATE_PHONE.
  • Дедлайны в календаре агента. Дано: бронь создана, затем подтверждена. Когда: calendar обрабатывает booking-created и booking-confirmed. Тогда: в календаре агента появился дедлайн «Резерв» (StartAt = ReserveExpiresAt), при подтверждении он закрыт Completed и создан дедлайн «Бронь» (StartAt = BookingExpiresAt); напоминания о дедлайнах доставляются штатно (UC-CAL-008).

Source Of Truth И Данные

ДанныеВладелецConsistency rule
Lead / Booking / таймерыcrmТаймеры — параметры booking-program; переходы через события
Статус объекта недвижимостиchessboardЕдинственный владелец; меняется только по событиям crm (eventual)
Программа бронирования (таймеры)crmpurchase-program.md

Контракты И Зависимости

Наблюдаемость

  • critical metrics: crm_booking_requests_total{result}, конверсия заявка→бронь, доля истёкших резервов.
  • trace path: витрина → crm.public → шина событий → chessboard (смена статуса) → notifications.
  • alerts: рассинхрон статуса объекта crm↔chessboard; всплеск истечений резерва.

Риски И Guardrails

  • Риск: двойная бронь одного объекта (гонка). Control: chessboard — единый владелец статуса, оптимистическая блокировка.
  • Риск: рассинхрон таймеров. Control: таймеры только из программы бронирования; истечение — событием, не клиентом.

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

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