UC-CAL-002. Агент публикует слоты доступности
Назначение
Описывает сценарий публикации доступности: агент (или инвест-консультант ПИН) задаёт локальные окна
(«Вт, Чт 10:00–13:00»), дни недели, длительность слота и горизонт — система нарезает окна на конкретные
слоты и выставляет их на публичную витрину для самозаписи инвесторов. Генерация DST-корректна (окна
считаются в локальном времени агента), батч ограничен 500 слотами, результат — отчёт «создано/пропущено с
причинами». Связан с контекстом calendar.
Metadata
| Поле | Значение |
|---|---|
| ID | UC-CAL-002 |
| Контекст | calendar (см. docs/06-services/calendar/) |
| Тип | основной |
| Статус | draft |
Актор
| Поле | Значение |
|---|---|
| Роль | Агент (или консультант ПИН) — владелец публикуемой доступности |
| Permissions / scope | calendar.slots.manage — scope Organization |
| Tenant | TenantId = CompanyId агентства (для консультанта ПИН — компания ПИН), ADR-0052 |
| Record-level | Слоты создаются только на себя (AgentUserId = актор); публикация за другого агента не поддерживается |
Предусловие
- Пользователь авторизован, активен и имеет
calendar.slots.manage. - Агент знает свою рабочую таймзону; default-значение предзаполняется из профиля
(
Timezone) — при недоступностиprofileтаймзона указывается вручную (деградация UX, операция не блокируется).
Триггер
| Поле | Значение |
|---|---|
| Источник | UI button |
| Триггерящий элемент | ЛК агента → «Календарь» → «Доступность» → «Опубликовать слоты» |
Основной Поток
- Агент открывает форму публикации и задаёт параметры батча: вид встречи (
Kind ∈ {Showing, Consultation}), горизонт (локальные датыDateFrom/DateTo), дни недели, локальные окна (HH:mm–HH:mm, до 8), длительность слота (минуты),TimeZone(IANA),AutoConfirm(defaultfalse),CancellationCutoffMinutes(default120),MinLeadTimeMinutes(default60), опциональноProjectId/PropertyObjectId/Location; клиент вызывает publish-slots. - Система проверяет вход: окна корректны (начало < конца), длительность > 0, дни недели непусты, горизонт
не целиком в прошлом (иначе
CAL_SLOT_BATCH_INVALID_WINDOW);TimeZone— валидный IANA-идентификатор (иначеCAL_SLOT_TIMEZONE_UNKNOWN);Kind— толькоShowing/Consultation(иначеVALIDATION_FAILED). - Система нарезает кандидатов: локальные окна × подходящие дни недели горизонта × длительность; неполный
хвост окна отбрасывается. Вычисление — в локальном времени агента по IANA-таймзоне, хранение — в UTC:
окно «10:00–13:00» остаётся 10:00 локальными по обе стороны перевода часов; несуществующее локальное
время дня перевода — кандидат пропускается (
NonExistentLocalTime), неоднозначное — первое вхождение (availability-slot.md, правила batch-генерации). - Если кандидатов больше 500 — весь батч отклоняется (
CAL_SLOT_BATCH_LIMIT_EXCEEDED), ничего не создаётся. - Система фильтрует кандидатов:
StartAt < now + MinLeadTimeMinutes→ пропуск (LeadTime); пересечение с существующими слотами агента вAvailable/Bookedили друг с другом → пропуск (Overlap, INV-SLT-2). Пропуск — не ошибка: кандидат попадает в отчёт с причиной. - Оставшиеся кандидаты создаются слотами
Availableс общимBatchIdи параметрами батча. Публикация атомарна: либо создан весь отчётный набор, либо ничего. Интеграционные события не публикуются — публикация слотов внутреннее дело контекста. - Система возвращает отчёт публикации:
BatchId,CreatedCount,SkippedCount, список пропущенных кандидатов с причинами (Overlap/LeadTime/NonExistentLocalTime). - Созданные слоты немедленно видны на анонимной витрине (
../api/public/list-available-slots.md) и в списке слотов агента (../api/agent/list-slots.md); инвесторы записываются на них — UC-CAL-003.
Альтернативные Потоки
A1. Повторная публикация тех же окон
- Агент повторяет публикацию батча с теми же параметрами (double-click, retry клиента). Все кандидаты
пересекаются с уже созданными слотами и пропускаются по INV-SLT-2 — отчёт «создано 0, пропущено N
(
Overlap)». Дублей слотов структурно не возникает; отдельный ключ идемпотентности не нужен.
A2. Батч слишком большой
- Параметры порождают > 500 кандидатов → отказ
CAL_SLOT_BATCH_LIMIT_EXCEEDED, ни один слот не создан. Агент уменьшает горизонт или окна и повторяет.
A3. Частично прошедший горизонт
- Горизонт начинается в прошлом, но не целиком: прошедшие кандидаты и кандидаты ближе
MinLeadTimeMinutesпропускаются (LeadTime), будущие создаются. Отчёт отражает оба исхода. Горизонт целиком в прошлом →CAL_SLOT_BATCH_INVALID_WINDOW(шаг 2).
A4. Уборка опубликованного
- Агент архивирует ненужный слот (
../api/agent/archive-slot.md):Available/Expired→Archived(слот уходит с витрины). - Слот
Bookedархивировать напрямую нельзя → отказCAL_SLOT_BOOKED(INV-SLT-4): сначала нужно разрешить судьбу записи (отменить/отклонить — UC-CAL-004, UC-CAL-005); слот освободится каскадом записи.
A5. Невостребованные слоты
Available-слот, чейStartAtнаступил, переводится worker-ом (../background/slot-expiration.md) вExpiredи уходит с витрины; действий агента не требуется. Повторный проход worker-а — no-op.
Постусловие
- В tenant агента существует батч слотов
Availableс общимBatchId; каждый слот удовлетворяет INV-SLT-1/2 (интервал корректен, пересечений с активными слотами агента нет). - Слоты видны на публичной витрине и доступны для бронирования до
StartAt − MinLeadTimeMinutes. - Отчёт публикации выдан агенту; пропущенные кандидаты не создали строк.
Возможные Ошибки
| Код | HTTP | Условие | Поведение клиента |
|---|---|---|---|
VALIDATION_FAILED | 400 | Kind вне {Showing, Consultation}, некорректный формат полей | Подсветить поля |
CAL_SLOT_BATCH_INVALID_WINDOW | 400 | Окно/длительность/дни недели/горизонт некорректны (шаг 2, A3) | Подсветить параметры окон |
CAL_SLOT_TIMEZONE_UNKNOWN | 400 | Невалидный IANA-идентификатор | Подсветить таймзону |
CAL_SLOT_BATCH_LIMIT_EXCEEDED | 400 | Батч породил бы > 500 слотов (A2) | Предложить сузить горизонт/окна |
CAL_SLOT_ACCESS_DENIED | 403 | Нет calendar.slots.manage | Скрыть публикацию |
CAL_SLOT_NOT_FOUND | 404 | Архивация несуществующего/чужого слота (A4) | Обновить список слотов |
CAL_SLOT_BOOKED | 409 | Прямая архивация Booked-слота (A4, INV-SLT-4) | Открыть карточку записи |
Acceptance Criteria
| # | Критерий | Проверка |
|---|---|---|
| AC1 | Окно «Вт, Чт 10:00–13:00 Europe/Moscow», 60 мин, горизонт 2 недели → создано 12 слотов Available (по 3 на каждый из 4 дней), время в UTC, отчёт «создано 12, пропущено 0» | интеграционный тест генерации |
| AC2 | Горизонт через перевод часов: слоты по обе стороны начинаются в 10:00 локальных (разные UTC-моменты); несуществующее локальное время — пропуск NonExistentLocalTime, двойных/пропавших слотов нет | тест DST |
| AC3 | Кандидат, пересекающийся с существующим Available/Booked-слотом агента, пропускается с причиной Overlap; непересекающиеся кандидаты того же батча создаются | тест пересечений |
| AC4 | Батч из 600 кандидатов → CAL_SLOT_BATCH_LIMIT_EXCEEDED, ни один слот не создан (атомарность) | негативный тест лимита |
| AC5 | Повторная публикация тех же окон → «создано 0», все кандидаты Overlap; дублей нет (A1) | тест идемпотентности |
| AC6 | Прямая архивация Booked-слота → CAL_SLOT_BOOKED; слот и запись не изменены (A4) | негативный тест защиты |
| AC7 | Публикация атомарна: при сбое посреди создания не существует частичного батча | тест атомарности |
| AC8 | Созданные слоты сразу возвращаются анонимной витриной свободных слотов | тест витрины |
Наблюдаемость
- Логи (без PII).
slots-published(tenantId,agentUserId,batchId,createdCount,skippedCount);Locationв логи не пишется (может содержать адрес). - Метрики.
calendar_slots_published_total{result},calendar_slots_skipped_total{reason}— ростOverlapбез роста созданных сигналит о повторных публикациях;calendar_slots_expired_total(worker).
Обратные ссылки
Автоссылки: где используется этот документ.
- Use Cases (4): UC-CAL-003. Инвестор бронирует слот с витрины, UC-CAL-009. Руководитель смотрит календарь команды, UC-CAL-010. Руководитель ПИН смотрит календари консультантов, US-реестр контекста calendar (US-CAL)
- scenarios (3): UC-OVR-001. Запрос консультации у агента недвижимости, UC-OVR-002. Запрос консультации у инвест-консультанта ПИН (+2a), UC-OVR-011. Самозапись инвестора: от слота до исхода встречи и лида в воронке
- index.md (1): Calendar Service
- registries (1): Реестр use cases и user stories PIN
Связанные Документы
- Domain: availability-slot.md (поля, правила batch-генерации,
INV-SLT-1..5,
SlotStatus, stateDiagram). - API: publish-slots.md (контракт батча и отчёта),
../api/agent/list-slots.md,../api/agent/archive-slot.md,../api/public/list-available-slots.md. - Worker:
../background/slot-expiration.md(истечение невостребованных слотов). - ADR: 0064 контекст calendar п.3/6 (материализованные слоты, DST, лимит батча).
- Связанные UC: UC-CAL-003 (бронирование опубликованных слотов), UC-CAL-001 (ручные события того же календаря).