При расчете сроков исполнения задач, регламентных сроков согласования документов или контроля исполнения поручений разработчики часто сталкиваются с необходимостью прибавить к дате определенное количество именно рабочих дней. Стандартная функция языка запросов ДОБАВИТЬКДАТЕ прибавляет календарные дни и не умеет учитывать график работы, праздники и переносы выходных.
Разберем, как правильно реализовать подобный расчет с помощью производственного календаря, рассмотрим типовой алгоритм выборки через запрос, изучим штатные механизмы БСП и обратим внимание на важные ограничения каждого подхода.
Рассмотрим ситуацию, когда в конфигурации присутствует производственный календарь (например, регистр сведений РегламентированныйПроизводственныйКалендарь или его аналог). Задача состоит в том, чтобы взять дату отсчета и получить дату, отстоящую от нее ровно на N рабочих дней вперед.
Проанализируем суть метода: нам необходимо выбрать из календаря дни, которые идут строго позже начальной даты, отфильтровать только рабочие и предпраздничные дни, отсортировать их по возрастанию и ограничить результат количеством прибавляемых дней. Последняя полученная дата выборки и будет искомым рабочим днем.
Посмотрим на практический пример формирования и выполнения такого запроса:
ЧислоДней = 2;
ДатаНачала = НачалоДня(ТекущаяДата());
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ ПЕРВЫЕ " + Формат(ЧислоДней, "ЧГ=0") + "
| Календарь.ДатаКалендаря КАК ДатаКалендаря
|ИЗ
| РегистрСведений.РегламентированныйПроизводственныйКалендарь КАК Календарь
|ГДЕ
| Календарь.ДатаКалендаря > &ДатаНач
| И (Календарь.ВидДня = ЗНАЧЕНИЕ(Перечисление.ВидыДнейПроизводственногоКалендаря.Рабочий)
| ИЛИ Календарь.ВидДня = ЗНАЧЕНИЕ(Перечисление.ВидыДнейПроизводственногоКалендаря.Предпраздничный))
|УПОРЯДОЧИТЬ ПО
| Календарь.ДатаКалендаря ВОЗР";
Запрос.УстановитьПараметр("ДатаНач", ДатаНачала);
Выборка = Запрос.Выполнить().Выбрать();
ИскомаяДата = ДатаНачала;
Пока Выборка.Следующий() Цикл
ИскомаяДата = Выборка.ДатаКалендаря;
КонецЦикла;
Разберем по шагам ключевые детали этого запроса:
ВЫБРАТЬ ПЕРВЫЕ: задает ограничение на количество строк. Мы подставляем количество добавляемых дней в текст запроса, чтобы получить ровно заданный пул рабочих смен.ЗНАЧЕНИЕ(...) избавляет от необходимости передавать ссылки на перечисления через параметры запроса.УПОРЯДОЧИТЬ ПО: без явного указания сортировки платформа 1С и СУБД не гарантируют хронологический порядок возвращаемых записей, что приведет к неверному определению конечной даты.Выясним причину, почему данный метод нельзя назвать универсальным для всех сценариев:
ВЫБРАТЬ ПЕРВЫЕ работает только для одного значения смещения и одной начальной точки. Если требуется в запросе рассчитать даты сразу для таблицы из сотен документов, этот метод не подойдет.ВЫБРАТЬ ПЕРВЫЕ 0 вызовет синтаксическую ошибку либо вернет пустую выборку. Для вычитания рабочих дней условие необходимо менять на Календарь.ДатаКалендаря < &ДатаНач, а упорядочивание делать по убыванию (УБЫВ).Проанализируем архитектуру конфигураций на базе Библиотеки стандартных подсистем (включая «1С:Документооборот» и «1С:ЗУП»). В них нет необходимости переносить устаревшие регистры из старых версий конфигураций. Графики работы и календари уже встроены в типовой функционал.
Для работы с датами по календарю в БСП предназначен общий модуль КалендарныеГрафики. В нем реализована функция ДатаПоКалендарю, которая берет на себя все проверки заполненности и расчет смещения.
Посмотрим на пример вызова типовой функции:
ГрафикРаботы = Справочники.ПроизводственныеКалендари.НайтиПоНаименованию("Российская Федерация");
ДатаОт = ТекущаяДата();
КоличествоДней = 2;
КонечнаяДата = КалендарныеГрафики.ДатаПоКалендарю(ГрафикРаботы, ДатаОт, КоличествоДней, Истина);
Рассмотрим подробнее преимущества использования типовой функции:
ВызыватьИсключение = Истина гарантирует, что если календарь на требуемый период не заполнен, система выдаст информативное сообщение об ошибке, а не запишет неверную дату.Календари).Если стоит задача рассчитать даты для большого массива документов в едином запросе без вызова функций в цикле, применяют метод нумерации рабочих дней.
Разберем логику построения такого алгоритма:
Календарь1.Дата >= Календарь2.Дата с подсчетом количества строк через КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...).ЦелевойКалендарь.НомерРабочегоДня = НачальныйКалендарь.НомерРабочегоДня + ИсходныеДанные.ДнейКДобавлению.Такой подход обеспечивает высокую скорость работы СУБД, так как исключает динамическое формирование текста запроса и построчную обработку в циклах встроенного языка.