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

UC-CAL-006. Агент фиксирует исход встречи

Назначение

Описывает закрытие подтверждённой записи фактом: после наступления времени встречи агент фиксирует исход — встреча состоялась (Completed) или клиент не пришёл (NoShow). Фиксация терминальна, каскадно архивирует слот и завершает календарное событие; CRM получает исход в ленту лида и использует его в работе с клиентом (конверсия записей во встречи, повторный контакт после неявки). Связан с контекстом calendar.

Metadata

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

Актор

ПолеЗначение
РольАгент (или консультант ПИН) — участник встречи
Permissions / scopecalendar.appointments.decide — scope Organization
TenantTenantId = CompanyId компании агента, ADR-0052
Record-levelИсход фиксируется только по записям на свои слоты (AgentUserId = актор); инвестор исход не фиксирует

Предусловие

  • Запись в статусе Confirmed (UC-CAL-004).
  • Время встречи наступило: now ≥ StartAt (INV-APT-4) — исход нельзя зафиксировать заранее.

Триггер

ПолеЗначение
ИсточникUI button
Триггерящий элементЛК агента → «Календарь» / «Записи» → карточка прошедшей Confirmed-записи → «Встреча состоялась» / «Клиент не пришёл»

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

  1. Агент открывает прошедшую Confirmed-запись (../api/agent/get-appointment.md); клиент показывает действия фиксации исхода, если now ≥ StartAt.
  2. Агент выбирает исход (Outcome = Completed — встреча состоялась, либо Outcome = NoShow — клиент не пришёл); клиент вызывает ../api/agent/complete-appointment.md.
  3. Система проверяет: актор — агент записи с calendar.appointments.decide; Outcome ∈ {Completed, NoShow} (иначе VALIDATION_FAILED); запись в Confirmed (иначе APPOINTMENT_INVALID_STATE, A2); now ≥ StartAt (иначе APPOINTMENT_NOT_STARTED, A1).
  4. В одном согласованном изменении: запись переходит Confirmed → Completed или Confirmed → NoShow (CompletedAt = now); слот Booked → Archived (уходит из оборота); календарное событие → Completed в обоих случаях — факт встречи в календаре зафиксирован, исход хранится на записи; оставшиеся Pending-напоминания → Skipped; публикуется pin.calendar.appointment-completed.v1 (Outcome = Completed | NoShow).
  5. Асинхронно: crm пишет исход в ленту лида («встреча состоялась» / «клиент не пришёл») — база для дальнейшей работы с клиентом; notifications уведомляет заинтересованные стороны.
  6. Агент видит запись и событие в терминальных статусах; статистика встреч агента пополнена.

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

A1. Фиксация раньше времени

  1. now < StartAt → отказ APPOINTMENT_NOT_STARTED (409, INV-APT-4). Клиент показывает, с какого момента фиксация станет доступна. Если встреча не состоится — путь отмены (UC-CAL-005), не исход.

A2. Запись не в Confirmed

  1. Запись Pending (агент не подтверждал) или уже терминальна (отменена, истекла, исход уже зафиксирован) → отказ APPOINTMENT_INVALID_STATE (409). Повторная фиксация исхода невозможна — статусы Completed/ NoShow терминальны и иммутабельны (INV-APT-6); ошибочный исход исправляется организационно (новая запись/встреча), состояние не переписывается.

A3. Гонка «фиксация ↔ отмена»

  1. Фиксация исхода конкурирует с отменой записи агентом: побеждает первая зафиксированная мутация, проигравший получает APPOINTMENT_INVALID_STATE и перечитывает запись. Отмена инвестором к этому моменту уже невозможна (cutoff давно пройден — now ≥ StartAt).

A4. Агент забыл зафиксировать исход

  1. Автоматического таймаута фиксации нет: запись остаётся Confirmed после StartAt, пока агент не зафиксирует исход или не отменит её. Просроченные незакрытые записи видны в списке (фильтр «требуют исхода») и в метрике — руководитель видит дисциплину закрытия встреч (UC-CAL-009).

Постусловие

  • Запись — Completed или NoShow (CompletedAt заполнен), терминальна и иммутабельна.
  • Слот Archived; календарное событие Completed; напоминания не отправляются.
  • Опубликовано appointment-completed с исходом; лента лида CRM отражает результат встречи.

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

КодHTTPУсловиеПоведение клиента
APPOINTMENT_NOT_FOUND404Запись не найдена или не на слот актораОбновить список записей
APPOINTMENT_ACCESS_DENIED403Нет calendar.appointments.decideСкрыть действия исхода
VALIDATION_FAILED400Outcome вне {Completed, NoShow}Исправить запрос
APPOINTMENT_NOT_STARTED409now < StartAt (A1, INV-APT-4)Показать время доступности действия
APPOINTMENT_INVALID_STATE409Запись не Confirmed; повторная фиксация; проигранная гонка (A2, A3)Перечитать запись

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

СобытиеTopicКогда публикуетсяConsumers
AppointmentCompletedEvent.v1pin.calendar.appointment-completed.v1Фиксация исхода (шаг 4): payload несёт Outcome (Completed / NoShow) и CompletedAtcrm (лента лида), notifications
CalendarEventStatusChangedEvent.v1pin.calendar.event-status-changed.v1Каскадное завершение календарного событияnotifications

Acceptance Criteria

#КритерийПроверка
AC1Фиксация Outcome = Completed по Confirmed-записи при now ≥ StartAt атомарно: запись Completed, слот Archived, событие Completed, appointment-completed (Completed)интеграционный тест исхода
AC2Фиксация Outcome = NoShow: запись NoShow, слот Archived, событие Completed (факт встречи в календаре), appointment-completed (NoShow)интеграционный тест неявки
AC3Фиксация при now < StartAtAPPOINTMENT_NOT_STARTED, состояние не измененонегативный тест (A1)
AC4Фиксация по Pending/терминальной записи → APPOINTMENT_INVALID_STATE; повторная фиксация не дублирует событийнегативный тест терминальности (A2)
AC5Конкурентные complete (агент) и cancel (агент с другого устройства): ровно один переход и один каскадтест гонки (A3)
AC6По appointment-completed CRM добавляет в ленту лида запись об исходе встречиинтеграционный тест CRM-проекции
AC7Незакрытая Confirmed-запись после StartAt не переводится автоматически и попадает в фильтр «требуют исхода»тест отсутствия автотаймаута (A4)

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

  • Логи (без PII). appointment-completed (appointmentId, slotId, tenantId, agentUserId, outcome, completedAt).
  • Метрики. calendar_appointments_completed_total{outcome} (доля NoShow — качество записей); calendar_appointments_pending_outcome (gauge: Confirmed c StartAt в прошлом — дисциплина закрытия встреч, alert при устойчивом росте).

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

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

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

  • Domain: appointment.md (INV-APT-4/6, каскад-таблица, терминальные статусы), availability-slot.md (Booked → Archived), calendar-event.md.
  • API: ../api/agent/complete-appointment.md, ../api/agent/get-appointment.md, ../api/agent/list-appointments.md.
  • События: ../events/appointment-completed.md, ../events/event-status-changed.md; CRM-консюмер — ../../crm/consumers/calendar-appointments.md.
  • Связанные UC: UC-CAL-004 (появление Confirmed-записи), UC-CAL-005 (отмена вместо исхода), UC-CAL-009 (контроль незакрытых встреч).