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

Клиенты

Модели в коде: clients.Client, clients.ClientPassport


Структура клиента

Основная модель Client

ПолеТипОписание
idintegerID клиента
salonFKПривязка к салону
first_namestringИмя
last_namestringФамилия
namestringПолное имя (вычисляемое)
phonestringТелефон (уникальный в рамках салона)
emailstringEmail
birth_datedateДата рождения
skin_typestringТип кожи
preferencesJSONПредпочтения клиента
risk_levelstringУровень риска клиента
notestextЗаметки администратора
total_visitsintegerКоличество завершённых визитов
last_visitdateДата последнего визита
return_ratefloatКоэффициент возвращаемости (0.0 – 1.0)
telegram_chat_idstringID чата в Telegram (для уведомлений)
is_whatsapp_warmed_upbooleanПрошёл ли icebreaker в WhatsApp
yclients_extrasJSONДополнительные поля из YClients
created_atdatetimeДата создания
updated_atdatetimeДата обновления

Паспорт клиента ClientPassport

Дополнительная медицинская / профессиональная карточка клиента, привязанная 1:1 к Client.

ПолеОписание
allergy_infoАллергии и противопоказания
hyperhidrosisГипергидроз (повышенное потоотделение)
preferred_materialsПредпочтительные материалы
nail_plate_featuresОсобенности ногтевой пластины
sensitivity_levelУровень чувствительности
ai_recommendationsAI-рекомендации по клиенту (генерирует Cindy)
history_summaryКраткая история визитов (генерируется AI)
visit_context_logЛог контекста визитов

Как считаются total_visits и return_rate

Поля пересчитываются в clients/visit_history.py при каждой синхронизации или завершении визита.

total_visits — количество Appointment со статусом "done"
(или импортированных YclientsRecord с соответствующим статусом)

return_rate — доля клиентов, пришедших повторно:
return_rate = (визиты после первого) / total_visits
Значение 0.0 — только один визит
Значение 1.0 — все визиты, кроме первого, повторные

При импорте из YClients эти поля обновляются в integrations/yclients_sync.py из исторических данных.


Алгоритм поиска клиентов

GET /api/clients/?search=... выполняет поиск по полям first_name, last_name, phone, name.

Особенности:

  • Поиск поддерживает транслитерацию: Иван найдёт Ivan и наоборот (реализовано в clients/yclients_profile.py)
  • Телефон нормализуется перед поиском (убираются пробелы, скобки, дефисы)
  • Поиск нечувствителен к регистру

Дедупликация при создании

При POST /api/clients/:

  1. Телефон проверяется на уникальность в рамках салона
  2. Если телефон уже существует — возвращается 400 Bad Request
  3. При импорте из YClients: если клиент с таким yclients_id уже есть — обновляются данные, дубль не создаётся
  4. Конфликт телефонов при синке YClients разрешается в пользу данных из YClients

Список клиентов

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

Параметры:

ПараметрТипОписание
searchstringПоиск по имени, телефону (с транслитерацией)
yclients_idintegerID в YClients
pageintegerНомер страницы
page_sizeintegerКол-во записей на странице

Ответ:

{
"count": 500,
"next": "https://api.salonai.ru/api/clients/?page=2",
"previous": null,
"results": [
{
"id": 42,
"first_name": "Иван",
"last_name": "Иванов",
"phone": "+79991234567",
"email": "ivan@example.com",
"birth_date": "1990-05-15",
"risk_level": "low",
"total_visits": 8,
"last_visit": "2026-07-15",
"return_rate": 0.875,
"telegram_chat_id": "123456789",
"is_whatsapp_warmed_up": true,
"yclients_id": 123456,
"created_at": "2025-01-10T12:00:00Z"
}
]
}

Создать клиента

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

{
"first_name": "Мария",
"last_name": "Петрова",
"phone": "+79998887766",
"email": "maria@example.com"
}

Получить клиента

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

История визитов клиента

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

Возвращает список всех записей клиента в формате GET /api/records/, отфильтрованный по client_id.