Как в 1С реализовать контроль пересекающихся периодов при выдаче документов?

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

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

Анализ проблемы пересечения периодов

Прежде чем переходить к коду, давайте проанализируем саму логическую задачу. Нам необходимо гарантировать, что для одного объекта (в данном случае — Сотрудник) в базе данных не существует двух записей, временные интервалы которых накладываются друг на друга. Рассмотрим ситуацию: у нас есть существующий период [ДатаНачала1, ДатаОкончания1] и мы пытаемся записать новый период [ДатаНачала2, ДатаОкончания2].

Многие начинающие разработчики совершают ошибку, используя оператор МЕЖДУ (BETWEEN) для проверки вхождения границ нового периода в старый. Однако этот подход не учитывает ситуацию, когда новый период полностью «поглощает» старый (то есть начинается раньше и заканчивается позже). Рассмотрим универсальную формулу пересечения двух интервалов, которая считается наиболее надежной:

Начало1 <= Конец2 И Конец1 >= Начало2

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

Вариант 1: Проверка по данным документов (без регистров)

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

Проанализируем пример запроса, который ищет пересечения:


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

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

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

Этот метод прост, но имеет недостатки. По мере роста количества документов поиск по всей таблице будет замедляться. Кроме того, поиск по документам не всегда удобен, если логика требует учитывать "ручное" закрытие пропусков или их аннулирование без отмены проведения.

Вариант 2: Использование регистра сведений

Для повышения производительности и гибкости системы лучше использовать РегистрСведений. Мы создадим непериодический регистр, где будем хранить информацию о занятых интервалах.

Рекомендуемая структура регистра:

  1. Измерение: Сотрудник (тип СправочникСсылка.Сотрудники).
  2. Измерение: ДатаНачала (чтобы позволить одному сотруднику иметь несколько записей на разные даты).
  3. Ресурс: ДатаОкончания.
  4. Реквизит: ДокументОснование.

Рассмотрим, как изменится наша проверка. Теперь мы будем обращаться к индексу регистра, что значительно быстрее. При проведении документа мы сначала проверяем наличие пересечений в регистре, а затем записываем в него данные о новом пропуске.

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

Выясним, как правильно установить блокировку перед проверкой:


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

// Далее следует запрос к регистру, аналогичный запросу к документам

Вариант 3: Механизм вытеснения в регистрах расчета

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

Разберем, как это работает. Мы настраиваем виды расчета так, чтобы "Первичный пропуск" вытеснялся "Особым распоряжением". При записи записи в регистр расчета платформа сама анализирует периоды и может автоматически "разрезать" существующие записи или выдавать ошибку при наложении. Это наиболее сложный, но и наиболее автоматизированный способ контроля временных интервалов.

Практические советы по работе с временем

Посмотрим на ситуацию с граничными значениями. Тип Дата в 1С всегда включает время. Часто возникают споры: считается ли пересечением, если один пропуск заканчивается в 23:59:59, а другой начинается в 00:00:00 следующего дня? Для корректной работы мы рекомендуем всегда приводить даты к началу или концу дня, если ваша бизнес-логика оперирует сутками.

Используйте функции НачалоДня() и КонецДня() при записи данных в регистр:


ЗаписьРегистра.ДатаНачала = НачалоДня(Объект.ДатаС);
ЗаписьРегистра.ДатаОкончания = КонецДня(Объект.ДатаПо);

Проанализируем еще один сценарий: досрочное прекращение действия пропуска. Если сотрудник вернул пропуск раньше срока, нам достаточно изменить ДатаОкончания в регистре сведений. Это мгновенно освободит временной интервал для возможности выдачи нового документа, при этом история выдачи в виде самих документов останется неизменной.

Резюме

Мы рассмотрели три основных способа контроля периодов в 1С. Для простых задач идеально подойдет запрос к таблице документов с использованием математически верной формулы пересечения. Для высоконагруженных систем или систем с большим объемом данных следует внедрять регистр сведений с обязательным использованием управляемых блокировок. А для сложных логических цепочек с приоритетами документов лучше всего подойдет механизм вытеснения регистров расчета. Выбор метода зависит от конкретных требований вашего технического задания и ожидаемого объема данных в системе.

← На главную