Два человека увидели свободные 14:00 и нажали «Записаться». Оба получили поздравление. Администратор обнаружил проблему, когда оба пришли. В графике свободным был слот, а не право каждого из двух интерфейсов продать его независимо. Когда информация и решение разделены, зелёная кнопка легко обгоняет бизнес.
Чтобы онлайн-запись не продавала один слот дважды, все каналы должны подтверждать бронь через одно правило для конкретного ресурса и периода. Список свободных часов — предварительная информация. При подтверждении проверьте актуальное состояние и создайте запись в одной защищённой операции. Клиенту показывайте успех только после подтверждённой записи; при конфликте предлагайте свежие варианты.

В чём ошибка между «вижу» и «занял»
Условный кабинет работает с шагом 30 минут. В 13:51 клиент на сайте открыл расписание. В 13:52 администратор получил запрос по телефону и записал другого человека на 14:00. Клиент сайта завершает форму в 13:53. Если сайт сверял доступность только при показе, появятся две записи. Если сайт повторно проверяет перед сохранением, вероятность уменьшается, но остаётся короткое окно, когда две операции проверили одно состояние и обе записали результат.
Правило должно звучать так: для ресурса «кабинет А» и периода 14:00–14:30 нельзя сохранить две одновременно активные записи. При конкуренции одна операция выигрывает, вторая получает понятный результат «время уже занято». Клиенту предложите ещё два или три доступных времени, сохранив введённые данные. Повторить форму с нуля — наказание за ваш же проект расписания.
Если форма записи уже превратилась в несколько каналов и календарей, на странице разработки приложений 404ai можно посмотреть подход к приложениям для заказов и записи. На обсуждение полезно принести не скриншот календаря, а перечисление мест, откуда реально можно занять одно и то же время.
Что именно считается ресурсом

Иногда уникальное место определяется сотрудником. Иногда для встречи нужны одновременно сотрудник и кабинет. Для монтажа нужны бригада, время в дороге и набор инструмента. Ошибка начинается, когда владельцы этих ресурсов ведут независимые календарики. Внешний вид расписания может остаться разным, но должно быть одно правило подтверждения для нужного набора.
Опишите длительность и буфер. Для услуги 14:00–15:00 с подготовкой до 15:15 нельзя продать 15:00 тому же ресурсу, хотя визуально блок услуги закончился. Для консультаций без подготовки соседние интервалы 14:00–14:30 и 14:30–15:00 допустимы. Согласуйте, входят ли конечные границы в интервал. Удобное представление — начало входит, конец нет. Это не универсальный бизнес-закон, просто выбранный способ сделать соседние записи однозначными.
Есть и вместимость. На групповое занятие можно записать восемь человек, поэтому девятая бронь конфликтует, а вторая нет. Для общего автомобиля некоторые назначения допустимо сочетать только по маршруту. Попытка решить всё ограничением «один слот — один человек» может оборвать половину нормального бизнеса. Начинайте с описания права на ресурс, а не с выбора библиотеки календаря.
| Услуга | Ресурс | Интервал | Условие конфликта |
|---|---|---|---|
| Консультация | Специалист | Разговор и буфер | Две активные записи пересекаются |
| Осмотр | Кабинет и врач | Оба ресурса на весь визит | Не хватает одного из двух |
| Занятие | Группа | Один запуск | Исчерпана вместимость |
| Выезд | Бригада | Работа плюс переезд | Недостаточно времени между адресами |
| Аренда | Конкретный предмет | Выдача, пользование, приёмка | Следующий старт раньше приёмки |
Предварительная доступность и подтверждение — разные ответы
Документация Google Calendar Freebusy описывает получение занятых интервалов. Отдельная операция создания события записывает событие. Из того, что чтение показало свободное окно, не следует, что последующая запись защищена от параллельной работы в вашем приложении. Если внешний календарь выбран системой истины, проверяйте его гарантии и способ интеграции, а не предполагаемую логику интерфейса.
Например, в PostgreSQL диапазоны и ограничения исключения позволяют запретить определённые пересечения. Это один из вариантов для системы с собственной базой. Такой механизм должен учитываться при обновлении брони, снятии удержания и операциях с несколькими ресурсами. Для вместимости может понадобиться транзакционное изменение доступного остатка. Технологический пример не заменяет правило, какие состояния считаются активными.
Подтверждение брони включает минимум три факта: уникальную запись, конкретный ресурс и итоговое состояние. SMS, письмо и событие во внешнем календаре лучше считать отдельными побочными эффектами. Если письмо не ушло, сама бронь может существовать. Если экран показал успех, но бронь не записалась, это другой инцидент. Уведомления должны повторяться без повторной записи слота.
Когда полезно временно удерживать слот

Удержание нужно, когда человеку требуется время на оплату или подтверждение документов. При простой записи без этих шагов оно может быть лишней задержкой. Удержание имеет срок, принадлежит определённому запросу и занимает ресурс по тому же правилу, что активная запись. Пока оно действует, другой канал не должен показать это время как свободное для подтверждения.
Представим удержание на пять минут. Клиент начал оплату на четвёртой минуте, ответ пришёл на седьмой. К этому времени слот мог получить кто-то другой. Выберите политику до запуска: например, не подтверждать истёкшее удержание автоматически, а предлагать новый слот или корректно разбирать платеж. Этот крайний случай не должен отрабатываться решением администратора на эмоциях.
При истечении срока не полагайтесь только на красивый таймер в браузере. Серверу важно знать реальное состояние в момент записи. Даже если уборка устаревших удержаний запускается по расписанию, логика подтверждения должна однозначно решить, участвует ли истёкшая запись в конфликте. Таймер полезен для объяснения клиенту, но не является источником истины.
Что делать при слабой сети и повторном нажатии
После клика телефон потерял связь, и человек не увидел результат. Он попробует ещё раз. Повтор той же операции должен вернуть ранее подтверждённую бронь либо текущий статус, а не записать вторую услугу. Используйте устойчивый идентификатор попытки бронирования, связывайте его с содержанием запроса и понятным сроком действия. Не делайте идентификатором один только номер телефона: этот же человек может бронировать два независимых визита.
В iCalendar RFC 5545 определены устойчивые идентификаторы компонентов и другие свойства календарных записей. Для взаимодействия со своим интерфейсом понадобится внутренний идентификатор операции, который переживает повтор запроса. Идентификатор календарного события и идентификатор попытки решают разные задачи. Первый помогает связать существующие записи, второй помогает не создать лишнюю при повторе.
Покажите состояние «проверяем запись» там, где итог неизвестен. На экране не должно быть одновременно зелёного поздравления и окошка «произошла ошибка». Если операция подтверждена, дайте номер и способ проверить запись. Если ответ задержан, верните безопасный путь найти её без звонка администратору. Если слот не достался, покажите варианты без обещания, что они не изменятся до клика.
Как обрабатывать перенос и отмену

У переноса есть ловушка: старую запись удалили, новую ещё не создали, а время уже заняли. У клиента не осталось ни одного визита. При возможности подтверждайте новый интервал и освобождайте прежний в одной операции. Если процессы разделены между системами, не скрывайте промежуточный статус и предусмотрите явное восстановление.
При отмене переведите запись в конечное состояние с причиной, временем и правом, по которому она произошла. Не удаляйте историю, если она нужна для сверки или поддержки. Повторное уведомление о той же отмене не должно повторно предлагать освободившееся время всем участникам. У листа ожидания своя очередь, а у каждой отправки — собственный результат. Если нужен этот сценарий, отдельно разберите лист ожидания клиентов.
Приёмка записи на разных каналах
Два клиента нажали одновременно
Оба видят 14:00 и отправляют форму с двух устройств. Время доступно в обоих экранах до отправки.
- Что проверить
- Проверьте попытки записи в журнале подтверждений и точные идентификаторы ресурса. Ответы интерфейсов не заменяют состояние базы.
- Решение
- В рамках одного ресурса разрешите одну активную бронь. Другому клиенту верните конфликт и актуальные времена.
- Ожидаемый результат
- В журнале одна подтверждённая запись, два понятных итога, ни одного «успеха без записи».
Администратор и сайт бронируют параллельно
Сотрудник вводит запись по телефону, сайт подтверждает такое же время для другого клиента.
- Что проверить
- Найдите все каналы записи и проверьте, проходят ли они через общее правило. Проверьте в том числе ручные изменения.
- Решение
- Подключите администратора к источнику подтверждения. Если ручной обход нужен, назначьте отдельное полномочие и реакцию на конфликт.
- Ожидаемый результат
- После теста в двух каналах не появляется независимых записей. В интерфейсе администратора доступна причина отказа.
Подтверждение пришло после истечения удержания
Платёж дошёл позже, время уже занял второй покупатель. На обеих сторонах отображается завершённая операция, но слот один.
- Что проверить
- Сопоставьте момент истечения, время подтверждения оплаты и право на ресурс. Не выводите владение слотом из одного факта оплаты.
- Решение
- Сохраните подтверждённую бронь второго. Для первого запустите согласованный разбор платежа и новый подбор времени.
- Ожидаемый результат
- После воспроизведения конфликт виден владельцу процесса, клиент получает понятный исход, существующая запись не уничтожается.
Внешний календарь не отвечает
Бронь записалась во внутренней системе, календарь специалиста ещё не обновился.
- Что проверить
- Определите, где источник истины и что означает неподтверждённое внешнее обновление. Проверьте видимость нового дела в рабочем интерфейсе.
- Решение
- Повторите синхронизацию по устойчивому ключу. Не создавайте бронь заново и не открывайте вторую запись на то же время.
- Ожидаемый результат
- Когда внешний канал вернулся, появляется одно связанное событие. Возможность выполнить бронирование не зависит от количества повторов доставки.
Перенос не удался
Клиент пытается перенести визит с 13:30 на 14:00, но новый слот успевают занять.
- Что проверить
- Посмотрите состояния обеих записей и то, в каком порядке изменения сохранились. Проверка доступа к новому времени сама ничего не меняет.
- Решение
- Оставьте старый визит подтверждённым и предложите другое время. Снимайте старую запись после принятого переноса.
- Ожидаемый результат
- Клиент видит, что старый визит остаётся в силе. Уведомление об отмене не уходит без отмены.
Свободен специалист, но занят кабинет
Пользователь выбрал врача. Кабинет закрепили за другой услугой, а приложение выводит время как свободное.
- Что проверить
- Проверьте требования услуги и одновременную доступность каждого ресурса на весь интервал, включая буфер.
- Решение
- Предлагайте только сочетания, которые можно подтвердить целиком. При конкуренции ни один ресурс не остаётся зависшим без общей записи.
- Ожидаемый результат
- При тестовом конфликте у врача нет фантомной брони. Пользователь видит другое время или допустимый кабинет.
Как мигрировать к одному источнику без двойной работы
Если администраторы привыкли менять внешний календарь напрямую, не переключайте запись внезапно. Сначала перечислите реальные способы изменить расписание: сайт, внутренний экран, интеграция с партнёром, импорт, ручное редактирование. Отметьте, кто отвечает за каждый способ. Потом выберите маршрут подтверждения. Для двух независимых систем, которые обе могут принимать окончательную бронь, задержка синхронизации неизбежно оставляет окно конфликта.
На переходном этапе можно ограничить часть ресурсов по каналам. Например, половина часов подтверждается сайтом, остальные — администратором. Это снижает доступность и не является конечным решением, зато позволяет проверить новый путь без риска двойных обещаний. Следующий шаг — перевести ручную запись на общий маршрут и разрешить внешний календарь как представление или строго управляемый источник.
Не забывайте про старые записи и повторяющиеся события. Перед переключением сделайте сверку на ближайший период: ресурс, время, состояние, происхождение. Не сливайте дубли автоматически, если могли записать разных людей. Спорные строки покажите координатору, прежде чем сайт начнёт считать время свободным. Первые дни после переключения держите видимый журнал расхождений и возможность быстро остановить приём новых броней по проблемному ресурсу.
Что показать клиенту, который не получил выбранное время
Сообщение «Ошибка 409» годится для журнала, но не для человека. Скажите, что выбранное время заняли, сохраните ранее введённые данные и предложите обновить расписание. Покажите варианты по близости: тот же специалист позже, другой специалист в то же время, другая площадка — только если услуги действительно сопоставимы. У клиента должна быть возможность понять разницу до выбора.
Если новых вариантов нет, предложите рабочий маршрут: лист ожидания либо связь с администратором. Укажите, что произойдёт дальше. «Оставьте заявку, мы запишем» нельзя обещать, если запись зависит от освободившегося места. Лучше «сообщим, когда появится время» там, где процесс действительно проверяет доступность. Сделайте отмену и возврат к форме предсказуемыми и с клавиатуры, особенно на телефоне.
Для человека с уже подтверждённой записью должен быть отдельный экран или письмо с датой, зоной и идентификатором. Если уведомление задержалось, интерфейс может сказать «запись подтверждена, письмо ещё отправляется». Это намного полезнее, чем один красный индикатор для сбоя письма и несохранённой брони.
Что покажет экономика ошибок
Посчитайте подтверждённые брони, конфликтные попытки и реальные повторные записи отдельно. Рост числа конфликтных попыток после введения защиты может быть хорошей новостью: раньше коллизии проходили незамеченными. Владелец видит, что стало известно, сколько клиентов встретили конфликт, сколько из них выбрали другое окно и сколько ушли. Чтобы улучшить опыт, смотрите последнюю группу, а не ослабляйте проверку.
Условная смена: 80 успешных броней, семь конфликтов, у пяти клиентов состоялась новая попытка, у четырёх есть подтверждённый визит. Понадобится разобрать три незавершённых сценария: двое не пробовали снова, один не получил следующее окно. Если дополнительное время исчезло при повторе, улучшите свежесть предложения. Если человек не заметил понятного пути, работайте с сообщением. Все семь конфликтов пропускать нельзя, даже если хочется получить красивую конверсию.
Соберите четыре следа для любого инцидента: когда видели слот, когда попытались занять, когда получили результат, когда действительно появилась запись. По ним можно отличить поломку обмена от нормальной конкуренции. Добавьте нагрузочный прогон в безопасной среде и отдельный сценарий с медленным телефоном. В многоканальной записи это дешевле, чем публичное выяснение, кого из двух клиентов сегодня обслужат.
Где нужен дополнительный разбор
Для событий на стыке регионов используйте правила часовых поясов онлайн-записи. Для общения после записи пригодится подтверждение и напоминание. Смысл этого материала другой: сделать факт владения временем достоверным в момент конкуренции.
Посмотрите на логику как на обещание магазина. Сайт может показать товар на полке двоим. Продать последнюю единицу он может одному. У записи тот же принцип, только товар нельзя вернуть на полку вчерашним числом. Проверяйте событие подтверждения раньше, чем тратить деньги на очередной слой напоминаний.