UC-OVR-003. Бронирование объекта у агента (+3a)
Назначение
Cross-service сценарий: инвестор с витрины отправляет заявку на бронирование конкретного объекта; заявка превращается в лид/бронь в воронке агента, а объект в шахматке переходит в статус «Резерв». Вариант 3a — инвестор-инициатор отправляет заявку, а саму бронь в сделке создаёт агент (или менеджер ПИН); таймеры резерва/ брони — параметры программы бронирования.
Metadata
| Поле | Значение |
|---|---|
| Статус | draft |
| Owning team | crm |
| Primary capability | booking-intake |
| Risk level | high |
Scope И Участники
| Участник | Роль |
|---|---|
| Инвестор / Гость | Актор: форма брони по объекту (PropertyObjectId) |
catalog | Витрина объекта, откуда пришла заявка |
crm (public → agent) | Оркестратор: заявка → лид → бронь в сделке (создаёт агент) |
identity (internal) | Lazy-регистрация инвестора |
chessboard | Владелец статуса объекта: перевод в «Резерв»/«Бронь» по событиям crm |
calendar | Consumer booking-событий: дедлайн-события «Резерв»/«Бронь» в календаре ответственного агента + напоминания (UC-CAL-007) |
notifications | Уведомления агенту (новая заявка) и инвестору (бронь/резерв) |
Preconditions
- Объект опубликован и доступен к бронированию (
chessboard); у застройщика настроена программа бронирования (таймеры резерва 1 ч и брони 4 ч / 10 дней — параметры программы). - Привязка агента определяется как в UC-OVR-001.
Основной Сценарий
- Инвестор на карточке объекта жмёт «Забронировать», заполняет ФИО + телефон (+опц.
SelectionId). - Фронт →
POST /api/public/crm/booking-requests(create-booking-request.md). Дубль-детект, lazy-регистрация, привязка агента (как UC-OVR-001). crmзаводитClient/Leadс событиемBookingRequested(payloadLeadBookingRequestedPayload) иpin.crm.lead-created.v1; агент получает уведомление.- Агент открывает лид и создаёт бронь в сделке:
POST /api/agent/crm/bookings(create-booking.md) — «Резерв 1 ч» по программе; см. UC-BKG-002. crmпубликуетpin.crm.booking-created.v1;chessboard(consumer) переводит объект в «Резерв» — UC-CHS-006 →pin.chessboard.property-object-status-changed.v1. По тому же событиюcalendar(consumer crm-booking-events) создаёт в календаре ответственного агента дедлайн-событие фазы «Резерв» (Kind = BookingDeadline,Origin = CrmBooking,StartAt = ReserveExpiresAt) с напоминаниями — UC-CAL-007.- Агент подтверждает бронь:
confirm-booking→pin.crm.booking-confirmed.v1; объект «Бронь», таймер брони стартует по программе. В календаре агента дедлайн резерва закрывается (Completed) и создаётся дедлайн-событие фазы «Бронь» (StartAt = BookingExpiresAt); о наступающих дедлайнах агента предупреждают напоминания (UC-CAL-008). - Дальнейшее (оплата брони, конвертация в сделку) — 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-booking | crm отклоняет создание брони (INV-BKG-001 объект должен быть свободен); chessboard — источник истины статуса |
| Гонка двух броней одного объекта | Побеждает первая зафиксированная; второй create-booking отклоняется (объект уже не свободен); оптимистическая блокировка статуса в chessboard |
| Резерв истёк (1 ч) без подтверждения | pin.crm.booking-expired.v1 → chessboard возвращает объект в «Свободен» (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) |
| Программа бронирования (таймеры) | crm | purchase-program.md |
Контракты И Зависимости
- API: create-booking-request.md, create-booking.md.
- Events: booking-created.md, booking-confirmed.md, property-object-status-changed.md.
- UC сервисов: UC-BKG-002, UC-CHS-006, UC-CAL-007, UC-CAL-008.
- Consumers: crm-booking-events.md (маппинг «booking-событие → дедлайн», идемпотентность, retry/DLQ).
- ADR: 0056 simplicity-first, 0064 calendar.
Наблюдаемость
- critical metrics:
crm_booking_requests_total{result}, конверсия заявка→бронь, доля истёкших резервов. - trace path: витрина →
crm.public→ шина событий →chessboard(смена статуса) →notifications. - alerts: рассинхрон статуса объекта crm↔chessboard; всплеск истечений резерва.
Риски И Guardrails
- Риск: двойная бронь одного объекта (гонка). Control:
chessboard— единый владелец статуса, оптимистическая блокировка. - Риск: рассинхрон таймеров. Control: таймеры только из программы бронирования; истечение — событием, не клиентом.
Обратные ссылки
Автоссылки: где используется этот документ.
- Use Cases (2): UC-CAL-007. Дедлайны брони CRM в календаре агента, US-реестр контекста calendar (US-CAL)
- scenarios (3): Кросс-сервисные сценарии PIN (OVR), UC-OVR-004. От реферальной ссылки до объекта у собственника, UC-OVR-006. Сделка: от брони до регистрации (инвестор-инициатор)
- index.md (1): Calendar Service
- registries (1): Реестр use cases и user stories PIN