Как проверить занятость даты и избежать дублей при записи клиентов в 1С?

Программист 1С v8.3 (Управляемые формы) Сфера услуг, туризм и социальный сектор
← На главную

При разработке конфигураций для сферы услуг, бронирования или планирования задач программисты часто сталкиваются с необходимостью проверки доступности времени. Будь то запись к врачу, аренда автомобиля или бронирование переговорной комнаты — система должна гарантировать, что один и тот же ресурс не будет занят дважды на одно и то же время.

В этой статье мы подробно разберем, как реализовать надежную проверку занятости даты в среде 1С:Предприятие. Мы рассмотрим несколько подходов — от простых запросов до проектирования метаданных и работы с транзакционными блокировками, чтобы ваша система работала без сбоев даже при высокой нагрузке.

Решение 1. Программная проверка занятости даты с помощью запроса

Самый распространенный и очевидный способ проверки свободной даты — выполнение запроса к регистру сведений перед записью нового документа или элемента справочника. Давайте детально разберем этот метод.

Предположим, у нас есть регистр сведений ЖурналЗаписей, в котором хранятся данные о бронировании. Нам нужно проверить, свободен ли конкретный сотрудник на выбранную дату. Основная сложность здесь заключается в работе с типом данных Дата, который в 1С по умолчанию хранит и дату, и время (с точностью до секунды).

Если мы будем сравнивать даты напрямую на равенство, то запись на 12.10.2023 14:00 и на 12.10.2023 15:00 не будут считаться дублями. Если же нам нужно заблокировать весь день целиком, даты необходимо привести к началу дня. Проанализируем следующий пример кода:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ ПЕРВЫЕ 1
|	1 КАК Поле1
|ИЗ
|	РегистрСведений.ЖурналЗаписей КАК Т
|ГДЕ
|	НАЧАЛОПЕРИОДА(Т.ДатаЗаписи, ДЕНЬ) = НАЧАЛОПЕРИОДА(&ДатаЗаписи, ДЕНЬ)
|	И Т.Сотрудник = &Сотрудник";

Запрос.УстановитьПараметр("ДатаЗаписи", ДатаЗаписи);
Запрос.УстановитьПараметр("Сотрудник", Сотрудник);

Выборка = Запрос.Выполнить().Выбрать();

Если Выборка.Следующий() Тогда 
	ВызватьИсключение СтрШаблон(НСтр("ru='Сотрудник %1 уже занят на дату %2'"), Сотрудник, Формат(ДатаЗаписи, "ДФ=dd.MM.yyyy"));
КонецЕсли;

Разберем по шагам, почему этот код эффективен:

  1. Конструкция ВЫБРАТЬ ПЕРВЫЕ 1 оптимизирует работу СУБД. Нам не нужно выбирать все записи, достаточно узнать, существует ли хотя бы одна запись на эту дату. Как только база данных находит первое совпадение, выполнение запроса прекращается.
  2. Использование функции НАЧАЛОПЕРИОДА как для поля таблицы регистра, так и для параметра гарантирует, что сравнение произойдет по границе дня (то есть время сбрасывается в 00:00:00). Это исключает ошибки, когда запись произведена на разные секунды одного дня.
  3. В случае обнаружения записи мы используем метод ВызватьИсключение. Это прерывает текущую транзакцию записи документа, откатывает изменения и выводит пользователю информативное сообщение.

Решение 2. Архитектурный контроль уникальности через настройки регистра

Писать программные проверки в коде — гибкое решение, но платформа 1С предоставляет мощные встроенные механизмы контроля уникальности на уровне базы данных. Для этого мы можем использовать свойства периодических регистров сведений.

Давайте разберем, как настроить структуру регистра для автоматического контроля уникальности:

  1. Создадим регистр сведений ЖурналЗаписей.
  2. Установим свойство Периодичность в значение В пределах дня.
  3. Добавим ключевое измерение — Сотрудник.
  4. В качестве ресурсов или реквизитов укажем клиента и оказываемую услугу.

Как это работает: Платформа 1С строго контролирует уникальность записей в регистре сведений по комбинации его измерений и периода. Если периодичность регистра установлена "В пределах дня", то физически невозможно записать две строки с одинаковым значением измерения Сотрудник на одну и ту же дату.

При попытке записать дублирующую запись платформа выдаст системную ошибку нарушения уникальности. Этот способ является наиболее надежным, так как контроль происходит на уровне СУБД.

Однако здесь есть важный нюанс, связанный с режимом записи регистра:

Решение 3. Защита от параллельной работы пользователей (Гонка данных)

Представим ситуацию: два администратора одновременно открыли форму записи. У обоих на экранах дата свободна. Они одновременно нажимают кнопку "Записать".

Если использовать только простой запрос из первого решения, то в момент начала транзакции у обоих пользователей проверка покажет, что дата свободна (ведь запись еще не завершена ни у одного из них). В итоге в базе данных появятся две записи на одно и то же время, что приведет к конфликту.

Чтобы избежать этой проблемы, необходимо использовать механизм транзакционных блокировок. Выясним, как применить объект БлокировкаДанных перед выполнением проверочного запроса:


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЖурналЗаписей");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.УстановитьЗначение("Сотрудник", Сотрудник);

ДиапазонДаты = Новый Диапазон(НачалоДня(ДатаЗаписи), КонецДня(ДатаЗаписи));
ЭлементБлокировки.УстановитьЗначение("Период", ДиапазонДаты);

Блокировка.Заблокировать();

// Только после наложения блокировки выполняем запрос проверки занятости

Установив Исключительный режим блокировки на комбинацию сотрудника и диапазона дат, мы заставляем сеанс второго пользователя ожидать завершения транзакции первого пользователя. Если первый пользователь успешно запишется, транзакция второго пользователя при проверке запросом уже увидит занятую дату и выдаст ошибку.

Решение 4. Проверка пересечения временных интервалов

Если ваш бизнес-процесс требует записи не на весь день, а на определенные интервалы времени (например, сеанс массажа с 14:15 до 15:45), стандартная проверка на равенство дат не сработает. Нам нужно проверять пересечение интервалов времени.

Рассмотрим математическую модель пересечения двух отрезков времени. Пусть у нас есть существующая запись в базе с интервалом [ВремяНачалаСущ, ВремяОкончанияСущ] и новая запись [ВремяНачалаНов, ВремяОкончанияНов]. Они пересекаются тогда и только тогда, когда выполняется следующее условие:

ВремяНачалаНов < ВремяОкончанияСущ И ВремяОкончанияНов > ВремяНачалаСущ

Посмотрим на пример запроса, реализующего эту логику в 1С:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ ПЕРВЫЕ 1
|	1 КАК Поле1
|ИЗ
|	РегистрСведений.ЖурналЗаписей КАК Т
|ГДЕ
|	Т.Сотрудник = &Сотрудник
|	И Т.ВремяНачала < &ВремяОкончанияНов
|	И Т.ВремяОкончания > &ВремяНачалаНов";

Запрос.УстановитьПараметр("Сотрудник", Сотрудник);
Запрос.УстановитьПараметр("ВремяНачалаНов", ВремяНачалаНов);
Запрос.УстановитьПараметр("ВремяОкончанияНов", ВремяОкончанияНов);

Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
	ВызватьИсключение "Выбранное время пересекается с уже существующей записью!";
КонецЕсли;

Этот простой и лаконичный запрос надежно отсекает любые наложения времени — как частичные, так и случаи, когда один интервал полностью поглощает другой.

Решение 5. Скрытие занятых дат в интерфейсе пользователя

Для улучшения пользовательского опыта (UX) гораздо правильнее не выдавать ошибку после заполнения формы, а изначально не давать пользователю возможность выбрать занятые даты или услуги.

Для реализации этого поведения можно использовать динамические списки или событие НачалоВыбора на форме документа:

Решение 6. Правильное место для размещения кода проверки

Где именно писать код проверки занятости? Начинающие разработчики часто совершают ошибку, размещая эту логику в модуле формы документа (например, в обработчике кнопки "Записать").

Важное правило: Проверка занятости должна располагаться в Модуле объекта в обработчике ПередЗаписью или ОбработкаПроверкиЗаполнения. Почему это критично?

Решение 7. Повышение производительности и индексирование

Когда ваша база данных вырастет до десятков и сотен тысяч записей, скорость выполнения запросов проверки может существенно снизиться. Чтобы система бронирования продолжала работать мгновенно, необходимо оптимизировать структуру базы данных:

Подводя итог, мы проанализировали ключевые аспекты проверки дат в 1С. Комбинируя правильное проектирование регистров сведений, транзакционные блокировки для защиты от параллельной работы пользователей и оптимизированные запросы с использованием индексов, вы сможете создать надежную, быструю и отказоустойчивую систему бронирования любого уровня сложности.

← На главную