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

Записи (Booking)

Управление записями клиентов. Нативная запись включена (NATIVE_BOOKING_ENABLED=true).

Модель в коде: booking.Appointment
Лог изменений статуса: booking.AppointmentStatusLog


Жизненный цикл записи

Каждая запись проходит через статусы:

┌─────────┐
создание │ booked │
─────────────────>│ забронир│
└────┬────┘
│ администратор подтверждает

┌─────────────┐
│ confirmed │
│ подтверждена│
└─────┬───────┘
│ клиент пришёл

┌─────────────┐
│ in_progress │
│ идёт приём │
└─────┬───────┘
│ услуга завершена

┌─────────────┐
│ done │
│ завершена │
└─────────────┘

(из booked / confirmed)

│ отмена

┌──────────────┐ ┌──────────┐
│ cancelled │ │ no_show │
│ отменена │ │ не пришёл│
└──────────────┘ └──────────┘
СтатусОписаниеПереход назад
bookedЗапись создана, ожидает подтверждения
confirmedПодтверждена администраторомcancelled
in_progressКлиент пришёл, услуга оказываетсянет
doneВизит завершённет
cancelledОтменена клиентом или администраторомнет
no_showКлиент не пришёл без предупреждениянет
примечание

Каждое изменение статуса фиксируется в AppointmentStatusLog с полями old_status, new_status, user_id, note, created_at.


Источники записи

sourceОткуда пришла запись
journalСоздана вручную администратором через журнал
onlineОнлайн-запись клиентом через форму
import_yclientsИмпортирована из YClients
import_dikidiИмпортирована из Dikidi

Логика проверки свободных слотов

При запросе /api/records/available-slots/ сервер:

  1. Берёт расписание мастера (StaffSchedule / StaffScheduleDay)
  2. Исключает выходные и отгулы (StaffTimeOff)
  3. Загружает все активные записи на эту дату (статусы booked, confirmed, in_progress)
  4. Вычитает занятые интервалы с учётом длительности услуги (MasterService.duration_minutes)
  5. Возвращает только окна, в которые влезает вся длительность до конца рабочего дня
Запрос: date=2026-08-05, service_id=12 (длительность 60 мин)

Рабочий день мастера: 10:00 – 18:00
Занятые слоты: 11:00–12:00, 14:00–15:30

Свободные окна: 10:00, 12:00, 15:30, 16:30, 17:00
(17:30 не попадает — до конца дня остаётся < 60 мин)

Список записей

GET /api/records/
Authorization: Bearer <token>

Параметры фильтрации:

ПараметрТипОписание
datedateДата записи (YYYY-MM-DD)
date_fromdateС даты
date_todateПо дату
statusstringbooked, confirmed, in_progress, done, cancelled, no_show
client_idintegerID клиента
master_idintegerID мастера
service_idintegerID услуги

Ответ:

{
"count": 25,
"next": null,
"previous": null,
"results": [
{
"id": 1001,
"client": {
"id": 42,
"name": "Иван Иванов",
"phone": "+79991234567"
},
"master": {
"id": 5,
"name": "Анна Мастерова"
},
"service": {
"id": 12,
"name": "Стрижка женская",
"duration_minutes": 60,
"price": "1500.00"
},
"date": "2026-08-05",
"time": "14:00:00",
"status": "confirmed",
"source": "online",
"yclients_id": 987654,
"created_at": "2026-07-30T10:15:00Z"
}
]
}

Создать запись

POST /api/records/
Authorization: Bearer <token>

{
"master_id": 5,
"service_id": 12,
"date": "2026-08-05",
"time": "14:00",
"comment": "Первый визит"
}

Логика при создании:

  1. Проверяется, что слот свободен (конкурентная проверка через транзакцию БД)
  2. Создаётся Appointment со статусом booked
  3. Если YClients подключён — запись синхронизируется туда и создаётся YclientsBookingLog
  4. Клиенту отправляется уведомление (Telegram / WhatsApp)
  5. Статус может сразу стать confirmed в зависимости от SalonBookingSettings

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

КодПричина
409 ConflictСлот уже занят в момент создания
400 Bad RequestМастер недоступен в это время
400 Bad RequestДата в прошлом

Получить запись

GET /api/records/{id}/
Authorization: Bearer <token>

Отменить запись

POST /api/records/{id}/cancel/
Authorization: Bearer <token>

{
"reason": "Не смогу прийти"
}

Логика при отмене:

  1. Проверяется текущий статус — отмена доступна только для booked и confirmed
  2. Статус меняется на cancelled, фиксируется в AppointmentStatusLog
  3. Если запись была в YClients — отменяется через yclients-connector
  4. Клиенту и мастеру отправляется уведомление

Свободные слоты

GET /api/records/available-slots/
Authorization: Bearer <token>

Параметры:

ПараметрТипОбязательныйОписание
datedateДата
service_idintegerУслуга
master_idintegerКонкретный мастер (если не указан — слоты всех доступных мастеров)

Ответ:

{
"date": "2026-08-05",
"slots": [
{ "time": "10:00", "master_id": 3, "master_name": "Мария С." },
{ "time": "10:00", "master_id": 5, "master_name": "Анна М." },
{ "time": "11:30", "master_id": 5, "master_name": "Анна М." }
]
}