UC-CAL-006. Агент фиксирует исход встречи
Назначение
Описывает закрытие подтверждённой записи фактом: после наступления времени встречи агент фиксирует исход —
встреча состоялась (Completed) или клиент не пришёл (NoShow). Фиксация терминальна, каскадно архивирует
слот и завершает календарное событие; CRM получает исход в ленту лида и использует его в работе с клиентом
(конверсия записей во встречи, повторный контакт после неявки). Связан с контекстом calendar.
Metadata
| Поле | Значение |
|---|---|
| ID | UC-CAL-006 |
| Контекст | calendar (см. docs/06-services/calendar/) |
| Тип | основной |
| Статус | draft |
Актор
| Поле | Значение |
|---|---|
| Роль | Агент (или консультант ПИН) — участник встречи |
| Permissions / scope | calendar.appointments.decide — scope Organization |
| Tenant | TenantId = CompanyId компании агента, ADR-0052 |
| Record-level | Исход фиксируется только по записям на свои слоты (AgentUserId = актор); инвестор исход не фиксирует |
Предусловие
- Запись в статусе
Confirmed(UC-CAL-004). - Время встречи наступило:
now ≥ StartAt(INV-APT-4) — исход нельзя зафиксировать заранее.
Триггер
| Поле | Значение |
|---|---|
| Источник | UI button |
| Триггерящий элемент | ЛК агента → «Календарь» / «Записи» → карточка прошедшей Confirmed-записи → «Встреча состоялась» / «Клиент не пришёл» |
Основной Поток
- Агент открывает прошедшую
Confirmed-запись (../api/agent/get-appointment.md); клиент показывает действия фиксации исхода, еслиnow ≥ StartAt. - Агент выбирает исход (
Outcome = Completed— встреча состоялась, либоOutcome = NoShow— клиент не пришёл); клиент вызывает../api/agent/complete-appointment.md. - Система проверяет: актор — агент записи с
calendar.appointments.decide;Outcome ∈ {Completed, NoShow}(иначеVALIDATION_FAILED); запись вConfirmed(иначеAPPOINTMENT_INVALID_STATE, A2);now ≥ StartAt(иначеAPPOINTMENT_NOT_STARTED, A1). - В одном согласованном изменении: запись переходит
Confirmed → CompletedилиConfirmed → NoShow(CompletedAt = now); слотBooked → Archived(уходит из оборота); календарное событие →Completedв обоих случаях — факт встречи в календаре зафиксирован, исход хранится на записи; оставшиесяPending-напоминания →Skipped; публикуетсяpin.calendar.appointment-completed.v1(Outcome = Completed | NoShow). - Асинхронно:
crmпишет исход в ленту лида («встреча состоялась» / «клиент не пришёл») — база для дальнейшей работы с клиентом;notificationsуведомляет заинтересованные стороны. - Агент видит запись и событие в терминальных статусах; статистика встреч агента пополнена.
Альтернативные Потоки
A1. Фиксация раньше времени
now < StartAt→ отказAPPOINTMENT_NOT_STARTED(409, INV-APT-4). Клиент показывает, с какого момента фиксация станет доступна. Если встреча не состоится — путь отмены (UC-CAL-005), не исход.
A2. Запись не в Confirmed
- Запись
Pending(агент не подтверждал) или уже терминальна (отменена, истекла, исход уже зафиксирован) → отказAPPOINTMENT_INVALID_STATE(409). Повторная фиксация исхода невозможна — статусыCompleted/NoShowтерминальны и иммутабельны (INV-APT-6); ошибочный исход исправляется организационно (новая запись/встреча), состояние не переписывается.
A3. Гонка «фиксация ↔ отмена»
- Фиксация исхода конкурирует с отменой записи агентом: побеждает первая зафиксированная мутация,
проигравший получает
APPOINTMENT_INVALID_STATEи перечитывает запись. Отмена инвестором к этому моменту уже невозможна (cutoff давно пройден —now ≥ StartAt).
A4. Агент забыл зафиксировать исход
- Автоматического таймаута фиксации нет: запись остаётся
ConfirmedпослеStartAt, пока агент не зафиксирует исход или не отменит её. Просроченные незакрытые записи видны в списке (фильтр «требуют исхода») и в метрике — руководитель видит дисциплину закрытия встреч (UC-CAL-009).
Постусловие
- Запись —
CompletedилиNoShow(CompletedAtзаполнен), терминальна и иммутабельна. - Слот
Archived; календарное событиеCompleted; напоминания не отправляются. - Опубликовано
appointment-completedс исходом; лента лида CRM отражает результат встречи.
Возможные Ошибки
| Код | HTTP | Условие | Поведение клиента |
|---|---|---|---|
APPOINTMENT_NOT_FOUND | 404 | Запись не найдена или не на слот актора | Обновить список записей |
APPOINTMENT_ACCESS_DENIED | 403 | Нет calendar.appointments.decide | Скрыть действия исхода |
VALIDATION_FAILED | 400 | Outcome вне {Completed, NoShow} | Исправить запрос |
APPOINTMENT_NOT_STARTED | 409 | now < StartAt (A1, INV-APT-4) | Показать время доступности действия |
APPOINTMENT_INVALID_STATE | 409 | Запись не Confirmed; повторная фиксация; проигранная гонка (A2, A3) | Перечитать запись |
Kafka Интеграционные События
| Событие | Topic | Когда публикуется | Consumers |
|---|---|---|---|
AppointmentCompletedEvent.v1 | pin.calendar.appointment-completed.v1 | Фиксация исхода (шаг 4): payload несёт Outcome (Completed / NoShow) и CompletedAt | crm (лента лида), notifications |
CalendarEventStatusChangedEvent.v1 | pin.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 < StartAt → APPOINTMENT_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:ConfirmedcStartAtв прошлом — дисциплина закрытия встреч, alert при устойчивом росте).
Обратные ссылки
Автоссылки: где используется этот документ.
- Use Cases (6): UC-CAL-001. Агент ведёт рабочий календарь, UC-CAL-004. Агент подтверждает или отклоняет запись, UC-CAL-005. Отмена и перенос записи, UC-CAL-009. Руководитель смотрит календарь команды, UC-CAL-010. Руководитель ПИН смотрит календари консультантов, US-реестр контекста calendar (US-CAL)
- scenarios (1): UC-OVR-011. Самозапись инвестора: от слота до исхода встречи и лида в воронке
- index.md (1): Calendar Service
- registries (1): Реестр use cases и user stories PIN
Связанные Документы
- 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 (контроль незакрытых встреч).