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

UC-CAL-007. Дедлайны брони CRM в календаре агента

Назначение

Описывает автоматические события «дедлайн брони»: когда в CRM создаётся и живёт бронь объекта, календарь по booking-событиям создаёт в календаре ответственного агента точечные события Kind = BookingDeadline (Origin = CrmBooking) на моменты дедлайнов резерва и брони — агент видит, до когда нужно подтвердить бронь или довести оплату, и получает напоминания штатным механизмом. Календарь не читает таблицы CRM и не управляет бронью: интеграция только событиями; владелец календаря определяется по реплике участников сделки. Связан с контекстом calendar.

Metadata

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

Актор

ПолеЗначение
РольSystem (консюмеры booking- и deal-событий CRM); бенефициар — агент, в чьём календаре появляются дедлайны
Permissions / scopesystem-scope консюмеров; просмотр событий агентом — calendar.events.view
TenantСобытия создаются в tenant компании агента сделки (AgencyCompanyId из реплики), ADR-0052
Record-levelOwnerUserId = текущий исполнитель сделки (ExecutorAgentUserId из реплики участников)

Предусловие

  • Реплика «сделка → участники» наполнена deal-событиями CRM (crm-deal-events.md): для сделки известны AgencyCompanyId (tenant), множество агентов и ExecutorAgentUserId (владелец календаря дедлайнов); сделка не терминальна.
  • CRM публикует booking-события с дедлайнами: ReserveExpiresAt в booking-created, BookingExpiresAt в booking-confirmed.

Триггер

ПолеЗначение
ИсточникKafka event
Триггерящий элементКонсюмер ../consumers/crm-booking-events.md (топики pin.crm.booking-created.v1, booking-confirmed.v1, booking-paid.v1, booking-expired.v1, booking-cancelled.v1)

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

  1. CRM создаёт бронь (UC-OVR-003) и публикует pin.crm.booking-created.v1 с DealId, BookingId, PropertyObjectId, ReserveExpiresAt.
  2. Консюмер календаря находит владельца по реплике сделки (ExecutorAgentUserId, AgencyCompanyId); реплика ещё не наполнена → retriable-ожидание (A1), сделка терминальна → событие не создаётся (A2).
  3. Консюмер создаёт календарное событие дедлайна фазы «Резерв»: Kind = BookingDeadline, Origin = CrmBooking, Status = Planned, точечное (EndAt = null), StartAt = ReserveExpiresAt, BookingId + DealId + PropertyObjectId заполнены (INV-CAL-5), заголовок формируется автоматически («Дедлайн резерва по брони…»), приоритет повышенный (High; для фазы с оплатой — Critical). Business-идемпотентность — не более одного дедлайн-события на пару (BookingId, фаза брони): повторная доставка события не создаёт дубль (A3).
  4. Напоминания материализуются от StartAt по офсетам события штатно (event-reminder.md); публикуется pin.calendar.event-created.v1. Агент видит дедлайн в календаре (list-events) и получает напоминания (UC-CAL-008).
  5. Бронь подтверждена — CRM публикует booking-confirmed с BookingExpiresAt: консюмер закрывает дедлайн фазы «Резерв» (дедлайн исполнен → событие Completed) и создаёт дедлайн-событие фазы «Бронь» (StartAt = BookingExpiresAt). Продление брони (повторная доставка фазы с новым BookingExpiresAt) переносит открытое событие фазы: StartAt обновляется, Pending-напоминания пересоздаются, публикуется pin.calendar.event-rescheduled.v1.
  6. Завершение жизненного цикла брони закрывает открытые дедлайн-события: booking-paid — дедлайн исполнен (событие Completed); booking-expired / booking-cancelled — дедлайн потерял смысл (событие Cancelled с причиной «Бронь истекла» / «Бронь отменена», Pending-напоминания → Skipped). Каждый переход публикует pin.calendar.event-status-changed.v1.
  7. Терминальные события сделки (deal-completed / deal-cancelled) независимо отменяют все открытые дедлайн-события сделки (crm-deal-events.md); пересечение с шагом 6 — идемпотентное: кто успел первым, второй получает no-op.

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

A1. Booking-событие обогнало реплику сделки

  1. Реплика сделки не найдена (deal- и booking-топики взаимно не упорядочены) → retriable-ошибка консюмера, retry с backoff; после прихода deal-created повтор успешен. Дедлайн появится с запозданием — окно eventual consistency (штатно < 1 мин, alert по lag).

A2. Сделка уже терминальна

  1. Реплика помечена терминальной (terminal-tombstone) → дедлайн-событие не создаётся (в календаре не появляются дедлайны по закрытой сделке); событие подтверждается (ack) без эффекта.

A3. Повторная доставка / replay

  1. Повтор booking-события — no-op по business-идемпотентности (BookingId + фаза): дубль дедлайна не создаётся, состояние не меняется. Полный replay booking-топиков безопасен — сходится к текущему состоянию брони.

A4. Агент пытается управлять дедлайном

  1. Прямое изменение/перенос/отмена события Origin = CrmBooking агентом → отказ CAL_EVENT_MANAGED_BY_CRM_BOOKING (409, INV-CAL-4): дедлайн живёт только по booking-событиям CRM. Агент действует в CRM (подтверждает/продлевает/отменяет бронь) — календарь отразит это событиями.

A5. Смена исполнителя / уход агента сделки

  1. deal-members-changed (ChangeType = ExecutorChanged или Removed/увольнение) меняет ExecutorAgentUserId/множество агентов в реплике: новые дедлайн-события создаются в календаре нового исполнителя, а открытые дедлайны брони и встречи ушедших агентов переназначаются на нового исполнителя консюмером deal-событий (crm-deal-events.md, шаг 4a, INV-CAL-8): OwnerUserId меняется в пределах того же AgencyCompanyId, по каждому событию публикуется pin.calendar.event-owner-changed.v1. События Origin = Appointment не переназначаются (INV-CAL-3).

A6. Некорректный payload

  1. Отсутствуют обязательные поля booking-события (нет BookingId/DealId/дедлайна) → non-retriable → DLQ + алерт; разбор контракта с producer-ом crm. Остальные брони обрабатываются штатно.

Постусловие

  • По каждой активной фазе брони в календаре ответственного агента существует не более одного открытого дедлайн-события (BookingDeadline, Origin = CrmBooking) с StartAt, равным актуальному дедлайну фазы.
  • Закрытые фазы/брони/сделки не оставляют открытых дедлайнов: события Completed (дедлайн исполнен) или Cancelled (дедлайн потерял смысл), напоминания Skipped.
  • Все изменения дедлайн-событий отражены штатными событиями pin.calendar.event-*notifications уведомляет агента.

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

КодHTTPУсловиеПоведение клиента
CAL_EVENT_MANAGED_BY_CRM_BOOKING409Прямая операция агента над дедлайн-событием (A4, INV-CAL-4)Открыть карточку брони в CRM

Ошибки консюмера (retriable/non-retriable, DLQ) — не HTTP: политика retry/DLQ — в ../consumers/crm-booking-events.md.

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

СобытиеTopicКогда публикуетсяConsumers
CalendarEventCreatedEvent.v1pin.calendar.event-created.v1Создан дедлайн фазы (шаги 3, 5)notifications
CalendarEventRescheduledEvent.v1pin.calendar.event-rescheduled.v1Перенос дедлайна при продлении (шаг 5)notifications (CalendarEventRescheduled)
CalendarEventStatusChangedEvent.v1pin.calendar.event-status-changed.v1Завершение/отмена дедлайна (шаги 5–7)notifications
CalendarEventOwnerChangedEvent.v1pin.calendar.event-owner-changed.v1Переназначение владельца открытого дедлайна/встречи при смене исполнителя / уходе агента (A5)notifications (CalendarEventOwnerChanged)
ReminderDueEvent.v1pin.calendar.reminder-due.v1Наступление напоминания по дедлайну (шаг 4)notifications (CalendarReminder)

Потребляемые события — контракты CRM: booking-created, booking-confirmed, booking-paid, booking-expired, booking-cancelled, deal-completed, deal-cancelled.

Acceptance Criteria

#КритерийПроверка
AC1booking-created создаёт в календаре агента сделки точечное событие BookingDeadline (Origin = CrmBooking, StartAt = ReserveExpiresAt, BookingId+DealId заполнены) и напоминания к немуинтеграционный тест консюмера
AC2Повторная доставка того же booking-created не создаёт второго события (идемпотентность BookingId + фаза)тест идемпотентности (A3)
AC3booking-confirmed закрывает дедлайн резерва (Completed) и создаёт дедлайн брони (StartAt = BookingExpiresAt)тест смены фазы (шаг 5)
AC4Продление брони переносит открытое дедлайн-событие: новый StartAt, напоминания пересозданы, опубликовано event-rescheduledтест переноса (шаг 5)
AC5booking-expired/booking-cancelled отменяют открытый дедлайн (Cancelled, причина заполнена, напоминания Skipped); booking-paid завершает (Completed)тест закрытия (шаг 6)
AC6deal-cancelled отменяет все открытые дедлайны сделки; пересечение с booking-отменой — no-op у второготест терминала сделки (шаг 7)
AC7Booking-событие по терминальной сделке не создаёт событий (A2); обогнавшее реплику — retry до успеха (A1)тесты out-of-order
AC8Прямая правка дедлайна агентом → CAL_EVENT_MANAGED_BY_CRM_BOOKING, событие не изменено (A4)негативный тест защиты
AC9deal-members-changed со сменой исполнителя (ExecutorChanged) переназначает OwnerUserId открытых дедлайнов и встреч сделки на нового исполнителя, публикует event-owner-changed; Origin = Appointment не трогается (A5)тест переназначения (шаг 4a консюмера deal-событий)

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

  • Логи (без PII). booking-deadline-created / -rescheduled / -closed (bookingId, dealId, eventId, phase, ownerUserId, tenantId, причина закрытия).
  • Метрики. calendar_booking_deadlines_total{action} (created/rescheduled/completed/cancelled); lag консюмера booking-событий (< 1 мин штатно, alert > 5 мин); DLQ консюмера (alert при непустом).

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

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

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

  • Consumers: ../consumers/crm-booking-events.md (маппинг «booking-событие → эффект», идемпотентность, retry/DLQ), crm-deal-events.md (реплика участников, терминальный guard).
  • Domain: calendar-event.md (BookingDeadline, INV-CAL-4/5), event-reminder.md.
  • CRM-сторона: booking.md (фазы и дедлайны брони), deal.md.
  • Сценарий: UC-OVR-003 (бронирование у агента — источник дедлайнов).
  • Связанные UC: UC-CAL-001 (дедлайны в общем календаре, защита от правки), UC-CAL-008 (напоминания о дедлайнах).