Представьте небольшую студию с одним процедурным кабинетом и двумя специалистами: Анной и Борисом. У каждого свой календарь, свои рабочие часы и своя привычка забывать вносить личные дела. Клиентка выбирает услугу на вторник, 15:00, и получает подтверждение. Через минуту другой клиент выбирает ту же услугу на то же время, но уже у Бориса, и тоже получает подтверждение. Оба специалиста по расписанию свободны. Кабинет один. Каждый календарь по отдельности говорит правду, а запись в целом оказывается ошибочной.
Ошибка здесь не в «глупом» сервисе, а в неверной модели занятости. Свободное время человека и свободное время услуги — разные величины. Услуга требует нескольких вещей одновременно: клиента, специалиста и помещения. Пока система видит только первого и второго, третий остаётся за кадром. Эта статья о том, как разложить такую задачу на части, где в Cal.com работают календари и расписания, какие вопросы придётся задать про редакцию продукта и как проверить конкурирующие бронирования без единого настоящего клиента.
Выбрать человека или распределить запись: две разные задачи
Слова «запись к нескольким специалистам» прячут два принципиально разных сценария.
В первом клиент сам выбирает исполнителя. Он хочет именно к Анне, потому что ходил к ней раньше. Тогда у каждого специалиста фактически своя страница записи со своим расписанием, и конфликты между людьми невозможны по определению: Анна занята только Анной. Конфликт появляется лишь с общим ресурсом, который эти страницы друг о друге не знают.
Во втором сценарии клиенту всё равно, кто его примет. Ему нужна услуга «в четверг после работы». Система должна сама решить, кого назначить, и показать клиенту объединённую доступность: слот есть, если свободен хотя бы один подходящий специалист. Здесь уже нужна логика распределения, а не просто набор личных страниц.
Есть и третий вариант, о котором часто забывают: услуга, для которой нужны сразу несколько людей. Например, консультация вдвоём — врач и ассистент. Тогда слот доступен, только когда свободны все участники одновременно. Это не распределение, а пересечение расписаний.
Эти три модели ведут себя по-разному, и путать их опасно. Если вам нужен «любой свободный», а вы настроили независимые страницы, клиенты будут видеть только расписание одного человека и записываться мимо остальных. Если нужны «все сразу», а настроено «любой», получится запись, на которую придёт половина необходимых людей.
В FAQ Cal.com collective events и round-robin отнесены к командным возможностям. Это название двух моделей: первая — про общее участие, вторая — про поочерёдное распределение. Но та же страница не доказывает, что эти функции есть в каждой редакции продукта. К этому вопросу мы вернёмся ниже, потому что от него зависит, стоит ли вообще начинать с настройки.
Чьи календари участвуют в расчёте занятости
Теперь о том, откуда сервис берёт сведения о занятости. Здесь важно понять устройство, а не заучить кнопки.
По FAQ Cal.com, настройки типа события включают расписание доступности и календарь, в который добавляется бронирование. Это два разных слоя. Расписание отвечает на вопрос «в какие часы человек вообще принимает». Календарь отвечает на вопрос «что у него уже занято из внешних источников» и куда записать новую встречу. Свободный слот — это рабочие часы за вычетом занятого.
Календарь назначения, как сказано там же, можно выбрать на уровне отдельного события или глобально, а дополнительные календари подключаются через настройки и интеграции. Из этого следуют практические вещи.
- Если Анна подключила только рабочий календарь, а личный приём у стоматолога лежит в другом, сервис о нём не узнает и предложит это время клиентам. Подключённые календари нужно считать частью настройки, а не необязательным украшением.
- Если календарь назначения выбран глобально, новая запись окажется там, где вы её не ждёте — например, в личном календаре вместо рабочего. А если выбран для конкретного события, разные услуги можно вести в разные календари.
- Расчёт занятости видит только то, что есть в подключённых календарях. Кабинет, у которого нет собственного календаря, для него не существует.
Последний пункт и есть корень нашей истории с двойной бронью. Помещение нигде не записано, поэтому никто его не охраняет.
Кабинет как участник, у которого нет лица
Самый простой способ привести ресурс в систему — завести ему собственный календарь и считать его участником, пусть и бессловесным. Назовём календарь «Кабинет 1». Каждая запись на процедуру должна попадать в него, а доступность услуги — вычитать из расписания людей занятость кабинета.
Тут нужна оговорка, которую нельзя пропускать. Вы собираетесь полагаться на то, что сервис умеет добавлять в расчёт ещё один календарь как обязательного участника события. Работает ли это именно так в вашей редакции и в вашей модели распределения, документация, на которую мы опираемся, не говорит. Это нужно проверить руками, и способ проверки описан в последнем разделе.
Есть и бытовой запасной вариант, который не зависит от возможностей продукта. Если кабинет один, а специалистов двое, можно сделать так, чтобы они вообще не принимали одновременно: разделить недельное расписание на смены. Анна принимает по понедельникам, средам и пятницам, Борис — по вторникам и четвергам. Конфликта нет по построению, но платить за это приходится пропускной способностью: кабинет простаивает, когда специалист в смене не загружен. Для маленькой студии это иногда честная цена спокойной жизни.
Пример: один кабинет, два специалиста, один вторник
Возьмём условный пример: все числа здесь выдуманы для расчёта. Процедура длится 60 минут. Анна работает во вторник с 10:00 до 18:00, Борис — с 12:00 до 20:00. Кабинет открыт с 10:00 до 20:00.
Если смотреть только на людей, слоты у клиентов такие: у Анны с 10 до 18, у Бориса с 12 до 20. Для услуги «любой специалист» объединение даёт доступность с 10:00 до 20:00. Выглядит прекрасно. Но с 12:00 до 18:00 обе записи в одну минуту могут оказаться в одном кабинете.
Теперь добавим кабинет как ограничение. В часы, когда работают двое, пропускная способность услуги — не два клиента, а один, потому что помещение одно. Реальная емкость определяется минимумом из трёх величин: числа свободных специалистов, числа свободных кабинетов и числа клиентов, которых вы готовы принять. Двойная бронь рождается там, где модель считает по первой величине, а жизнь управляется второй.
| Время | Свободны специалисты | Свободен кабинет | Реально можно принять |
|---|---|---|---|
| 10:00–12:00 | только Анна | да | 1 |
| 12:00–18:00 | Анна и Борис | да, один | 1 |
| 18:00–20:00 | только Борис | да | 1 |
Таблица показывает неприятную арифметику: второй специалист в середине дня даёт не вторую запись, а возможность выбора. Он повышает шанс найти клиенту удобное время, но не увеличивает число процедур. Если вы хотите, чтобы он приносил больше записей, нужен второй кабинет. Вот почему вопрос о ресурсах стоит решать до выбора инструмента.
Часовые пояса: одна запись, три разных времени
Пока клиент и кабинет в одном городе, часовые пояса кажутся чужой проблемой. Но стоит студии принимать онлайн, или специалисту уехать в другой регион, как одна запись начинает существовать в нескольких временах одновременно.
Проведём одну запись через эту цепочку. Клиентка живёт в Новосибирске, кабинет и Анна — в Москве. Клиентка видит слот «вторник, 19:00» в своём часовом поясе. В Москве это 15:00. Внутри системы корректнее всего хранить момент времени в универсальном виде, а локальные часы вычислять только для показа. Если хранить «19:00» без привязки, то при любом дальнейшем действии — переносе, напоминании, выгрузке в календарь — возникнет разночтение.
Теперь перенос. Клиентка просит сдвинуть запись на следующий вторник. Между двумя датами вполне может лежать переход на летнее или зимнее время, если в одной из сторон оно применяется. Тогда разница между двумя поясами изменится на час, и «такое же время» у клиента и у специалиста перестанет означать одно и то же. Правильное поведение — сохранить намерение, которое было заявлено. Если клиентка сказала «в 19:00 по моему времени», нужно решить заранее, что для вас первично: часы на стене у клиента или часы у исполнителя. Любой ответ допустим, плохо только его отсутствие.
Для самостоятельных проверок удобно иметь надёжный способ считать такие переходы. Модуль zoneinfo в Python, как сказано в его документации, использует базу часовых поясов IANA и учитывает переходы; данные берутся из системы или из пакета tzdata. Это значит, что ожидаемые значения для тестов можно рассчитать независимо от самого сервиса записи и сравнить с тем, что он показывает. Совпадение результатов — хороший знак, расхождение — повод разбираться.
from datetime import datetime
from zoneinfo import ZoneInfo
moscow = ZoneInfo("Europe/Moscow")
nsk = ZoneInfo("Asia/Novosibirsk")
slot = datetime(2026, 11, 3, 15, 0, tzinfo=moscow)
print(slot.astimezone(nsk))Сам по себе этот скрипт ничего не доказывает про Cal.com. Он нужен как независимая шпаргалка: вы заранее знаете, что должен увидеть клиент, и сверяете это с экраном. Дату для проверки переходов стоит брать ту, где в одной из выбранных зон действительно меняются часы, иначе тест окажется слишком ровным.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.
Date Overrides: когда обычного расписания недостаточно
В жизни студии постоянно случаются исключения: праздничный день, внеплановое обучение, один вечер, когда Борис принимает дольше. Для этого в Cal.com есть Date Overrides. Как объясняется в материале Cal.com о них, они задают отдельные дни и часы доступности и дополняют обычное расписание и синхронизированные календари. Там же сказано, что изменение override применяется к событиям, которые используют выбранное расписание.
Второе предложение на самом деле важнее первого. Расписание в Cal.com — отдельная сущность, и несколько событий могут ссылаться на одно. Исправив исключение в расписании Анны, вы изменяете доступность сразу всех услуг, привязанных к нему. Это удобно, когда нужно закрыть всё сразу, и опасно, когда вы хотели поправить одну услугу, а затронули пять.
Для нашей задачи отсюда вытекает практическое правило. Исключения касаются людей, а не помещения. Если кабинет закрыт на уборку или ремонт, надёжнее отразить это там, где живёт сам кабинет, — например, блокирующей записью в его календаре, если он у вас есть. Иначе придётся не забыть поставить override каждому специалисту, и рано или поздно кто-то забудет.
Что решает редакция продукта
Теперь о том, что стоило бы выяснить до всякой установки. Cal.com — это не один, а несколько вариантов одного семейства, и возможности у них разные.
В актуальном README репозитория calcom/cal.com на дату нашей проверки описан Cal.diy — community self-hosted edition, то есть сообщественная версия для самостоятельного размещения. Разработчик прямо рекомендует её для личного и непромышленного использования. Для коммерческой и корпоративной инфраструктуры там указаны другие пути: hosted Cal.com или доступ к enterprise on-prem. Это позиция авторов проекта, а не результат нашего испытания.
Из этого следуют два вывода, которые легко потерять, если читать старые инструкции по установке.
- Коммерческий продукт Cal.com и свободная сборка для самостоятельного размещения — не синонимы. Нельзя считать, что всё, о чём рассказывают в материалах о Cal.com, автоматически есть в Cal.diy. README прямо предупреждает об этом: коммерческие функции не переносятся на свободную сборку сами собой.
- Командные функции, о которых мы говорили в начале, — collective и round-robin — FAQ относит к командным возможностям. Но та страница не утверждает, что они присутствуют во всех self-hosted редакциях. А отсутствие утверждения — не доказательство отсутствия. Единственный честный способ — проверить конкретную сборку, которую вы собираетесь использовать.
Для студии с реальными клиентами это означает, что решение «ставим на свой сервер» нужно принимать не из соображений цены, а после ответа на вопрос: на какой редакции и с какими условиями мы собираемся принимать платные записи. Если ответ — hosted-версия или корпоративная поставка, то вопрос установки отпадает, а остаётся только настройка. Если же вы изучаете Cal.diy для личных целей, обучения или пробной среды, то сервер под него — разумная площадка для экспериментов.
Для такого пробного стенда подходит обычный VPS. На VPS MAATRIX есть NVMe, процессоры AMD EPYC и root-доступ, а тарифы начинаются с конфигурации Ba — 1 vCPU, 2 ГБ RAM и 40 ГБ NVMe; более крупные конфигурации — Uab, Heka, Maat и Djed — дают больше ресурсов. Это ресурсы, а не обещание ёмкости, поэтому нагрузку под реальные записи всё равно нужно мерить. Подробности о развёртывании приложения — в отдельной статье про установку и настройку Cal.com на VPS; её стоит читать с оглядкой на то, какая именно редакция сейчас описана в README, и не воспринимать как рекомендацию для коммерческой работы.
Как проверить конкурирующие бронирования без клиентов
Теперь самое практичное: как убедиться, что двойной брони нет, не приглашая живых людей. Идея проста — построить тестовую студию, в которой всё выдумано, а затем намеренно создать конкуренцию.
Тестовая студия
Заведите двух тестовых специалистов с отдельными календарями, один календарь для кабинета и услугу на 60 минут. Расписания сделайте пересекающимися, как в нашем примере: один работает 10–18, другой 12–20. Адреса почты используйте свои или служебные, чтобы уведомления не уходили посторонним. Пока не настроите всё, не публикуйте страницу записи наружу.
Сценарии, которые нужно прогнать
- Одновременность. Откройте страницу записи в двух окнах браузера, выберите один и тот же слот и подтвердите почти одновременно. Ожидаемый результат — одна запись и один понятный отказ. Если подтвердились обе, значит, ресурс не учитывается или проверка занятости срабатывает слишком поздно.
- Занятый кабинет, свободные люди. Создайте запись у Анны, затем попробуйте записаться к Борису на то же время. Если слот всё ещё предлагается, кабинет не участвует в расчёте.
- Внешний календарь. Добавьте событие в подключённый календарь специалиста вручную и убедитесь, что слот исчез. Затем добавьте событие в календарь, который не подключён, и проверьте, что слот остаётся: так вы увидите границу того, что сервис знает.
- Перенос через часовые пояса. Запишитесь из браузера с другим часовым поясом, затем перенесите запись на дату после перехода на зимнее или летнее время. Сравните результат с расчётом в zoneinfo.
- Исключение. Задайте Date Override на один день у одного специалиста и проверьте, что изменились только те услуги, которые используют это расписание.
- Отмена. Отмените запись и убедитесь, что слот вернулся и у человека, и у кабинета, а не только в одном из календарей.
Запоминайте не только итог, но и момент, когда сервис сообщил об отказе: до подтверждения или после. Для клиента это разница между «время занято, выберите другое» и «мы вам отказали после того, как вы уже получили письмо».
Как читать результаты
Если ни один сценарий не дал дубля — это хороший признак для вашей конфигурации и объёма проверки, но не гарантия вообще. Гонки редки и зависят от нагрузки, поэтому нагрузочный тест с десятками одновременных клиентов в этой статье мы не описываем и результатов никаких не обещаем. Если же дубль получился, не торопитесь винить продукт: в половине случаев причина в том, что календарь кабинета подключён не к тому событию или не отмечен как проверяемый.
И ещё одно наблюдение, которое экономит время: тестируйте на той самой редакции, на которой собираетесь работать. Результат, полученный на hosted-версии, ничего не говорит о Cal.diy, и наоборот. Командные функции могут быть в одной и отсутствовать в другой, и это нормальный ответ на вопрос выбора, а не сбой теста.
Три согласования для одной записи
Вернёмся в кабинет, с которого мы начали. Запись состоялась, если совпали три вещи. Клиент согласился со временем — и понимает его в своём часовом поясе. Специалист свободен — не только по рабочему расписанию, но и по всем календарям, где у него могут быть дела. И кабинет свободен — а значит, у помещения есть собственное место в расчёте, пусть даже в виде простого календаря.
Две из трёх проверок умеет делать любой планировщик. Третья требует, чтобы вы сами сказали системе, что помещение тоже участник. Поэтому прежде чем выбирать редакцию, сервер или тариф, разложите услугу на людей и ресурсы, проверьте это на тестовой студии с конкурирующими записями — и только потом пускайте настоящих клиентов. Тогда вторник в 15:00 окажется свободным одновременно для клиента, для специалиста и для кабинета, а не только для одного из них.