UC-CAL-007. Дедлайны брони CRM в календаре агента
Назначение
Описывает автоматические события «дедлайн брони»: когда в CRM создаётся и живёт бронь объекта, календарь по
booking-событиям создаёт в календаре ответственного агента точечные события Kind = BookingDeadline
(Origin = CrmBooking) на моменты дедлайнов резерва и брони — агент видит, до когда нужно подтвердить
бронь или довести оплату, и получает напоминания штатным механизмом. Календарь не читает таблицы CRM и не
управляет бронью: интеграция только событиями; владелец календаря определяется по реплике участников
сделки. Связан с контекстом calendar.
| Поле | Значение |
|---|
| ID | UC-CAL-007 |
| Контекст | calendar (см. docs/06-services/calendar/) |
| Тип | основной |
| Статус | draft |
Актор
| Поле | Значение |
|---|
| Роль | System (консюмеры booking- и deal-событий CRM); бенефициар — агент, в чьём календаре появляются дедлайны |
| Permissions / scope | system-scope консюмеров; просмотр событий агентом — calendar.events.view |
| Tenant | События создаются в tenant компании агента сделки (AgencyCompanyId из реплики), ADR-0052 |
| Record-level | OwnerUserId = текущий исполнитель сделки (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) |
Основной Поток
- CRM создаёт бронь (UC-OVR-003) и
публикует
pin.crm.booking-created.v1 с DealId, BookingId, PropertyObjectId, ReserveExpiresAt.
- Консюмер календаря находит владельца по реплике сделки (
ExecutorAgentUserId, AgencyCompanyId); реплика
ещё не наполнена → retriable-ожидание (A1), сделка терминальна → событие не создаётся (A2).
- Консюмер создаёт календарное событие дедлайна фазы «Резерв»:
Kind = BookingDeadline,
Origin = CrmBooking, Status = Planned, точечное (EndAt = null), StartAt = ReserveExpiresAt,
BookingId + DealId + PropertyObjectId заполнены (INV-CAL-5), заголовок формируется автоматически
(«Дедлайн резерва по брони…»), приоритет повышенный (High; для фазы с оплатой — Critical).
Business-идемпотентность — не более одного дедлайн-события на пару (BookingId, фаза брони):
повторная доставка события не создаёт дубль (A3).
- Напоминания материализуются от
StartAt по офсетам события штатно
(event-reminder.md); публикуется pin.calendar.event-created.v1.
Агент видит дедлайн в календаре (list-events) и получает напоминания
(UC-CAL-008).
- Бронь подтверждена — CRM публикует
booking-confirmed с BookingExpiresAt: консюмер закрывает
дедлайн фазы «Резерв» (дедлайн исполнен → событие Completed) и создаёт дедлайн-событие фазы «Бронь»
(StartAt = BookingExpiresAt). Продление брони (повторная доставка фазы с новым BookingExpiresAt)
переносит открытое событие фазы: StartAt обновляется, Pending-напоминания пересоздаются,
публикуется pin.calendar.event-rescheduled.v1.
- Завершение жизненного цикла брони закрывает открытые дедлайн-события:
booking-paid — дедлайн исполнен
(событие Completed); booking-expired / booking-cancelled — дедлайн потерял смысл (событие
Cancelled с причиной «Бронь истекла» / «Бронь отменена», Pending-напоминания → Skipped). Каждый
переход публикует pin.calendar.event-status-changed.v1.
- Терминальные события сделки (
deal-completed / deal-cancelled) независимо отменяют все открытые
дедлайн-события сделки (crm-deal-events.md); пересечение с шагом 6 —
идемпотентное: кто успел первым, второй получает no-op.
Альтернативные Потоки
A1. Booking-событие обогнало реплику сделки
- Реплика сделки не найдена (deal- и booking-топики взаимно не упорядочены) → retriable-ошибка консюмера,
retry с backoff; после прихода
deal-created повтор успешен. Дедлайн появится с запозданием — окно
eventual consistency (штатно < 1 мин, alert по lag).
A2. Сделка уже терминальна
- Реплика помечена терминальной (terminal-tombstone) → дедлайн-событие не создаётся (в календаре не
появляются дедлайны по закрытой сделке); событие подтверждается (ack) без эффекта.
A3. Повторная доставка / replay
- Повтор booking-события — no-op по business-идемпотентности (
BookingId + фаза): дубль дедлайна не
создаётся, состояние не меняется. Полный replay booking-топиков безопасен — сходится к текущему
состоянию брони.
A4. Агент пытается управлять дедлайном
- Прямое изменение/перенос/отмена события
Origin = CrmBooking агентом → отказ
CAL_EVENT_MANAGED_BY_CRM_BOOKING (409, INV-CAL-4): дедлайн живёт только по booking-событиям CRM.
Агент действует в CRM (подтверждает/продлевает/отменяет бронь) — календарь отразит это событиями.
A5. Смена исполнителя / уход агента сделки
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
- Отсутствуют обязательные поля 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_BOOKING | 409 | Прямая операция агента над дедлайн-событием (A4, INV-CAL-4) | Открыть карточку брони в CRM |
Ошибки консюмера (retriable/non-retriable, DLQ) — не HTTP: политика retry/DLQ — в
../consumers/crm-booking-events.md.
Kafka Интеграционные События
| Событие | Topic | Когда публикуется | Consumers |
|---|
CalendarEventCreatedEvent.v1 | pin.calendar.event-created.v1 | Создан дедлайн фазы (шаги 3, 5) | notifications |
CalendarEventRescheduledEvent.v1 | pin.calendar.event-rescheduled.v1 | Перенос дедлайна при продлении (шаг 5) | notifications (CalendarEventRescheduled) |
CalendarEventStatusChangedEvent.v1 | pin.calendar.event-status-changed.v1 | Завершение/отмена дедлайна (шаги 5–7) | notifications |
CalendarEventOwnerChangedEvent.v1 | pin.calendar.event-owner-changed.v1 | Переназначение владельца открытого дедлайна/встречи при смене исполнителя / уходе агента (A5) | notifications (CalendarEventOwnerChanged) |
ReminderDueEvent.v1 | pin.calendar.reminder-due.v1 | Наступление напоминания по дедлайну (шаг 4) | notifications (CalendarReminder) |
Потребляемые события — контракты CRM: booking-created,
booking-confirmed, booking-paid,
booking-expired, booking-cancelled,
deal-completed, deal-cancelled.
Acceptance Criteria
| # | Критерий | Проверка |
|---|
| AC1 | booking-created создаёт в календаре агента сделки точечное событие BookingDeadline (Origin = CrmBooking, StartAt = ReserveExpiresAt, BookingId+DealId заполнены) и напоминания к нему | интеграционный тест консюмера |
| AC2 | Повторная доставка того же booking-created не создаёт второго события (идемпотентность BookingId + фаза) | тест идемпотентности (A3) |
| AC3 | booking-confirmed закрывает дедлайн резерва (Completed) и создаёт дедлайн брони (StartAt = BookingExpiresAt) | тест смены фазы (шаг 5) |
| AC4 | Продление брони переносит открытое дедлайн-событие: новый StartAt, напоминания пересозданы, опубликовано event-rescheduled | тест переноса (шаг 5) |
| AC5 | booking-expired/booking-cancelled отменяют открытый дедлайн (Cancelled, причина заполнена, напоминания Skipped); booking-paid завершает (Completed) | тест закрытия (шаг 6) |
| AC6 | deal-cancelled отменяет все открытые дедлайны сделки; пересечение с booking-отменой — no-op у второго | тест терминала сделки (шаг 7) |
| AC7 | Booking-событие по терминальной сделке не создаёт событий (A2); обогнавшее реплику — retry до успеха (A1) | тесты out-of-order |
| AC8 | Прямая правка дедлайна агентом → CAL_EVENT_MANAGED_BY_CRM_BOOKING, событие не изменено (A4) | негативный тест защиты |
| AC9 | deal-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 при непустом).
Обратные ссылки
Автоссылки: где используется этот документ.
Связанные Документы