Перейти к содержанию

Scheduling Rules — Правила расписания

Извлечено из кода: backend/app/models/catalog.py, backend/app/services/availability.py, backend/app/api/v1/endpoints/schedule.py, backend/app/api/v1/endpoints/public.py


SR-001: Двухуровневая модель расписания

  • Описание: Расписание существует на двух уровнях: бизнес и мастер
  • Условие: Любой запрос расписания или слотов
  • Действие:
  • Бизнес-расписание: Schedule WHERE business_id=X AND master_id IS NULL
  • Мастер-расписание: Schedule WHERE business_id=X AND master_id=Y
  • Приоритет: мастер > бизнес (fallback на бизнес, если у мастера нет)
  • Исключения:
  • Мастер может не иметь своего расписания — тогда используется бизнес-дефолт
  • При создании бизнеса автоматически создаётся 7-дневное расписание (Пн-Пт: 09:00-18:00, Сб-Вс: off)
  • Источник в коде: backend/app/services/availability.py::get_day_schedule()

SR-002: Структура дня расписания

  • Описание: Каждый день описывается одной записью в таблице schedules
  • Условие:
  • Действие: Поля:
  • day_of_week: 0=Monday .. 6=Sunday (Python weekday convention)
  • start_time: "HH:MM" (строка, например "09:00")
  • end_time: "HH:MM" (строка, например "18:00")
  • break_start_time: "HH:MM" (nullable)
  • break_end_time: "HH:MM" (nullable)
  • is_active: boolean (рабочий/выходной)
  • Исключения:
  • Только один перерыв в день (нет поддержки множественных перерывов)
  • Время хранится как строка, не как Time type
  • Источник в коде: backend/app/models/catalog.py::Schedule

SR-003: Дефолтное расписание при регистрации бизнеса

  • Описание: При создании бизнеса автоматически генерируется расписание
  • Условие: POST /auth/register
  • Действие:
  • 7 записей (0-6) с master_id=NULL
  • Пн-Пт (0-4): is_active=True, 09:00-18:00, перерыв 13:00-14:00
  • Сб-Вс (5-6): is_active=False, 09:00-18:00, перерыв 13:00-14:00
  • Исключения:
  • Все бизнесы получают одинаковый дефолт (нет учёта часового пояса)
  • Дефолтное расписание мастерам НЕ создаётся при регистрации
  • Источник в коде: backend/app/api/v1/endpoints/auth.py::register() (строки 228-243)

SR-004: Дефолтное расписание мастера

  • Описание: Расписание мастера создаётся по запросу через API
  • Условие: POST /schedule/master/{master_id}
  • Действие:
  • 7 записей (0-6) с конкретным master_id
  • Дефолт: Пн-Пт 09:00-18:00, Сб-Вс off
  • Без перерыва (break_start_time=NULL, break_end_time=NULL)
  • Если расписание уже существует — возвращает пустой массив (не дублирует)
  • Исключения:
  • Нет перерыва в дефолте мастера (в отличие от бизнес-расписания)
  • Источник в коде: backend/app/api/v1/endpoints/schedule.py::create_master_schedule()

SR-005: Расчёт свободных слотов (per-master)

  • Описание: Для конкретного мастера на конкретную дату вычисляются busy intervals
  • Условие: GET /public/{slug}/slots?date=YYYY-MM-DD&master_id=XXX
  • Действие:
  • Получить расписание дня (мастер или fallback на бизнес)
  • Если day не active — вернуть work_start=00:00, work_end=00:00, busy=[]
  • Получить все bookings мастера на эту дату (кроме cancelled/rejected)
  • Собрать busy intervals: перерыв + все записи
  • Merge overlapping intervals
  • Вернуть: {work_start, work_end, busy_intervals}
  • Исключения:
  • Фронтенд сам вычисляет доступные слоты из (work_start..work_end) минус busy_intervals
  • Бэкенд НЕ генерирует список конкретных слотов
  • Источник в коде: backend/app/services/availability.py::get_busy_intervals()

SR-006: Агрегированные слоты (любой мастер)

  • Описание: Если мастер не выбран, слоты считаются по всем мастерам
  • Условие: GET /public/{slug}/slots?date=YYYY-MM-DD (без master_id)
  • Действие:
  • Получить всех мастеров бизнеса
  • Создать массив доступности [0..1440] (поминутно)
  • Для каждого мастера: +1 за рабочие минуты, -1 за перерыв, -1 за каждую запись
  • Busy = интервалы где count <= 0 (ни один мастер не свободен)
  • overall_start = min(start_time всех мастеров), overall_end = max(end_time)
  • Исключения:
  • Если нет мастеров — fallback на бизнес-расписание (только перерыв как busy)
  • O(masters * bookings) по сложности — может быть медленным при большом количестве
  • Источник в коде: backend/app/services/availability.py::get_aggregated_busy_intervals()

SR-007: Обновление расписания

  • Описание: Провайдер может менять расписание через админку
  • Условие: PUT /schedule/{id}
  • Действие:
  • Принимает как camelCase, так и snake_case поля
  • Поля: startTime, endTime, isActive, breakStartTime, breakEndTime
  • Авторизация: schedule.business_id == current_user.business_id
  • Исключения:
  • Нет валидации: start_time < end_time (можно поставить end раньше start)
  • Нет валидации: break внутри рабочих часов
  • Изменение расписания НЕ влияет на уже созданные записи
  • Источник в коде: backend/app/api/v1/endpoints/schedule.py::update_schedule()

SR-008: Удаление расписания мастера

  • Описание: Можно удалить всё расписание мастера (все 7 дней)
  • Условие: DELETE /schedule/master/{master_id}
  • Действие: DELETE FROM schedules WHERE master_id=X AND business_id=Y
  • Исключения:
  • После удаления мастер будет использовать бизнес-расписание (fallback)
  • Существующие записи не затрагиваются
  • Источник в коде: backend/app/api/v1/endpoints/schedule.py::delete_master_schedule()

SR-009: Праздники и исключения — НЕ реализованы

  • Описание: Нет системы праздничных дней или одноразовых исключений из расписания
  • Условие:
  • Действие: TODO: не реализовано
  • Исключения: Единственный способ закрыть день — поставить is_active=False для этого дня недели (но это закроет ВСЕ такие дни)
  • Источник в коде: Отсутствует

SR-010: Шаг слотов при переносе

  • Описание: При переносе через Telegram бот предлагает слоты с шагом 15 минут
  • Условие: Callback reschedule
  • Действие:
  • Бот показывает доступные часы (кнопки по 3 в ряд)
  • После выбора часа — показывает минуты: 00, 15, 30, 45
  • Проверяет доступность: слот должен помещаться в расписание и не пересекаться с busy minutes
  • Исключения:
  • Перенос возможен только на ту же дату (дата не меняется в Telegram flow)
  • Через API (POST /reschedule/{booking_id}) можно перенести на другую дату
  • Источник в коде: backend/app/services/telegram_handler.py::_handle_reschedule_init()